minty 1.0.0 → 1.1.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.
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 -30
  47. data/lib/minty/api/v2.rb +0 -8
  48. data/lib/minty/client.rb +0 -7
  49. data/lib/minty/exception.rb +0 -50
  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)