aws-cognito-srp 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9d319dfe478786e4bda431aa7d660c10944737a7d66e95c8c253ab5ec75f691
4
- data.tar.gz: 3741cfe9072feea72cc50d5c41ed5f5111cf9f28df32eaeefdd223908e00e880
3
+ metadata.gz: 6000777b1471345bf83ef454e2147786b46c8960218729c0ef9b33d05bc08634
4
+ data.tar.gz: 5b28a08b3614d6dbe6f05a1bc25fd54200a8c90bb6fe8e3a24f97e4afc1bbe80
5
5
  SHA512:
6
- metadata.gz: f6f713236d8e76dd635dda2a2437393d7a2b0b009e1f148bf138e53791cf0a1d90e077ffedc7a880f0caf878ab4d493ace2194a43d84b5c96bffcab8c07c5ba2
7
- data.tar.gz: d28cb221df9702987bf37c1f7b1c4fd12af4b74182834435e8ca7833ffc3ad7ba772b3564c883d26d069af167abfa955bc42703d622a794762050617c6077eaa
6
+ metadata.gz: d1e507ab4e5a8ce39647a2699ad1bac52a62352c6b2a04d2eb47b12954ae8c8a1f86f26c457583c13f84200064620cbf938381894d302a457b7a2c0468f5f26e
7
+ data.tar.gz: 3714f171b5527ed457d2c05e0ff5aece5f96d572e9f48e4fa6412b9cedad85d69ac54432b7e719f7afe0406aa7d135562b0f7446d705d0a4a494f22c48a45bd4
@@ -0,0 +1,26 @@
1
+ name: CI
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0']
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ name: Test against Ruby ${{ matrix.ruby }}
15
+
16
+ steps:
17
+ - uses: actions/checkout@v2
18
+ - name: Set up Ruby
19
+ uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ bundler-cache: true
23
+ - name: Install dependencies
24
+ run: bundle install --jobs 4 --retry 3
25
+ - name: Run specs
26
+ run: bundle exec rspec spec --backtrace
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## Changelog for aws-cognito-srp-ruby
2
2
 
3
- ### [0.1.0] - 2021-09-17
3
+ ### 0.4.0 (October 1, 2021)
4
+
5
+ * Added `refresh_tokens` method
6
+
7
+ ### 0.3.0 (September 29, 2021)
8
+
9
+ * Added support for Ruby 2.4 and 2.3
10
+
11
+ ### 0.2.0 (September 20, 2021)
12
+
13
+ * Added custom exception classes and better error messages
14
+
15
+ ### 0.1.0 (Septembre 17, 2021)
4
16
 
5
17
  * Initial release
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Aws::CognitoSrp for Ruby
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/aws-cognito-srp.svg?style=flat)](https://rubygems.org/gems/aws-cognito-srp)
4
+ ![CI](https://github.com/beezwax/aws-cognito-srp-ruby/workflows/CI/badge.svg)
5
+
3
6
  An unofficial Ruby library implementing
4
7
  [AWS Cognito's SRP authentication flow](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#Using-SRP-password-verification-in-custom-authentication-flow).
5
8
 
@@ -26,12 +29,25 @@ aws_srp = Aws::CognitoSrp.new(
26
29
  password: "password",
27
30
  pool_id: "pool-id",
28
31
  client_id: "client-id",
29
- aws_client: Aws::CognitoIdentityProvider::Client.new(region: "ap-southeast-2")
32
+ aws_client: Aws::CognitoIdentityProvider::Client.new(region: "aws-region")
30
33
  )
31
34
 
32
- aws_srp.authenticate
35
+ resp = aws_srp.authenticate
36
+
37
+ # Read tokens
38
+ resp.id_token
39
+ resp.access_token
40
+ resp.refresh_token
41
+
42
+ # A few hours later ... ⌛️
43
+
44
+ new_tokens = aws_srp.refresh_tokens(resp.refresh_token)
33
45
  ```
34
46
 
47
+ ## Supported rubies
48
+
49
+ This gem is tested against and supports Ruby 2.3, 2.4, 2.5, 2.6, 2.7 and 3.0.
50
+
35
51
  ## Development
36
52
 
37
53
  After checking out the repo, run `bin/setup` to install dependencies. You can
@@ -47,4 +63,11 @@ git commits and the created tag, and push the `.gem` file to
47
63
  ## Contributing
48
64
 
49
65
  Bug reports and pull requests are welcome on GitHub at
50
- https://github.com/pilaf/aws-cognito-srp-ruby
66
+ https://github.com/beezwax/aws-cognito-srp-ruby
67
+
68
+ ## Disclaimer
69
+
70
+ This project is not sponsored by or otherwise affiliated with Amazon Web
71
+ Services, Inc., an Amazon.com, Inc. subsidiary. AWS and Amazon Cognito are
72
+ trademarks of Amazon.com, Inc., or its affiliates in the United States and/or
73
+ other countries.
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = "AWS Cognito SRP auth for Ruby"
12
12
  spec.description = "Unofficial Ruby library implementing AWS Cognito's SRP authentication flow"
13
- spec.homepage = "https://github.com/pilaf/aws-cognito-srp-ruby"
13
+ spec.homepage = "https://github.com/beezwax/aws-cognito-srp-ruby"
14
14
  spec.license = "MIT"
15
15
 
16
16
  # Specify which files should be added to the gem when it is released.
@@ -25,6 +25,8 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "bundler", "~> 2.2.0"
26
26
  spec.add_development_dependency "rake", "~> 13.0"
27
27
  spec.add_development_dependency "ox", "~> 2.14.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ spec.add_development_dependency "pry"
28
30
 
29
31
  # For more information and examples about making a new gem, checkout our
30
32
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws/cognito_srp/errors"
4
+
5
+ module Aws
6
+ class CognitoSrp
7
+ class Error < ::RuntimeError; end
8
+ class UnexpectedChallenge < Error; end
9
+ class NewPasswordRequired < Error; end
10
+ class ValueError < Error; end
11
+ end
12
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Aws
4
4
  class CognitoSrp
5
- VERSION = "0.1.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
@@ -1,10 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "aws-sdk-cognitoidentityprovider"
4
- require "aws/cognito_srp/version"
5
4
 
6
5
  require "openssl"
7
6
  require "digest"
7
+ require "securerandom"
8
+ require "base64"
9
+
10
+ require "aws/cognito_srp/version"
11
+ require "aws/cognito_srp/errors"
12
+
13
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5")
14
+ module IntegerWithPow
15
+ refine Integer do
16
+ # Integer#pow was introduced in Ruby 2.5
17
+ # Use OpenSSL's modular exponentiation in older Rubies
18
+ def pow(b, m)
19
+ self.to_bn.mod_exp(b, m).to_i
20
+ end
21
+ end
22
+ end
23
+
24
+ using IntegerWithPow
25
+ end
26
+
27
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.4")
28
+ module StringWithUnpack1
29
+ refine String do
30
+ # String#unpack1 was introduced in Ruby 2.4
31
+ def unpack1(fmt)
32
+ unpack(fmt)[0]
33
+ end
34
+ end
35
+ end
36
+
37
+ using StringWithUnpack1
38
+ end
8
39
 
9
40
  module Aws
10
41
  # Client for AWS Cognito Identity Provider using Secure Remote Password (SRP).
@@ -28,9 +59,10 @@ module Aws
28
59
  # aws_srp.authenticate
29
60
  #
30
61
  class CognitoSrp
31
- USER_SRP_AUTH = "USER_SRP_AUTH"
32
- PASSWORD_VERIFIER = "PASSWORD_VERIFIER"
33
62
  NEW_PASSWORD_REQUIRED = "NEW_PASSWORD_REQUIRED"
63
+ PASSWORD_VERIFIER = "PASSWORD_VERIFIER"
64
+ REFRESH_TOKEN = "REFRESH_TOKEN"
65
+ USER_SRP_AUTH = "USER_SRP_AUTH"
34
66
 
35
67
  N_HEX = %w(
36
68
  FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
@@ -77,7 +109,9 @@ module Aws
77
109
  }
78
110
  )
79
111
 
80
- raise unless init_auth_response.challenge_name == PASSWORD_VERIFIER
112
+ unless init_auth_response.challenge_name == PASSWORD_VERIFIER
113
+ raise UnexpectedChallenge, "Expected Cognito to respond with a #{PASSWORD_VERIFIER} challenge, got #{init_auth_response.challenge_name} instead"
114
+ end
81
115
 
82
116
  challenge_response = process_challenge(init_auth_response.challenge_parameters)
83
117
 
@@ -87,11 +121,26 @@ module Aws
87
121
  challenge_responses: challenge_response
88
122
  )
89
123
 
90
- raise "new password required" if auth_response.challenge_name == NEW_PASSWORD_REQUIRED
124
+ if auth_response.challenge_name == NEW_PASSWORD_REQUIRED
125
+ raise NewPasswordRequired, "Cognito responded to password verifier with a #{NEW_PASSWORD_REQUIRED} challenge"
126
+ end
91
127
 
92
128
  auth_response.authentication_result
93
129
  end
94
130
 
131
+ def refresh_tokens(refresh_token)
132
+ resp = @aws_client.initiate_auth(
133
+ client_id: @client_id,
134
+ auth_flow: REFRESH_TOKEN,
135
+ auth_parameters: {
136
+ REFRESH_TOKEN: refresh_token
137
+ }
138
+ )
139
+
140
+ resp.authentication_result
141
+ end
142
+ alias_method :refresh, :refresh_tokens
143
+
95
144
  private
96
145
 
97
146
  def generate_random_small_a
@@ -101,18 +150,14 @@ module Aws
101
150
 
102
151
  def calculate_a
103
152
  big_a = @g.pow(@small_a_value, @big_n)
104
- if big_a % @big_n == 0
105
- raise "Safety check for A failed"
106
- end
107
-
153
+ raise ValueError, "Safety check for A failed" if big_a % @big_n == 0
108
154
  big_a
109
155
  end
110
156
 
111
157
  def get_password_authentication_key(username, password, server_b_value, salt)
112
158
  u_value = calculate_u(@large_a_value, server_b_value)
113
- if u_value == 0
114
- raise "U cannot be zero."
115
- end
159
+
160
+ raise ValueError, "U cannot be zero" if u_value == 0
116
161
 
117
162
  username_password = "#{@pool_id.split("_")[1]}#{username}:#{password}"
118
163
  username_password_hash = hash_sha256(username_password)
@@ -121,8 +166,7 @@ module Aws
121
166
  g_mod_pow_xn = @g.pow(x_value, @big_n)
122
167
  int_value2 = server_b_value - @k * g_mod_pow_xn
123
168
  s_value = int_value2.pow(@small_a_value + u_value * x_value, @big_n)
124
- hkdf = compute_hkdf(hex_to_bytes(pad_hex(s_value)), hex_to_bytes(pad_hex(long_to_hex(u_value))))
125
- hkdf
169
+ compute_hkdf(hex_to_bytes(pad_hex(s_value)), hex_to_bytes(pad_hex(long_to_hex(u_value))))
126
170
  end
127
171
 
128
172
  def process_challenge(challenge_parameters)
@@ -131,24 +175,24 @@ module Aws
131
175
  srp_b_hex = challenge_parameters.fetch("SRP_B")
132
176
  secret_block_b64 = challenge_parameters.fetch("SECRET_BLOCK")
133
177
 
134
- timestamp = Time.now.utc.strftime("%a %b %-d %H:%M:%S %Z %Y")
178
+ timestamp = ::Time.now.utc.strftime("%a %b %-d %H:%M:%S %Z %Y")
135
179
 
136
180
  hkdf = get_password_authentication_key(user_id_for_srp, @password, srp_b_hex.to_i(16), salt_hex)
137
- secret_block_bytes = Base64.strict_decode64(secret_block_b64)
181
+ secret_block_bytes = ::Base64.strict_decode64(secret_block_b64)
138
182
  msg = @pool_id.split("_")[1] + user_id_for_srp + secret_block_bytes + timestamp
139
- hmac_digest = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), hkdf, msg)
140
- signature_string = Base64.strict_encode64(hmac_digest).force_encoding('utf-8')
183
+ hmac_digest = ::OpenSSL::HMAC.digest(::OpenSSL::Digest::SHA256.new, hkdf, msg)
184
+ signature_string = ::Base64.strict_encode64(hmac_digest).force_encoding('utf-8')
141
185
 
142
186
  {
143
- "TIMESTAMP" => timestamp,
144
- "USERNAME" => user_id_for_srp,
145
- "PASSWORD_CLAIM_SECRET_BLOCK" => secret_block_b64,
146
- "PASSWORD_CLAIM_SIGNATURE" => signature_string
187
+ TIMESTAMP: timestamp,
188
+ USERNAME: user_id_for_srp,
189
+ PASSWORD_CLAIM_SECRET_BLOCK: secret_block_b64,
190
+ PASSWORD_CLAIM_SIGNATURE: signature_string
147
191
  }
148
192
  end
149
193
 
150
194
  def hash_sha256(buf)
151
- Digest::SHA256.hexdigest(buf)
195
+ ::Digest::SHA256.hexdigest(buf)
152
196
  end
153
197
 
154
198
  def hex_hash(hex_string)
@@ -172,16 +216,11 @@ module Aws
172
216
  end
173
217
 
174
218
  def get_random(nbytes)
175
- random_hex = bytes_to_hex(SecureRandom.bytes(nbytes))
176
- hex_to_long(random_hex)
219
+ hex_to_long(bytes_to_hex(::SecureRandom.gen_random(nbytes)))
177
220
  end
178
221
 
179
222
  def pad_hex(long_int)
180
- hash_str = if long_int.is_a?(String)
181
- long_int
182
- else
183
- long_to_hex(long_int)
184
- end
223
+ hash_str = long_int.is_a?(::String) ? long_int : long_to_hex(long_int)
185
224
 
186
225
  if hash_str.size % 2 == 1
187
226
  hash_str = "0#{hash_str}"
@@ -193,9 +232,9 @@ module Aws
193
232
  end
194
233
 
195
234
  def compute_hkdf(ikm, salt)
196
- prk = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), salt, ikm)
235
+ prk = ::OpenSSL::HMAC.digest(::OpenSSL::Digest::SHA256.new, salt, ikm)
197
236
  info_bits_update = INFO_BITS + 1.chr.force_encoding('utf-8')
198
- hmac_hash = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), prk, info_bits_update)
237
+ hmac_hash = ::OpenSSL::HMAC.digest(::OpenSSL::Digest::SHA256.new, prk, info_bits_update)
199
238
  hmac_hash[0, 16]
200
239
  end
201
240
 
@@ -205,4 +244,3 @@ module Aws
205
244
  end
206
245
  end
207
246
  end
208
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-cognito-srp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Viney
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-09-17 00:00:00.000000000 Z
13
+ date: 2021-10-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk-cognitoidentityprovider
@@ -68,6 +68,34 @@ dependencies:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
70
  version: 2.14.0
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '3.0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: pry
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
71
99
  description: Unofficial Ruby library implementing AWS Cognito's SRP authentication
72
100
  flow
73
101
  email:
@@ -76,8 +104,9 @@ executables: []
76
104
  extensions: []
77
105
  extra_rdoc_files: []
78
106
  files:
79
- - ".github/workflows/main.yml"
107
+ - ".github/workflows/ci.yml"
80
108
  - ".gitignore"
109
+ - ".rspec"
81
110
  - CHANGELOG.md
82
111
  - Gemfile
83
112
  - LICENSE
@@ -88,9 +117,10 @@ files:
88
117
  - bin/setup
89
118
  - lib/aws-cognito-srp.rb
90
119
  - lib/aws/cognito_srp.rb
120
+ - lib/aws/cognito_srp/errors.rb
91
121
  - lib/aws/cognito_srp/version.rb
92
122
  - lib/aws_cognito_srp.rb
93
- homepage: https://github.com/pilaf/aws-cognito-srp-ruby
123
+ homepage: https://github.com/beezwax/aws-cognito-srp-ruby
94
124
  licenses:
95
125
  - MIT
96
126
  metadata: {}
@@ -109,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
139
  - !ruby/object:Gem::Version
110
140
  version: '0'
111
141
  requirements: []
112
- rubygems_version: 3.2.3
142
+ rubygems_version: 3.0.6
113
143
  signing_key:
114
144
  specification_version: 4
115
145
  summary: AWS Cognito SRP auth for Ruby
@@ -1,16 +0,0 @@
1
- name: Ruby
2
-
3
- on: [push,pull_request]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
- steps:
9
- - uses: actions/checkout@v2
10
- - name: Set up Ruby
11
- uses: ruby/setup-ruby@v1
12
- with:
13
- ruby-version: 3.0.0
14
- bundler-cache: true
15
- - name: Run the default task
16
- run: bundle exec rake