minty 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -16
  3. data/Gemfile.lock +28 -208
  4. data/README.md +58 -57
  5. data/Rakefile +4 -27
  6. data/docs/DefaultApi.md +217 -0
  7. data/docs/RedirectUrl.md +18 -0
  8. data/git_push.sh +57 -0
  9. data/lib/minty/api/default_api.rb +210 -0
  10. data/lib/minty/api_client.rb +388 -0
  11. data/lib/minty/api_error.rb +53 -0
  12. data/lib/minty/configuration.rb +275 -0
  13. data/lib/minty/models/redirect_url.rb +216 -0
  14. data/lib/minty/version.rb +9 -3
  15. data/lib/minty.rb +33 -7
  16. data/minty.gemspec +19 -32
  17. data/pkg/minty-1.1.0.gem +0 -0
  18. data/publish_rubygem.sh +1 -1
  19. data/spec/api/default_api_spec.rb +65 -0
  20. data/spec/api_client_spec.rb +222 -0
  21. data/spec/configuration_spec.rb +38 -0
  22. data/spec/models/redirect_url_spec.rb +28 -0
  23. data/spec/spec_helper.rb +95 -63
  24. metadata +37 -292
  25. data/.bundle/config +0 -4
  26. data/.devcontainer/Dockerfile +0 -19
  27. data/.devcontainer/devcontainer.json +0 -37
  28. data/.env.example +0 -2
  29. data/.gemrelease +0 -2
  30. data/.github/PULL_REQUEST_TEMPLATE.md +0 -33
  31. data/.github/dependabot.yml +0 -10
  32. data/.github/stale.yml +0 -20
  33. data/.gitignore +0 -18
  34. data/.rspec +0 -3
  35. data/.rubocop.yml +0 -9
  36. data/CODE_OF_CONDUCT.md +0 -3
  37. data/DEPLOYMENT.md +0 -61
  38. data/DEVELOPMENT.md +0 -35
  39. data/EXAMPLES.md +0 -195
  40. data/Guardfile +0 -39
  41. data/LICENSE +0 -21
  42. data/Makefile +0 -5
  43. data/RUBYGEM.md +0 -9
  44. data/codecov.yml +0 -22
  45. data/lib/minty/algorithm.rb +0 -7
  46. data/lib/minty/api/authentication_endpoints.rb +0 -55
  47. data/lib/minty/api/v2.rb +0 -8
  48. data/lib/minty/client.rb +0 -7
  49. data/lib/minty/exception.rb +0 -58
  50. data/lib/minty/mixins/api_token_struct.rb +0 -4
  51. data/lib/minty/mixins/headers.rb +0 -19
  52. data/lib/minty/mixins/httpproxy.rb +0 -125
  53. data/lib/minty/mixins/initializer.rb +0 -38
  54. data/lib/minty/mixins/validation.rb +0 -113
  55. data/lib/minty/mixins.rb +0 -23
  56. data/lib/minty_client.rb +0 -4
  57. data/spec/integration/lib/minty/api/api_authentication_spec.rb +0 -122
  58. data/spec/integration/lib/minty/minty_client_spec.rb +0 -92
  59. data/spec/lib/minty/client_spec.rb +0 -223
  60. data/spec/lib/minty/mixins/httpproxy_spec.rb +0 -658
  61. data/spec/lib/minty/mixins/initializer_spec.rb +0 -121
  62. data/spec/lib/minty/mixins/token_management_spec.rb +0 -129
  63. data/spec/lib/minty/mixins/validation_spec.rb +0 -559
  64. data/spec/support/credentials.rb +0 -14
  65. data/spec/support/dummy_class.rb +0 -20
  66. data/spec/support/dummy_class_for_proxy.rb +0 -6
  67. data/spec/support/dummy_class_for_restclient.rb +0 -4
  68. data/spec/support/dummy_class_for_tokens.rb +0 -18
  69. data/spec/support/import_users.json +0 -13
  70. data/spec/support/stub_response.rb +0 -3
@@ -1,559 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Metrics/BlockLength
4
- require 'spec_helper'
5
-
6
- RSA_PUB_KEY_JWK_1 = { 'kty': 'RSA', 'use': 'sig',
7
- 'n': 'uGbXWiK3dQTyCbX5xdE4yCuYp0AF2d15Qq1JSXT_lx8CEcXb9RbDddl8jGDv-spi5qPa8qEHiK7FwV2KpRE983wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVsWXI9C-yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT69s7of9-I9l5lsJ9cozf1rxrXX4V1u_SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8AziMCxS-VrRPDM-zfvpIJg3JljAh3PJHDiLu902v9w-Iplu1WyoB2aPfitxEhRN0Yw', 'e': 'AQAB', 'kid': 'test-key-1' }.freeze
8
- RSA_PUB_KEY_JWK_2 = { 'kty': 'RSA', 'use': 'sig',
9
- 'n': 'uGbXWiK3dQTyCbX5xdE4yCuYp0AF2d15Qq1JSXT_lx8CEcXb9RbDddl8jGDv-spi5qPa8qEHiK7FwV2KpRE983wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVsWXI9C-yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT69s7of9-I9l5lsJ9cozf1rxrXX4V1u_SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8AziMCxS-VrRPDM-zfvpIJg3JljAh3PJHDiLu902v9w-Iplu1WyoB2aPfitxEhRN0Yw', 'e': 'AQAB', 'kid': 'test-key-2' }.freeze
10
- JWKS_RESPONSE_1 = { 'keys': [RSA_PUB_KEY_JWK_1] }.freeze
11
- JWKS_RESPONSE_2 = { 'keys': [RSA_PUB_KEY_JWK_2] }.freeze
12
- JWKS_URL = 'https://tokens-test.minty.page/.well-known/jwks.json'
13
- JWKS_URL_2 = 'https://tokens-test2.minty.page/.well-known/jwks.json'
14
- HMAC_SHARED_SECRET = 'secret'
15
-
16
- LEEWAY = 60
17
- CLOCK = 1_587_592_561 # Apr 22 2020 21:56:01 UTC
18
- CONTEXT = { algorithm: Minty::Algorithm::HS256.secret(HMAC_SHARED_SECRET), leeway: LEEWAY, audience: 'tokens-test-123',
19
- issuer: 'https://tokens-test.minty.page/', clock: CLOCK }.freeze
20
-
21
- describe Minty::Mixins::Validation::IdTokenValidator do
22
- subject { @instance }
23
-
24
- context 'instance' do
25
- it 'is expected respond to :validate' do
26
- instance = Minty::Mixins::Validation::IdTokenValidator.new({})
27
-
28
- expect(instance).to respond_to(:validate)
29
- end
30
- end
31
-
32
- context 'ID token decoding' do
33
- expected_error = 'ID token could not be decoded'
34
- instance = Minty::Mixins::Validation::IdTokenValidator.new({})
35
-
36
- it 'is expected to raise an error with a nil id_token' do
37
- expect { instance.validate(nil) }.to raise_exception(expected_error)
38
- end
39
-
40
- it 'is expected to raise an error with an empty id_token' do
41
- expect { instance.validate('') }.to raise_exception(expected_error)
42
- end
43
-
44
- it 'is expected to raise an error with an invalid format' do
45
- expect { instance.validate('a.b') }.to raise_exception(expected_error)
46
- expect { instance.validate('a.b.') }.to raise_exception(expected_error)
47
- expect { instance.validate('a.b.c.d') }.to raise_exception(expected_error)
48
- end
49
-
50
- it 'is expected to raise an error with an invalid encoding' do
51
- expect { instance.validate('a.b.c') }.to raise_exception(expected_error)
52
- end
53
- end
54
-
55
- context 'algorithm verification' do
56
- token = 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.Hn38QVtN_mWN0c-jOa-Fqq69kXpbBp0THsvE-CQ47Ps'
57
-
58
- it 'is expected to raise an error with an unsupported algorithm' do
59
- instance = Minty::Mixins::Validation::IdTokenValidator.new({ algorithm: 'ES256' })
60
-
61
- expect { instance.validate(token) }.to raise_exception('Signature algorithm of "ES256" is not supported')
62
- end
63
-
64
- it 'is expected to raise an error when the algorithm does not match the alg header value' do
65
- algorithm = Minty::Algorithm::HS256.secret(HMAC_SHARED_SECRET)
66
- instance = Minty::Mixins::Validation::IdTokenValidator.new({ algorithm: algorithm })
67
-
68
- expect do
69
- instance.validate(token)
70
- end.to raise_exception('Signature algorithm of "ES256" is not supported. Expected the ID token to be signed with "HS256"')
71
- end
72
- end
73
-
74
- context 'HS256 signature verification' do
75
- before :each do
76
- algorithm = Minty::Algorithm::HS256.secret(HMAC_SHARED_SECRET)
77
- @instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ algorithm: algorithm }))
78
- end
79
-
80
- it 'is expected not to raise an error with a valid signature' do
81
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.Hn38QVtN_mWN0c-jOa-Fqq69kXpbBp0THsvE-CQ47Ps'
82
-
83
- expect { @instance.validate(token) }.not_to raise_exception
84
- end
85
-
86
- it 'is expected to raise an error with an invalid signature' do
87
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.invalidsignature'
88
-
89
- expect { @instance.validate(token) }.to raise_exception('Invalid ID token signature')
90
- end
91
- end
92
-
93
- context 'RS256 signature verification' do
94
- before :each do
95
- stub_jwks
96
- algorithm = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
97
- @instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ algorithm: algorithm }))
98
- end
99
-
100
- after :each do
101
- Minty::Algorithm::RS256.remove_jwks
102
- WebMock.reset!
103
- end
104
-
105
- it 'is expected not to raise an error with a valid signature' do
106
- token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.jE00ARUiAwrKEoAMwbioKYj4bUZjmg31V7McDtIPsJJ16rYcvI-e5mtSSMgCmAom6t-WA7dsSWCJUlBCW2nAMvyCZ-hj8HG9Z0RmQEiwig9Fk22avoX94zdx65TwAeDfn2uMRaX_ps3TJcn4nymUtMp8Lps_vMw15eJerKThlSO4KuLTrvDDdRaCRamAd7jxuzhiwOt0mB0TVD55b5itA02pGuyapbjQXvvLYEx8OgpyIaAkB9Ry25abgjev0bSw2kjFZckG3lv9QgvZM85m9l3Rbrc6msNPGfMDFWGyT3Tu2ObqnSEA-57hZeuCbFrOya3vUwgSlc66rfvZj2xpzg'
107
-
108
- expect { @instance.validate(token) }.not_to raise_exception
109
- end
110
-
111
- it 'is expected to raise an error with an invalid signature' do
112
- token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTEifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.invalidsignature'
113
-
114
- expect { @instance.validate(token) }.to raise_exception('Invalid ID token signature')
115
- end
116
-
117
- it 'is expected to raise an error when the public key cannot be found' do
118
- token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTIifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.r2ksgiiM8zGJ6byea_Fq_zWWEmUdOnwQLVdb5JzgdBv1GUQFp-4LNaRhcga4FIrbKgxaPeewGLTq2VqfjmNJUNfARcE3QEacQ_JEHkC6zKZIiqcu4msHl8X9HXyHxOPHMTTjYMjauPzET7UH_oLxF68DDDQqvKX9VqLsncpyC-KdTCFTLGlFSq6pxmYt6gwrF2Uo15Gzc6qe2I9-MTXCYd44VW1zQi6GhNJNKbXH6U3bf7nof0ot1PSjBXXuLgf6d3qumTStECCjIUmdBb6FiEX4SSRI4MgHWj4q0LyN28baRpYwYPhVnjzUxOP7OLjKiHs45ORBhuAWhrJnuR_uBQ'
119
-
120
- expect do
121
- @instance.validate(token)
122
- end.to raise_exception('Could not find a public key for Key ID (kid) "test-key-2"')
123
- end
124
-
125
- it 'is expected to fetch the JWK set from the url if the public key cannot be found and the cache is not empty' do
126
- token = 'eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2V5LTIifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.r2ksgiiM8zGJ6byea_Fq_zWWEmUdOnwQLVdb5JzgdBv1GUQFp-4LNaRhcga4FIrbKgxaPeewGLTq2VqfjmNJUNfARcE3QEacQ_JEHkC6zKZIiqcu4msHl8X9HXyHxOPHMTTjYMjauPzET7UH_oLxF68DDDQqvKX9VqLsncpyC-KdTCFTLGlFSq6pxmYt6gwrF2Uo15Gzc6qe2I9-MTXCYd44VW1zQi6GhNJNKbXH6U3bf7nof0ot1PSjBXXuLgf6d3qumTStECCjIUmdBb6FiEX4SSRI4MgHWj4q0LyN28baRpYwYPhVnjzUxOP7OLjKiHs45ORBhuAWhrJnuR_uBQ'
127
- Minty::Algorithm::RS256.jwks_url(JWKS_URL).jwks
128
- stub_jwks(JWKS_RESPONSE_2)
129
- @instance.validate(token)
130
-
131
- expect(a_request(:get, JWKS_URL)).to have_been_made.twice
132
- end
133
- end
134
-
135
- context 'context validation' do
136
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.Hn38QVtN_mWN0c-jOa-Fqq69kXpbBp0THsvE-CQ47Ps'
137
-
138
- it 'is expected to raise an error with a non-integer leeway' do
139
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ leeway: '1' }))
140
-
141
- expect { instance.validate(token) }.to raise_exception('Must supply a valid leeway')
142
- end
143
-
144
- it 'is expected to raise an error with a negative leeway' do
145
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ leeway: -1 }))
146
-
147
- expect { instance.validate(token) }.to raise_exception('Must supply a valid leeway')
148
- end
149
-
150
- it 'is expected to raise an error with an empty nonce' do
151
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ nonce: '' }))
152
-
153
- expect { instance.validate(token) }.to raise_exception('Must supply a valid nonce')
154
- end
155
-
156
- it 'is expected to raise an error with an empty organization' do
157
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ organization: '' }))
158
-
159
- expect { instance.validate(token) }.to raise_exception('Must supply a valid organization')
160
- end
161
-
162
- it 'is expected to raise an error with an empty issuer' do
163
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ issuer: '' }))
164
-
165
- expect { instance.validate(token) }.to raise_exception('Must supply a valid issuer')
166
- end
167
-
168
- it 'is expected to raise an error with an empty audience' do
169
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ audience: '' }))
170
-
171
- expect { instance.validate(token) }.to raise_exception('Must supply a valid audience')
172
- end
173
-
174
- it 'is expected to raise an error with a non-integer max_age' do
175
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: '1' }))
176
-
177
- expect { instance.validate(token) }.to raise_exception('Must supply a valid max_age')
178
- end
179
-
180
- it 'is expected to raise an error with a negative max_age' do
181
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: -1 }))
182
-
183
- expect { instance.validate(token) }.to raise_exception('Must supply a valid max_age')
184
- end
185
- end
186
-
187
- context 'claims validation' do
188
- before :all do
189
- @instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT)
190
- end
191
-
192
- it 'is expected to raise an error with a missing iss' do
193
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.QL2B2tqJhlW9rc8HQ3PQKkjDufBeSvtRBtJmRPdQ5K8'
194
-
195
- expect do
196
- @instance.validate(token)
197
- end.to raise_exception('Issuer (iss) claim must be a string present in the ID token')
198
- end
199
-
200
- it 'is expected to raise an error with a invalid iss' do
201
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzb21ldGhpbmctZWxzZSIsInN1YiI6ImF1dGgwfDEyMzQ1Njc4OSIsImF1ZCI6WyJ0b2tlbnMtdGVzdC0xMjMiLCJleHRlcm5hbC10ZXN0LTk5OSJdLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MSwibm9uY2UiOiJhMWIyYzNkNGU1IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.AhMMouDlGMdxTYrY9Cn-p8svJ8ssKmsHeT6JNRVTh10'
202
-
203
- expect do
204
- @instance.validate(token)
205
- end.to raise_exception("Issuer (iss) claim mismatch in the ID token; expected \"#{CONTEXT[:issuer]}\", found \"something-else\"")
206
- end
207
-
208
- it 'is expected to raise an error with a missing sub' do
209
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0._4sUXtAZYpGrO3QaYArXnk2JivCqixa7hgHhH3w4SlY'
210
-
211
- expect do
212
- @instance.validate(token)
213
- end.to raise_exception('Subject (sub) claim must be a string present in the ID token')
214
- end
215
-
216
- it 'is expected to raise an error with a missing aud' do
217
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MSwibm9uY2UiOiJhMWIyYzNkNGU1IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.TlwnBmGUKe0SElSYKxPqsG1mujkK2t1CwDJGGiWRdXA'
218
-
219
- expect do
220
- @instance.validate(token)
221
- end.to raise_exception('Audience (aud) claim must be a string or array of strings present in the ID token')
222
- end
223
-
224
- it 'is expected to raise an error with an invalid string aud' do
225
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOiJleHRlcm5hbC10ZXN0LTk5OSIsImV4cCI6MTU4Nzc2NTM2MSwiaWF0IjoxNTg3NTkyNTYxLCJub25jZSI6ImExYjJjM2Q0ZTUiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1ODc2Nzg5NjF9.-Tf5CIi2bZ51UdgqxFWQNXpJJmK5GgsetcVoVrQwHIA'
226
-
227
- expect do
228
- @instance.validate(token)
229
- end.to raise_exception("Audience (aud) claim mismatch in the ID token; expected \"#{CONTEXT[:audience]}\", found \"external-test-999\"")
230
- end
231
-
232
- it 'is expected to raise an error with an invalid array aud' do
233
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsiZXh0ZXJuYWwtdGVzdC05OTgiLCJleHRlcm5hbC10ZXN0LTk5OSJdLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MSwibm9uY2UiOiJhMWIyYzNkNGU1IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.Q1GRVL5g3RLQqG5sEV_cc8WW_oiZzFIAfzRfBdxMW2s'
234
-
235
- expect do
236
- @instance.validate(token)
237
- end.to raise_exception("Audience (aud) claim mismatch in the ID token; expected \"#{CONTEXT[:audience]}\" but was not one of \"external-test-998, external-test-999\"")
238
- end
239
-
240
- it 'is expected to raise an error with a missing exp' do
241
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiaWF0IjoxNTg3NTkyNTYxLCJub25jZSI6ImExYjJjM2Q0ZTUiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1ODc2Nzg5NjF9.aoLiQX3sHsf1bEbc0axbjJ9qV6hhomtEzJq-FT8OGF0'
242
-
243
- expect do
244
- @instance.validate(token)
245
- end.to raise_exception('Expiration Time (exp) claim must be a number present in the ID token')
246
- end
247
-
248
- it 'is expected to raise an error with a invalid exp' do
249
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NTkyNTYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.A8Pc0vlCG5Ufez7VIoRqXTYpJehalTEgGX9cR2xJLkU'
250
- clock = CLOCK + LEEWAY + 1
251
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ clock: clock }))
252
-
253
- expect do
254
- instance.validate(token)
255
- end.to raise_exception("Expiration Time (exp) claim mismatch in the ID token; current time \"#{clock}\" is after expiration time \"1587592621\"")
256
- end
257
-
258
- it 'is expected to raise an error with a missing iat' do
259
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJub25jZSI6ImExYjJjM2Q0ZTUiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1ODc2Nzg5NjF9.Jea6UVJsAK7Hnb494f_WIQCIbaLTnnCvMenSY1Y2toc'
260
-
261
- expect do
262
- @instance.validate(token)
263
- end.to raise_exception('Issued At (iat) claim must be a number present in the ID token')
264
- end
265
-
266
- it 'is expected not to raise an error with a missing but not required nonce' do
267
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.-o5grnyODbBdRgzcrn7Sf9Hb6eOC0x_U2i3YjVgHN0U'
268
-
269
- expect { @instance.validate(token) }.not_to raise_exception
270
- end
271
-
272
- it 'is expected to raise an error with a missing but required nonce' do
273
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.-o5grnyODbBdRgzcrn7Sf9Hb6eOC0x_U2i3YjVgHN0U'
274
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ nonce: 'a1b2c3d4e5' }))
275
-
276
- expect do
277
- instance.validate(token)
278
- end.to raise_exception('Nonce (nonce) claim must be a string present in the ID token')
279
- end
280
-
281
- it 'is expected to raise an error with an invalid nonce' do
282
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiMDAwOTk5IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.XqQPdFN4m5kmTUQQi_mAJu0LQOeUTS9lF2J_xWc_j-0'
283
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ nonce: 'a1b2c3d4e5' }))
284
-
285
- expect do
286
- instance.validate(token)
287
- end.to raise_exception('Nonce (nonce) claim mismatch in the ID token; expected "a1b2c3d4e5", found "000999"')
288
- end
289
-
290
- it 'is expected to raise an error with a missing azp' do
291
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF1dGhfdGltZSI6MTU4NzY3ODk2MX0.LrgYkIbWrxMq6jvvkL1lxWL237ii1IBhtN2o_tDxFns'
292
-
293
- expect do
294
- @instance.validate(token)
295
- end.to raise_exception('Authorized Party (azp) claim must be a string present in the ID token')
296
- end
297
-
298
- it 'is expected to raise an error with an invalid azp' do
299
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6ImV4dGVybmFsLXRlc3QtOTk5IiwiYXV0aF90aW1lIjoxNTg3Njc4OTYxfQ.3DX-LY3B4UngDML-9nv11Sv89ECJpRpOLeWnkF1vAFY'
300
-
301
- expect do
302
- @instance.validate(token)
303
- end.to raise_exception('Authorized Party (azp) claim mismatch in the ID token; expected "tokens-test-123", found "external-test-999"')
304
- end
305
-
306
- it 'is expected to raise an error with a missing auth_time' do
307
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyJ9.JqUotnjHbGW0FcHz1s1YsRkce9Sbpsv2AEBDMpcUhp8'
308
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: 120 }))
309
-
310
- expect do
311
- instance.validate(token)
312
- end.to raise_exception('Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified')
313
- end
314
-
315
- it 'is expected to raise an error with a invalid auth_time' do
316
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU4NzU5MjU2MX0.B7eWHJPHjhOh0ALjIQi0ro6zVsqGIeHd0gpRZsv6Hg8'
317
- max_age = 120
318
- auth_time = CLOCK + LEEWAY + max_age
319
- clock = auth_time + 1
320
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ max_age: max_age, clock: clock }))
321
-
322
- expect do
323
- instance.validate(token)
324
- end.to raise_exception("Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time \"#{clock}\" is after last auth at \"#{auth_time}\"")
325
- end
326
-
327
- it 'is expected not to raise an error when org_id exsist in the token, but not required' do
328
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNjE2NjE3ODgxLCJpYXQiOjE2MTY0NDUwODEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTYxNjUzMTQ4MSwib3JnX2lkIjoidGVzdE9yZyJ9.AOafUKUNgaxUXpSRYFCeJERcwrQZ4q2NZlutwGXnh9I'
329
- expect { @instance.validate(token) }.not_to raise_exception
330
- end
331
-
332
- it 'is expected to raise an error with a missing but required organization' do
333
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNjE2NjE4MTg1LCJpYXQiOjE2MTY0NDUzODUsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTYxNjUzMTc4NX0.UMo5pmgceXO9lIKzbk7X0ZhE5DOe0IP2LfMKdUj03zQ'
334
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ organization: 'a1b2c3d4e5' }))
335
-
336
- expect do
337
- instance.validate(token)
338
- end.to raise_exception('Organization Id (org_id) claim must be a string present in the ID token')
339
- end
340
-
341
- it 'is expected to raise an error with an invalid organization' do
342
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNjE2NjE3ODgxLCJpYXQiOjE2MTY0NDUwODEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTYxNjUzMTQ4MSwib3JnX2lkIjoidGVzdE9yZyJ9.AOafUKUNgaxUXpSRYFCeJERcwrQZ4q2NZlutwGXnh9I'
343
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ organization: 'a1b2c3d4e5' }))
344
-
345
- expect do
346
- instance.validate(token)
347
- end.to raise_exception('Organization Id (org_id) claim value mismatch in the ID token; expected "a1b2c3d4e5", found "testOrg"')
348
- end
349
-
350
- it 'is expected to NOT raise an error with a valid organization' do
351
- token = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxNjE2NjE3ODgxLCJpYXQiOjE2MTY0NDUwODEsIm5vbmNlIjoiYTFiMmMzZDRlNSIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTYxNjUzMTQ4MSwib3JnX2lkIjoidGVzdE9yZyJ9.AOafUKUNgaxUXpSRYFCeJERcwrQZ4q2NZlutwGXnh9I'
352
- instance = Minty::Mixins::Validation::IdTokenValidator.new(CONTEXT.merge({ organization: 'testOrg' }))
353
-
354
- expect { instance.validate(token) }.not_to raise_exception
355
- end
356
- end
357
- end
358
-
359
- describe Minty::Algorithm::HS256 do
360
- context 'class' do
361
- it 'is expected to respond to :secret' do
362
- expect(Minty::Algorithm::HS256).to respond_to(:secret)
363
- end
364
-
365
- it 'is expected not to respond to :new' do
366
- expect(Minty::Algorithm::HS256).not_to respond_to(:new)
367
- end
368
- end
369
-
370
- context 'instance' do
371
- it 'is expected to respond to :secret' do
372
- instance = Minty::Algorithm::HS256.secret('secret')
373
-
374
- expect(instance).to respond_to(:secret)
375
- end
376
-
377
- it 'is expected to return the secret' do
378
- instance = Minty::Algorithm::HS256.secret('secret')
379
-
380
- expect(instance.secret).to eq('secret')
381
- end
382
-
383
- it 'is expected to return the algorithm name' do
384
- instance = Minty::Algorithm::HS256.secret('secret')
385
-
386
- expect(instance.name).to eq('HS256')
387
- end
388
- end
389
-
390
- context 'parameters' do
391
- expected_error = 'Must supply a valid secret'
392
-
393
- it 'is expected to raise an error with a nil secret' do
394
- expect { Minty::Algorithm::HS256.secret(nil) }.to raise_exception(expected_error)
395
- end
396
-
397
- it 'is expected to raise an error with an empty secret' do
398
- expect { Minty::Algorithm::HS256.secret('') }.to raise_exception(expected_error)
399
- end
400
- end
401
- end
402
-
403
- describe Minty::Algorithm::RS256 do
404
- before :each do
405
- stub_jwks
406
- end
407
-
408
- after :each do
409
- Minty::Algorithm::RS256.remove_jwks
410
- WebMock.reset!
411
- end
412
-
413
- context 'class' do
414
- it 'is expected to respond to :jwks_url' do
415
- expect(Minty::Algorithm::RS256).to respond_to(:jwks_url)
416
- end
417
-
418
- it 'is expected not to respond to :new' do
419
- expect(Minty::Algorithm::RS256).not_to respond_to(:new)
420
- end
421
- end
422
-
423
- context 'instance' do
424
- it 'is expected to respond to :jwks' do
425
- instance = Minty::Algorithm::RS256.jwks_url('jwks url')
426
-
427
- expect(instance).to respond_to(:jwks)
428
- end
429
-
430
- it 'is expected to respond to :fetched_jwks?' do
431
- instance = Minty::Algorithm::RS256.jwks_url('jwks url')
432
-
433
- expect(instance).to respond_to(:fetched_jwks?)
434
- end
435
-
436
- it 'is expected to return a jwks' do
437
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
438
-
439
- expect(instance.jwks).to have_key('keys') and contain_exactly(a_hash_including(kid: 'test-key-1'))
440
- end
441
-
442
- it 'is expected to return if the jwks was fetched from the url' do
443
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
444
- instance.jwks
445
-
446
- expect(instance.fetched_jwks?).to eq(true)
447
- end
448
-
449
- it 'is expected to return if the jwks was fetched from the cache' do
450
- Minty::Algorithm::RS256.jwks_url(JWKS_URL).jwks
451
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
452
- instance.jwks
453
-
454
- expect(instance.fetched_jwks?).to eq(false)
455
- end
456
-
457
- it 'is expected to return the algorithm name' do
458
- instance = Minty::Algorithm::RS256.jwks_url('jwks url')
459
-
460
- expect(instance.name).to eq('RS256')
461
- end
462
- end
463
-
464
- context 'parameters' do
465
- it 'is expected to raise an error with a nil jwks_url' do
466
- expect { Minty::Algorithm::RS256.jwks_url(nil) }.to raise_exception('Must supply a valid jwks_url')
467
- end
468
-
469
- it 'is expected to raise an error with an empty jwks_url' do
470
- expect { Minty::Algorithm::RS256.jwks_url('') }.to raise_exception('Must supply a valid jwks_url')
471
- end
472
-
473
- it 'is expected to raise an error with a non-integer lifetime' do
474
- expect do
475
- Minty::Algorithm::RS256.jwks_url('jwks url', lifetime: '1')
476
- end.to raise_exception('Must supply a valid lifetime')
477
- end
478
-
479
- it 'is expected to raise an error with a negative lifetime' do
480
- expect do
481
- Minty::Algorithm::RS256.jwks_url('jwks url', lifetime: -1)
482
- end.to raise_exception('Must supply a valid lifetime')
483
- end
484
- end
485
-
486
- context 'cache' do
487
- it 'is expected to fetch the jwks from the url when the cache is empty' do
488
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
489
- instance.jwks
490
-
491
- expect(a_request(:get, JWKS_URL)).to have_been_made.once
492
- end
493
-
494
- it 'is expected to fetch the jwks from the url when the cache is expired' do
495
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL, lifetime: 0)
496
- instance.jwks
497
- instance.jwks
498
-
499
- expect(a_request(:get, JWKS_URL)).to have_been_made.twice
500
- end
501
-
502
- it 'is not expected to fetch the jwks from the url when there is a value cached' do
503
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
504
- instance.jwks
505
- instance.jwks
506
-
507
- expect(a_request(:get, JWKS_URL)).to have_been_made.once
508
- end
509
-
510
- it 'is expected to fetch the jwks from multiple urls' do
511
- stub_jwks(JWKS_RESPONSE_2, JWKS_URL_2)
512
-
513
- instance1 = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
514
- instance2 = Minty::Algorithm::RS256.jwks_url(JWKS_URL_2)
515
- instance1.jwks
516
- instance2.jwks
517
- instance1.jwks
518
-
519
- expect(a_request(:get, JWKS_URL)).to have_been_made.once
520
- expect(a_request(:get, JWKS_URL_2)).to have_been_made.once
521
- end
522
-
523
- it 'is expected to forcibly fetch the jwks from the url' do
524
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
525
- instance.jwks
526
- instance.jwks(force: true)
527
-
528
- expect(a_request(:get, JWKS_URL)).to have_been_made.twice
529
- end
530
-
531
- it 'is expected to forcibly fetch the jwks from the url and cache it' do
532
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
533
- instance.jwks(force: true)
534
- instance.jwks
535
-
536
- expect(a_request(:get, JWKS_URL)).to have_been_made.once
537
- end
538
-
539
- it 'is expected to return the last cached value if the jwks could not be fetched' do
540
- Minty::Algorithm::RS256.jwks_url(JWKS_URL).jwks
541
- stub_request(:get, JWKS_URL).to_return(body: 'invalid')
542
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
543
-
544
- expect(instance.jwks).to have_key('keys') and contain_exactly(a_hash_including(kid: 'test-key-1'))
545
- end
546
-
547
- it 'is expected to raise an error if the jwks could not be fetched and the cache is empty' do
548
- stub_request(:get, JWKS_URL).to_return(body: 'invalid')
549
- instance = Minty::Algorithm::RS256.jwks_url(JWKS_URL)
550
-
551
- expect { instance.jwks }.to raise_exception('Could not fetch the JWK set')
552
- end
553
- end
554
- end
555
- # rubocop:enable Metrics/BlockLength
556
-
557
- def stub_jwks(stub = JWKS_RESPONSE_1, url = JWKS_URL)
558
- stub_request(:get, url).to_return(body: stub.to_json)
559
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Credentials
4
- module_function
5
-
6
- def v2_creds
7
- {
8
- domain: ENV.fetch('DOMAIN', 'DOMAIN'),
9
- client_id: ENV.fetch('CLIENT_ID', 'CLIENT_ID'),
10
- client_secret: ENV.fetch('CLIENT_SECRET', 'TEST_CLIENT_SECRET'),
11
- token: ENV.fetch('MASTER_JWT', 'TEST_MASTER_JWT')
12
- }
13
- end
14
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class DummyClass
4
- include Minty::Mixins::Headers
5
-
6
- attr_reader :domain, :client_id, :client_secret, :audience
7
-
8
- def initialize
9
- @domain = 'test.minty.page'
10
- @client_id = '__test_client_id__'
11
- @client_secret = '__test_client_secret__'
12
- @audience = "https://#{@domain}/api/v2/"
13
- end
14
-
15
- %i[get post put patch delete delete_with_body].each do |method|
16
- define_method(method) do |_path, _body = {}|
17
- true
18
- end
19
- end
20
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class DummyClassForProxy
4
- include Minty::Mixins::HTTPProxy
5
- include Minty::Mixins::Headers
6
- end
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class DummyClassForRestClient < RestClient::Exception
4
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class DummyClassForTokens
4
- include Minty::Mixins::HTTPProxy
5
- include Minty::Mixins::Headers
6
- include Minty::Mixins::Initializer
7
-
8
- def initialize(config)
9
- extend Minty::Api::AuthenticationEndpoints
10
- @client_id = config[:client_id]
11
- @client_secret = config[:client_secret]
12
- @audience = config[:api_identifier]
13
- @domain = config[:domain]
14
- @base_uri = "https://#{@domain}"
15
- @token = config[:token]
16
- @token_expires_at = config[:token_expires_at]
17
- end
18
- end
@@ -1,13 +0,0 @@
1
- [
2
- {
3
- "email": "john.doe@contoso.com",
4
- "email_verified": false,
5
- "app_metadata": {
6
- "roles": ["admin"],
7
- "plan": "premium"
8
- },
9
- "user_metadata": {
10
- "theme": "light"
11
- }
12
- }
13
- ]
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- StubResponse = Struct.new(:body, :success?, :code, :headers)