aws-cognito-srp 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +26 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +13 -1
- data/README.md +26 -3
- data/aws-cognito-srp.gemspec +3 -1
- data/lib/aws/cognito_srp/errors.rb +12 -0
- data/lib/aws/cognito_srp/version.rb +1 -1
- data/lib/aws/cognito_srp.rb +71 -33
- metadata +35 -5
- data/.github/workflows/main.yml +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6000777b1471345bf83ef454e2147786b46c8960218729c0ef9b33d05bc08634
|
4
|
+
data.tar.gz: 5b28a08b3614d6dbe6f05a1bc25fd54200a8c90bb6fe8e3a24f97e4afc1bbe80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
###
|
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: "
|
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/
|
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.
|
data/aws-cognito-srp.gemspec
CHANGED
@@ -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/
|
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
|
data/lib/aws/cognito_srp.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
114
|
-
|
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
|
-
|
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
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
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 =
|
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
|
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
|
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.
|
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-
|
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/
|
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/
|
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.
|
142
|
+
rubygems_version: 3.0.6
|
113
143
|
signing_key:
|
114
144
|
specification_version: 4
|
115
145
|
summary: AWS Cognito SRP auth for Ruby
|
data/.github/workflows/main.yml
DELETED
@@ -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
|