silkey-sdk 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +1 -1
  4. data/.yardopts +14 -0
  5. data/CHANGELOG.md +46 -1
  6. data/DEVELOPMENT.md +2 -5
  7. data/Gemfile +1 -9
  8. data/Gemfile.lock +3 -1
  9. data/README.md +37 -14
  10. data/lib/silkey-sdk.rb +3 -4
  11. data/lib/silkey/models/jwt_payload.rb +57 -53
  12. data/lib/silkey/models/settings.rb +34 -0
  13. data/lib/silkey/models/sso_params.rb +42 -0
  14. data/lib/silkey/registry_contract/registry_contract.rb +1 -1
  15. data/lib/silkey/sdk.rb +63 -126
  16. data/lib/silkey/services/logger_service.rb +1 -1
  17. data/lib/silkey/services/verifier.rb +190 -0
  18. data/lib/silkey/utils.rb +7 -1
  19. data/lib/silkey/version.rb +1 -1
  20. data/silkey-sdk.gemspec +16 -3
  21. metadata +56 -93
  22. data/doc/CHANGELOG_md.html +0 -112
  23. data/doc/LICENSE_txt.html +0 -99
  24. data/doc/Object.html +0 -117
  25. data/doc/README_md.html +0 -144
  26. data/doc/Silkey.html +0 -269
  27. data/doc/Silkey/ClientFactory.html +0 -106
  28. data/doc/Silkey/Configuration.html +0 -217
  29. data/doc/Silkey/Contract.html +0 -106
  30. data/doc/Silkey/Contract/FrozenArray.html +0 -106
  31. data/doc/Silkey/ContractFactory.html +0 -106
  32. data/doc/Silkey/LoggerService.html +0 -106
  33. data/doc/Silkey/Models.html +0 -99
  34. data/doc/Silkey/Models/JwtPayload.html +0 -622
  35. data/doc/Silkey/RegistryContract.html +0 -106
  36. data/doc/Silkey/SDK.html +0 -356
  37. data/doc/Silkey/Utils.html +0 -709
  38. data/doc/created.rid +0 -23
  39. data/doc/css/fonts.css +0 -167
  40. data/doc/css/rdoc.css +0 -619
  41. data/doc/fonts/Lato-Light.ttf +0 -0
  42. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  43. data/doc/fonts/Lato-Regular.ttf +0 -0
  44. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  45. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  46. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  47. data/doc/images/add.png +0 -0
  48. data/doc/images/arrow_up.png +0 -0
  49. data/doc/images/brick.png +0 -0
  50. data/doc/images/brick_link.png +0 -0
  51. data/doc/images/bug.png +0 -0
  52. data/doc/images/bullet_black.png +0 -0
  53. data/doc/images/bullet_toggle_minus.png +0 -0
  54. data/doc/images/bullet_toggle_plus.png +0 -0
  55. data/doc/images/date.png +0 -0
  56. data/doc/images/delete.png +0 -0
  57. data/doc/images/find.png +0 -0
  58. data/doc/images/loadingAnimation.gif +0 -0
  59. data/doc/images/macFFBgHack.png +0 -0
  60. data/doc/images/package.png +0 -0
  61. data/doc/images/page_green.png +0 -0
  62. data/doc/images/page_white_text.png +0 -0
  63. data/doc/images/page_white_width.png +0 -0
  64. data/doc/images/plugin.png +0 -0
  65. data/doc/images/ruby.png +0 -0
  66. data/doc/images/tag_blue.png +0 -0
  67. data/doc/images/tag_green.png +0 -0
  68. data/doc/images/transparent.png +0 -0
  69. data/doc/images/wrench.png +0 -0
  70. data/doc/images/wrench_orange.png +0 -0
  71. data/doc/images/zoom.png +0 -0
  72. data/doc/index.html +0 -112
  73. data/doc/js/darkfish.js +0 -84
  74. data/doc/js/navigation.js +0 -105
  75. data/doc/js/navigation.js.gz +0 -0
  76. data/doc/js/search.js +0 -110
  77. data/doc/js/search_index.js +0 -1
  78. data/doc/js/search_index.js.gz +0 -0
  79. data/doc/js/searcher.js +0 -229
  80. data/doc/js/searcher.js.gz +0 -0
  81. data/doc/table_of_contents.html +0 -318
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 151c068dbbeccc6d242140bbf755dbc2d7ac783de3c0fa9f8bed08828c875011
4
- data.tar.gz: '09c21b562b50c1a66c9d72d2fd0042e6a30549941017e1e7fff4e4644bbdcfef'
3
+ metadata.gz: ac39da25efe706b56abfda8f580295dc0d0c7734ce83ccc13130b85cce5a1e22
4
+ data.tar.gz: 86744220f6d4e5ee274c1c451cc5be9591a60f240a5a2fc871d5fc3794d71988
5
5
  SHA512:
6
- metadata.gz: f7505da37a916d4e26d7ebd896a432ff901ae3ee805707e924e34a832643d52fe6ddb7a52f3a0676d7170976a6cdfeb469327b8c99fd8292136313324cbfba08
7
- data.tar.gz: 1f346357fc4623f9cfb54c93cbd68e000a2a4597374a3f5c39f037907da4aa1d4003611e09a6b52386816a27d481bd200b644472416e6db9adb48fcc96e01c92
6
+ metadata.gz: b634f6d573b002484a32a02059b730b5cd9184d93de5aaa67d9df981d833269845df561394444d239ea327ac653e68d2ffda4fa36a05e680b73e34d67384a065
7
+ data.tar.gz: 4024009b029a6d8aa45b7beb9d79a2d61c677d761d94ba79234539dc1aac9fe43e729735bd2df58b86cdfdcb8085c2fbbf70018624fbbb5504d9c85132c0ed22
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /tmp/
8
8
  .idea/
9
9
  pkg/
10
+ doc/
10
11
 
11
12
  *.gem
12
13
  # rspec failure tracking
@@ -32,7 +32,7 @@ Metrics/BlockLength:
32
32
  ExcludedMethods: ['describe', 'context', 'let']
33
33
 
34
34
  Layout/LineLength:
35
- Max: 100
35
+ Max: 120
36
36
 
37
37
  Metrics/ClassLength:
38
38
  Max: 150
@@ -0,0 +1,14 @@
1
+ --no-private
2
+ --title "Silkey SDK for Ruby"
3
+ --exclude Gemfile
4
+ --exclude Gemfile.lock
5
+ --exclude registry_contract_abi.json
6
+ --exclude setup
7
+ --exclude Rakefile
8
+ --exclude DEVELOPMENT.md
9
+ --exclude lib/silkey/factories/*
10
+ --exclude lib/silkey/registry_contract/*
11
+ --exclude lib/silkey/services/*
12
+ --exclude lib/silkey/contract.rb
13
+ --exclude lib/silkey/utils.rb
14
+ --exclude lib/silkey/version.rb
@@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [Unreleased]
7
+ ## [0.1.0] - 2021-01-03
8
+ ### Added
9
+ - additional verification for token:
10
+ - verify token age
11
+ - verify sso params
12
+ - website signature
13
+ - support for migration
14
+ - settings model for jwt and sso params
15
+
16
+ ### Changed
17
+ - all SSO params have prefix `sso`
18
+
19
+ ### Removed
20
+ - `refId` is not longer part of token
21
+
22
+ ## [0.0.5] - 2020-12-15
23
+ ### Changed
24
+ - add `0x` to signature to be compatible with JS and not throw `signature missing v and recoveryParam` error
25
+
26
+ ## [0.0.4] - 2020-11-26
27
+ ### Added
28
+ - `generate_sso_request_params` throws on empty PK
29
+
30
+ ### Changed
31
+ - join operator for `message_to_sign` is now `&`
32
+ - `message_to_sign` ignores data that are not set
33
+
34
+ ### Fixed
35
+ - ensure testing `throws` is working correctly
36
+ - rubocop offences
37
+
38
+ ## [0.0.3] - 2020-11-25
39
+ ### Added
40
+ - Logo and badges to README
41
+
42
+ ## [0.0.2] 2020-11-23
43
+ ### Added
44
+ - add support for `redirectMethod`
45
+
46
+ ### Changed
47
+ - `generate_sso_request_params` raises error when missing required params
48
+
49
+ ### Removed
50
+ - html doc files from repo
51
+
52
+ ## [0.0.1] 2020-11-20
8
53
  ### Added:
9
54
  - initial version
@@ -1,9 +1,5 @@
1
1
  # Silkey-SDK for Ruby
2
2
 
3
- [logo]
4
-
5
- [slogan]
6
-
7
3
  ## Development
8
4
 
9
5
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -13,7 +9,7 @@ You can also run `bin/console` for an interactive prompt that will allow you to
13
9
 
14
10
  ```
15
11
  rm -rf doc/
16
- bundle exec rdoc --main README.rdoc -x Gemfile -x Gemfile.lock -x registry_contract_abi.json -x setup -x Rakefile -x DEVELOPMENT.md
12
+ bundle exec yardoc
17
13
 
18
14
  bundle exec gem build
19
15
 
@@ -51,6 +47,7 @@ Silkey::RegistryContract.get_address('Name')
51
47
  ```bash
52
48
  bundle exec rspec
53
49
  bundle exec rubocop --fix
50
+ bundle exec rubocop --auto-correct-all
54
51
  ```
55
52
 
56
53
  #### Init setup environment
data/Gemfile CHANGED
@@ -1,13 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
-
5
- # Specify your gem's dependencies in andromeda.gemspec
6
-
7
- #source 'https://hwGZD769AGZu5Wyzp87F@repo.fury.io/silkey/' do
8
- # gem 'ethereum.rb', '~> 2.2.3'
9
- #end
10
-
11
3
  gemspec
12
4
 
13
- gem "rspec"
5
+ gem "rspec"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- silkey-sdk (0.0.1)
4
+ silkey-sdk (0.1.0)
5
5
  activesupport (~> 6.0)
6
6
  eth (~> 0.4.12)
7
7
  ethereum.rb (~> 2.5)
@@ -108,6 +108,7 @@ GEM
108
108
  descendants_tracker (~> 0.0, >= 0.0.3)
109
109
  equalizer (~> 0.0, >= 0.0.9)
110
110
  virtus_convert (0.1.0)
111
+ yard (0.9.25)
111
112
  zeitwerk (2.4.1)
112
113
 
113
114
  PLATFORMS
@@ -123,6 +124,7 @@ DEPENDENCIES
123
124
  rubocop (~> 1.3)
124
125
  rubocop-performance (~> 1.8)
125
126
  silkey-sdk!
127
+ yard (~> 0.9.25)
126
128
 
127
129
  BUNDLED WITH
128
130
  2.1.4
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # Silkey-SDK for Ruby
1
+ # Silkey SDK for Ruby
2
2
 
3
- [logo]
4
-
5
- [slogan]
3
+ ![Silkey Logo](https://raw.githubusercontent.com/Silkey-Team/brand/master/silkey-word-black.png)
6
4
 
5
+ [![GitHub version](https://badge.fury.io/gh/Silkey-Team%2Fsilkey-sdk.svg)](https://badge.fury.io/gh/Silkey-Team%2Fsilkey-sdk)
6
+ [![Gem Version](https://badge.fury.io/rb/silkey-sdk.svg)](https://badge.fury.io/rb/silkey-sdk)
7
7
 
8
8
  ## Integration
9
9
 
@@ -11,36 +11,59 @@
11
11
 
12
12
  ```rb
13
13
  Silkey::Configuration.setup do |config|
14
- config.client_url = 'http://localhost:8545' # for local development
15
- config.client_url = 'https://kovan.infura.io/v3/:id' # for real
16
- config.registry_contract_address = '--silky-registry-contrract-address--'
14
+ # for local development, use local provider url eg 'http://localhost:8545'
15
+ # for testing, we recommend using infura.io:
16
+ # - for sandbox: https://rinkeby.infura.io/v3/:id
17
+ # - for production: https://mainnet.infura.io/v3/:id
18
+ config.client_url = 'http://localhost:8545'
19
+ config.registry_contract_address = '--silky-registry-contract-address--'
17
20
  config.enable_logs = false
18
21
  end
19
22
  ```
20
23
 
21
- [List of Silkey contract address.](https://github.com/Silkey-Team/silkey-sdk#silkey-sdk)
24
+ [List of Silkey smart contract addresses.](https://github.com/Silkey-Team/silkey-sdk#smart-contracts)
22
25
 
23
26
  ### Sign In with Silkey
24
27
 
25
28
  #### Making request
26
29
 
27
- [List of request parameters.](https://github.com/Silkey-Team/silkey-sdk#silkey-sdk)
30
+ | Parameter | Required | Type | Desc
31
+ | ----------------- |:---------:| -------- | -----
32
+ | ssoSignature | yes | string | Domain owner signature
33
+ | ssoTimestamp | yes | number | Time of signing SSO request
34
+ | ssoRedirectUrl | yes | string | Where to redirect user with token after sign in
35
+ | ssoCancelUrl | yes | string | Where to redirect user on error
36
+ | ssoRedirectMethod | no | GET/POST | How to redirect user after sign in, default is POST
37
+ | ssoRefId | no | string | Any value, you may use it to identify request
38
+ | ssoScope | no | string | Scope of data to return in a token payload: `id` (default) returns only user address, `email` returns address + email
28
39
 
29
40
 
30
41
  ```rb
31
- - Silkey::SDK.message_to_sign({params})
32
- - Silkey::SDK.generate_sso_request_params(private_key, hash)
42
+ params = { ssoRedirectUrl: 'https://your-website', ssoRefId: '12ab' }
43
+ sso_params = Silkey::SDK.generate_sso_request_params(private_key, params)
33
44
  ```
34
45
 
35
46
  #### On request callback page
36
47
 
37
- `token` - get if from request params (it can be send either via POST or GET)
48
+ Callback will be done via POST (default) or GET, based on `ssoRedirectMethod`.
49
+
50
+ Callback params contains:
51
+ - sso parameters that were used to make SSO call
52
+ - `token`.
53
+
54
+ `token` - get if from request params
55
+
56
+ `ssoRequestParams` - get if from request params (it can be send via POST or GET, based on `ssoRedirectMethod`)
38
57
 
39
58
  ```rb
40
- silkey_public_key = Silkey::SDK.fetch_silkey_public_key
41
- Silkey::SDK.token_payload_verifier(token, silkey_public_key)
59
+ silkey_eth_address = Silkey::SDK.fetch_silkey_eth_address
60
+ Silkey::SDK.token_payload_verifier(token, silkey_eth_address)
42
61
  ```
43
62
 
63
+ ## Recommendations and Migration
64
+
65
+ See [recommendation and migration](https://github.com/Silkey-Team/silkey-sdk#recommendations) sections on main SDK package.
66
+
44
67
  ## License
45
68
 
46
69
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,14 +1,10 @@
1
1
  require 'eth'
2
2
  require 'ethereum.rb'
3
- # require 'json'
4
3
  require 'jwt'
5
4
  require 'logger'
6
5
  require 'pry'
7
- #require 'active_support'
8
- #require 'active_support/core_ext/module'
9
6
  require 'virtus_convert'
10
7
  require 'virtus'
11
- # require 'retriable'
12
8
  require 'silkey'
13
9
 
14
10
  require_relative 'silkey/contract'
@@ -16,7 +12,10 @@ require_relative 'silkey/configuration'
16
12
  require_relative 'silkey/factories/client_factory'
17
13
  require_relative 'silkey/factories/contract_factory'
18
14
  require_relative 'silkey/models/jwt_payload'
15
+ require_relative 'silkey/models/settings'
16
+ require_relative 'silkey/models/sso_params'
19
17
  require_relative 'silkey/services/logger_service'
18
+ require_relative 'silkey/services/verifier'
20
19
  require_relative 'silkey/sdk'
21
20
  require_relative 'silkey/utils'
22
21
 
@@ -2,28 +2,36 @@
2
2
 
3
3
  module Silkey
4
4
  module Models
5
+ ##
6
+ # Generates message to sign based on plain object data (keys => values)
7
+ #
8
+ # @param email [string] verified email of the user
9
+ # IMPORTANT: if email in user profile is different, you should always update it with this one.
10
+ #
11
+ # @param scope [string]
12
+ # @param address [string] ID of the user, this is also valid ethereum address, use this to identify user
13
+ # @param userSignature [string] proof that request came from the user
14
+ # @param userSignatureTimestamp [number] time when signature was crated
15
+ # @param silkeySignature [string] proof that Silkey verified the email
16
+ # @param silkeySignatureTimestamp [number] time when signature was crated
17
+ # @param migration [boolean] true if user started migration to Silkey
18
+ #
5
19
  class JwtPayload
6
20
  include Virtus.model
7
21
 
8
- SCOPE_DIVIDER = ','
9
-
10
22
  # rubocop:disable Style/HashSyntax
11
- attribute :address, String, :writer => :private
12
23
  attribute :email, String, :writer => :private
24
+ attribute :_scope, {}, :writer => :private, :reader => :private
25
+ attribute :address, String, :writer => :private
13
26
  attribute :silkey_signature, String, :writer => :private
14
- attribute :silkey_signature_timestamp, Integer
27
+ attribute :silkey_signature_timestamp, Integer, :default => 0
15
28
  attribute :user_signature, String, :writer => :private
16
- attribute :user_signature_timestamp, Integer
17
- attribute :ref_id, String, :writer => :private
18
- attribute :_scope, {}, :writer => :private, :reader => :private
29
+ attribute :user_signature_timestamp, Integer, :default => 0
30
+ attribute :migration, Boolean, :writer => :private, :default => false
19
31
  # rubocop:enable Style/HashSyntax
20
32
 
21
33
  def scope
22
- _scope.keys.sort.join(SCOPE_DIVIDER)
23
- end
24
-
25
- def scope_divider
26
- SCOPE_DIVIDER
34
+ _scope.keys.sort.join(Silkey::Settings.SCOPE_DIVIDER)
27
35
  end
28
36
 
29
37
  # rubocop:disable Naming/AccessorMethodName
@@ -46,8 +54,8 @@ module Silkey
46
54
  self
47
55
  end
48
56
 
49
- def set_ref_id(ref_id)
50
- self.ref_id = ref_id
57
+ def set_migrations(migrating)
58
+ self.migration = migrating
51
59
  self
52
60
  end
53
61
 
@@ -68,31 +76,24 @@ module Silkey
68
76
  self.silkey_signature_timestamp = timestamp
69
77
  self
70
78
  end
71
-
72
79
  # rubocop:enable Naming/AccessorMethodName
73
80
 
81
+ # rubocop:disable Metrics/AbcSize
82
+ #
74
83
  ##
75
84
  # Creates message that's need to be sign by user
76
85
  def message_to_sign_by_user
77
- if !Silkey::Utils.empty?(address) && Silkey::Utils.empty?(user_signature_timestamp)
78
- self.user_signature_timestamp = Silkey::Utils.current_timestamp
79
- end
80
-
81
- str1_hex = 'address'.unpack('H*')[0]
82
- adr_hex = Silkey::Utils.remove0x(address).downcase
83
-
84
- str2_hex = [
85
- 'refId', ref_id.to_s,
86
- 'scope', scope,
87
- 'userSignatureTimestamp'
88
- ].map { |str| str.to_s.unpack('H*') }.join('')
89
-
90
- str_hex = "#{str1_hex}#{adr_hex}#{str2_hex}"
91
-
92
- return str_hex if Silkey::Utils.empty?(user_signature_timestamp)
93
-
94
- "#{str_hex}#{Silkey::Utils.int_to_hex(user_signature_timestamp.to_s)}"
86
+ data = {
87
+ address: Silkey::Utils.strings_to_hex(['address']) + Silkey::Utils.remove0x(address).downcase,
88
+ migration: Silkey::Utils.strings_to_hex(['migration']) + (migration ? '01' : '00'),
89
+ scope: Silkey::Utils.strings_to_hex(['scope', scope]),
90
+ userSignatureTimestamp: Silkey::Utils.strings_to_hex(['userSignatureTimestamp']) +
91
+ Silkey::Utils.int_to_hex(user_signature_timestamp)
92
+ }
93
+
94
+ data.keys.sort.map { |k| data[k] }.join('')
95
95
  end
96
+ # rubocop:enable Metrics/AbcSize
96
97
 
97
98
  def message_to_sign_by_silkey
98
99
  return '' if Silkey::Utils.empty?(email)
@@ -101,36 +102,31 @@ module Silkey
101
102
  self.silkey_signature_timestamp = Silkey::Utils.current_timestamp
102
103
  end
103
104
 
104
- str_hex = [
105
- 'email', email,
106
- 'silkeySignatureTimestamp'
107
- ].map { |str| str.to_s.unpack('H*') }.join('')
108
-
109
- "#{str_hex}#{Silkey::Utils.int_to_hex(silkey_signature_timestamp.to_s)}"
105
+ "#{email.to_s.unpack('H*')[0]}#{Silkey::Utils.int_to_hex(silkey_signature_timestamp)}"
110
106
  end
111
107
 
112
108
  def validate
113
109
  raise "address is invalid: #{address}" unless Silkey::Utils.ethereum_address?(address)
114
110
 
115
- unless Silkey::Utils.signature?(user_signature)
116
- raise "user_signature is invalid: #{user_signature}"
117
- end
111
+ raise "user_signature is invalid: #{user_signature}" unless Silkey::Utils.signature?(user_signature)
118
112
 
119
- raise 'user_signature_timestamp is empty' if Silkey::Utils.empty?(user_signature_timestamp)
113
+ raise 'user_signature_timestamp is invalid' unless Silkey::Utils.timestamp?(user_signature_timestamp)
120
114
 
121
115
  return self if Silkey::Utils.empty?(scope) || scope == 'id'
122
116
 
123
117
  validate_scope_email
124
118
  end
125
119
 
126
- def import(hash)
127
- hash.each do |k, v|
120
+ def import(data = {})
121
+ return data if data.is_a?(Silkey::Models::JwtPayload)
122
+
123
+ data.each do |k, v|
128
124
  var = k.to_s.underscore
129
125
 
130
- if k == 'scope'
126
+ if var == 'scope'
131
127
  set_scope(v)
132
128
  else
133
- self.instance_variable_set("@#{var}", v)
129
+ instance_variable_set("@#{var}", v)
134
130
  end
135
131
  end
136
132
 
@@ -139,16 +135,24 @@ module Silkey
139
135
 
140
136
  private
141
137
 
138
+ def pack_payload_to_hex
139
+ str1_hex = 'address'.unpack('H*')[0]
140
+ adr_hex = Silkey::Utils.remove0x(address).downcase
141
+
142
+ str2_hex = [
143
+ 'scope', scope,
144
+ 'userSignatureTimestamp'
145
+ ].map { |str| str.to_s.unpack('H*') }.join('')
146
+
147
+ "#{str1_hex}#{adr_hex}#{str2_hex}"
148
+ end
149
+
142
150
  def validate_scope_email
143
151
  raise 'email is empty' if Silkey::Utils.empty?(email)
144
152
 
145
- unless Silkey::Utils.signature?(silkey_signature)
146
- raise "silkey_signature is invalid: #{silkey_signature}"
147
- end
153
+ raise "silkey_signature is invalid: #{silkey_signature}" unless Silkey::Utils.signature?(silkey_signature)
148
154
 
149
- if Silkey::Utils.empty?(silkey_signature_timestamp)
150
- raise 'silkey_signature_timestamp is empty'
151
- end
155
+ raise 'silkey_signature_timestamp is invalid' unless Silkey::Utils.timestamp?(silkey_signature_timestamp)
152
156
 
153
157
  self
154
158
  end