omniauth-auth0 2.2.0 → 2.3.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 +4 -4
- data/.circleci/config.yml +1 -22
- data/.github/CODEOWNERS +1 -0
- data/.github/stale.yml +20 -0
- data/.snyk +9 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +60 -59
- data/README.md +11 -10
- data/lib/omniauth-auth0/version.rb +1 -1
- data/lib/omniauth/auth0/errors.rb +11 -0
- data/lib/omniauth/auth0/jwt_validator.rb +123 -30
- data/lib/omniauth/strategies/auth0.rb +29 -6
- data/spec/omniauth/auth0/jwt_validator_spec.rb +208 -39
- data/spec/omniauth/strategies/auth0_spec.rb +18 -1
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 07f73634dc123aa4d6912e4d17e7c461948cf6b1fa2086003600434bfd99dcf9
|
|
4
|
+
data.tar.gz: 29e79d38181335c08618108b96e667d24c768185432e2d5e22312e716c333b48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0d1fa93a13a96f24a55ea4fa475561d7252cfd39a2975277ca93be8a91b07a58222dd16e522225f1e4bf0ff3ad5a7456bdac6e640e2e372f3a415ca6b4fafe7a
|
|
7
|
+
data.tar.gz: 6528f8a41f91bffb7a8c6285882a0f9179c51a3bc133824f896cbae7d36f7deafa2d70bf6f7b20bdb684c66831d043f91c3c9f938cc21d603da4d7788a4cca1d
|
data/.circleci/config.yml
CHANGED
|
@@ -15,35 +15,14 @@ jobs:
|
|
|
15
15
|
paths:
|
|
16
16
|
- Gemfile
|
|
17
17
|
- Gemfile.lock
|
|
18
|
+
- .snyk
|
|
18
19
|
- save_cache:
|
|
19
20
|
key: gems-v2--{{ checksum "Gemfile.lock" }}
|
|
20
21
|
paths:
|
|
21
22
|
- vendor/bundle
|
|
22
23
|
- run: bundle exec rake spec
|
|
23
|
-
snyk:
|
|
24
|
-
docker:
|
|
25
|
-
- image: snyk/snyk-cli:rubygems
|
|
26
|
-
steps:
|
|
27
|
-
- attach_workspace:
|
|
28
|
-
at: .
|
|
29
|
-
- run: snyk test
|
|
30
|
-
- run:
|
|
31
|
-
command: |
|
|
32
|
-
if [[ "${CIRCLE_BRANCH}" == "master" ]]
|
|
33
|
-
then
|
|
34
|
-
snyk monitor --org=auth0-sdks
|
|
35
|
-
fi
|
|
36
|
-
when: always
|
|
37
24
|
|
|
38
25
|
workflows:
|
|
39
26
|
tests:
|
|
40
27
|
jobs:
|
|
41
28
|
- run-tests
|
|
42
|
-
snyk:
|
|
43
|
-
jobs:
|
|
44
|
-
- run-tests
|
|
45
|
-
- snyk:
|
|
46
|
-
# Must define SNYK_TOKEN env
|
|
47
|
-
context: snyk-env
|
|
48
|
-
requires:
|
|
49
|
-
- run-tests
|
data/.github/CODEOWNERS
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @auth0/dx-sdks-approver
|
data/.github/stale.yml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Configuration for probot-stale - https://github.com/probot/stale
|
|
2
|
+
|
|
3
|
+
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
|
4
|
+
daysUntilStale: 90
|
|
5
|
+
|
|
6
|
+
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
|
7
|
+
daysUntilClose: 7
|
|
8
|
+
|
|
9
|
+
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
|
10
|
+
exemptLabels: []
|
|
11
|
+
|
|
12
|
+
# Set to true to ignore issues with an assignee (defaults to false)
|
|
13
|
+
exemptAssignees: true
|
|
14
|
+
|
|
15
|
+
# Label to use when marking as stale
|
|
16
|
+
staleLabel: closed:stale
|
|
17
|
+
|
|
18
|
+
# Comment to post when marking as stale. Set to `false` to disable
|
|
19
|
+
markComment: >
|
|
20
|
+
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇♂️
|
data/.snyk
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
|
2
|
+
version: v1.13.5
|
|
3
|
+
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
|
|
4
|
+
ignore:
|
|
5
|
+
SNYK-RUBY-OMNIAUTH-174820:
|
|
6
|
+
- '*':
|
|
7
|
+
reason: Not affected.
|
|
8
|
+
expires: 2020-01-01T00:00:00.000Z
|
|
9
|
+
patch: {}
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [v2.3.0](https://github.com/auth0/omniauth-auth0/tree/v2.3.0) (2020-03-06)
|
|
4
|
+
[Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v2.3.0...v2.2.0)
|
|
5
|
+
|
|
6
|
+
**Added**
|
|
7
|
+
- Improved OIDC Compliance [\#74](https://github.com/auth0/omniauth-auth0/pull/92) ([davidpatrick](https://github.com/davidpatrick))
|
|
8
|
+
|
|
3
9
|
## [v2.2.0](https://github.com/auth0/omniauth-auth0/tree/v2.2.0) (2018-04-18)
|
|
4
10
|
[Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v2.1.0...v2.2.0)
|
|
5
11
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
omniauth-auth0 (2.
|
|
4
|
+
omniauth-auth0 (2.3.0)
|
|
5
5
|
omniauth-oauth2 (~> 1.5)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
addressable (2.
|
|
11
|
-
public_suffix (>= 2.0.2, <
|
|
10
|
+
addressable (2.7.0)
|
|
11
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
12
12
|
ast (2.4.0)
|
|
13
|
-
codecov (0.1.
|
|
13
|
+
codecov (0.1.16)
|
|
14
14
|
json
|
|
15
15
|
simplecov
|
|
16
16
|
url
|
|
@@ -19,15 +19,15 @@ GEM
|
|
|
19
19
|
safe_yaml (~> 1.0.0)
|
|
20
20
|
daemons (1.3.1)
|
|
21
21
|
diff-lcs (1.3)
|
|
22
|
-
docile (1.3.
|
|
23
|
-
dotenv (2.7.
|
|
22
|
+
docile (1.3.2)
|
|
23
|
+
dotenv (2.7.5)
|
|
24
24
|
eventmachine (1.2.7)
|
|
25
|
-
faraday (0.
|
|
25
|
+
faraday (1.0.0)
|
|
26
26
|
multipart-post (>= 1.2, < 3)
|
|
27
|
-
ffi (1.
|
|
27
|
+
ffi (1.12.2)
|
|
28
28
|
formatador (0.2.5)
|
|
29
|
-
gem-release (2.
|
|
30
|
-
guard (2.
|
|
29
|
+
gem-release (2.1.1)
|
|
30
|
+
guard (2.16.1)
|
|
31
31
|
formatador (>= 0.2.4)
|
|
32
32
|
listen (>= 2.7, < 4.0)
|
|
33
33
|
lumberjack (>= 1.0.12, < 2.0)
|
|
@@ -41,104 +41,105 @@ GEM
|
|
|
41
41
|
guard (~> 2.1)
|
|
42
42
|
guard-compat (~> 1.1)
|
|
43
43
|
rspec (>= 2.99.0, < 4.0)
|
|
44
|
-
hashdiff (0.
|
|
45
|
-
hashie (
|
|
46
|
-
jaro_winkler (1.5.
|
|
47
|
-
json (2.
|
|
48
|
-
jwt (2.1
|
|
44
|
+
hashdiff (1.0.1)
|
|
45
|
+
hashie (4.1.0)
|
|
46
|
+
jaro_winkler (1.5.4)
|
|
47
|
+
json (2.3.0)
|
|
48
|
+
jwt (2.2.1)
|
|
49
49
|
listen (3.1.5)
|
|
50
50
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
|
51
51
|
rb-inotify (~> 0.9, >= 0.9.7)
|
|
52
52
|
ruby_dep (~> 1.2)
|
|
53
|
-
lumberjack (1.
|
|
53
|
+
lumberjack (1.2.4)
|
|
54
54
|
method_source (0.9.2)
|
|
55
|
-
multi_json (1.
|
|
55
|
+
multi_json (1.14.1)
|
|
56
56
|
multi_xml (0.6.0)
|
|
57
|
-
multipart-post (2.
|
|
58
|
-
mustermann (1.
|
|
57
|
+
multipart-post (2.1.1)
|
|
58
|
+
mustermann (1.1.1)
|
|
59
|
+
ruby2_keywords (~> 0.0.1)
|
|
59
60
|
nenv (0.3.0)
|
|
60
|
-
notiffany (0.1.
|
|
61
|
+
notiffany (0.1.3)
|
|
61
62
|
nenv (~> 0.1)
|
|
62
63
|
shellany (~> 0.0)
|
|
63
|
-
oauth2 (1.4.
|
|
64
|
-
faraday (>= 0.8, <
|
|
64
|
+
oauth2 (1.4.4)
|
|
65
|
+
faraday (>= 0.8, < 2.0)
|
|
65
66
|
jwt (>= 1.0, < 3.0)
|
|
66
67
|
multi_json (~> 1.3)
|
|
67
68
|
multi_xml (~> 0.5)
|
|
68
69
|
rack (>= 1.2, < 3)
|
|
69
|
-
omniauth (1.9.
|
|
70
|
-
hashie (>= 3.4.6
|
|
70
|
+
omniauth (1.9.1)
|
|
71
|
+
hashie (>= 3.4.6)
|
|
71
72
|
rack (>= 1.6.2, < 3)
|
|
72
73
|
omniauth-oauth2 (1.6.0)
|
|
73
74
|
oauth2 (~> 1.1)
|
|
74
75
|
omniauth (~> 1.9)
|
|
75
|
-
parallel (1.
|
|
76
|
-
parser (2.
|
|
76
|
+
parallel (1.19.1)
|
|
77
|
+
parser (2.7.0.4)
|
|
77
78
|
ast (~> 2.4.0)
|
|
78
79
|
pry (0.12.2)
|
|
79
80
|
coderay (~> 1.1.0)
|
|
80
81
|
method_source (~> 0.9.0)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
rack (2.0.
|
|
84
|
-
rack-protection (2.0.5)
|
|
82
|
+
public_suffix (4.0.3)
|
|
83
|
+
rack (2.2.2)
|
|
84
|
+
rack-protection (2.0.8.1)
|
|
85
85
|
rack
|
|
86
86
|
rack-test (1.1.0)
|
|
87
87
|
rack (>= 1.0, < 3)
|
|
88
88
|
rainbow (3.0.0)
|
|
89
|
-
rake (
|
|
89
|
+
rake (13.0.1)
|
|
90
90
|
rb-fsevent (0.10.3)
|
|
91
|
-
rb-inotify (0.10.
|
|
91
|
+
rb-inotify (0.10.1)
|
|
92
92
|
ffi (~> 1.0)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
rspec-
|
|
96
|
-
rspec-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
rexml (3.2.4)
|
|
94
|
+
rspec (3.9.0)
|
|
95
|
+
rspec-core (~> 3.9.0)
|
|
96
|
+
rspec-expectations (~> 3.9.0)
|
|
97
|
+
rspec-mocks (~> 3.9.0)
|
|
98
|
+
rspec-core (3.9.1)
|
|
99
|
+
rspec-support (~> 3.9.1)
|
|
100
|
+
rspec-expectations (3.9.0)
|
|
100
101
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
101
|
-
rspec-support (~> 3.
|
|
102
|
-
rspec-mocks (3.
|
|
102
|
+
rspec-support (~> 3.9.0)
|
|
103
|
+
rspec-mocks (3.9.1)
|
|
103
104
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
104
|
-
rspec-support (~> 3.
|
|
105
|
-
rspec-support (3.
|
|
106
|
-
rubocop (0.
|
|
105
|
+
rspec-support (~> 3.9.0)
|
|
106
|
+
rspec-support (3.9.2)
|
|
107
|
+
rubocop (0.80.1)
|
|
107
108
|
jaro_winkler (~> 1.5.1)
|
|
108
109
|
parallel (~> 1.10)
|
|
109
|
-
parser (>= 2.
|
|
110
|
-
psych (>= 3.1.0)
|
|
110
|
+
parser (>= 2.7.0.1)
|
|
111
111
|
rainbow (>= 2.2.2, < 4.0)
|
|
112
|
+
rexml
|
|
112
113
|
ruby-progressbar (~> 1.7)
|
|
113
|
-
unicode-display_width (>= 1.4.0, < 1.
|
|
114
|
-
ruby-progressbar (1.10.
|
|
114
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
|
115
|
+
ruby-progressbar (1.10.1)
|
|
116
|
+
ruby2_keywords (0.0.2)
|
|
115
117
|
ruby_dep (1.5.0)
|
|
116
118
|
safe_yaml (1.0.5)
|
|
117
119
|
shellany (0.0.1)
|
|
118
120
|
shotgun (0.9.2)
|
|
119
121
|
rack (>= 1.0)
|
|
120
|
-
simplecov (0.
|
|
122
|
+
simplecov (0.18.5)
|
|
121
123
|
docile (~> 1.1)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
sinatra (2.0.5)
|
|
124
|
+
simplecov-html (~> 0.11)
|
|
125
|
+
simplecov-html (0.12.2)
|
|
126
|
+
sinatra (2.0.8.1)
|
|
126
127
|
mustermann (~> 1.0)
|
|
127
128
|
rack (~> 2.0)
|
|
128
|
-
rack-protection (= 2.0.
|
|
129
|
+
rack-protection (= 2.0.8.1)
|
|
129
130
|
tilt (~> 2.0)
|
|
130
131
|
thin (1.7.2)
|
|
131
132
|
daemons (~> 1.0, >= 1.0.9)
|
|
132
133
|
eventmachine (~> 1.0, >= 1.0.4)
|
|
133
134
|
rack (>= 1, < 3)
|
|
134
|
-
thor (0.
|
|
135
|
-
tilt (2.0.
|
|
136
|
-
unicode-display_width (1.
|
|
135
|
+
thor (1.0.1)
|
|
136
|
+
tilt (2.0.10)
|
|
137
|
+
unicode-display_width (1.6.1)
|
|
137
138
|
url (0.3.2)
|
|
138
|
-
webmock (3.
|
|
139
|
+
webmock (3.8.2)
|
|
139
140
|
addressable (>= 2.3.6)
|
|
140
141
|
crack (>= 0.3.2)
|
|
141
|
-
hashdiff
|
|
142
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
142
143
|
|
|
143
144
|
PLATFORMS
|
|
144
145
|
ruby
|
data/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
An [OmniAuth](https://github.com/intridea/omniauth) strategy for authenticating with [Auth0](https://auth0.com). This strategy is based on the [OmniAuth OAuth2](https://github.com/omniauth/omniauth-oauth2) strategy.
|
|
4
4
|
|
|
5
|
+
**Important security note:** The parent library for this strategy currently has an unresolved security issue. Please see the discussion, including mitigations for Rails and non-Rails applications, [here](https://github.com/auth0/omniauth-auth0/issues/82).
|
|
6
|
+
|
|
5
7
|
[](https://circleci.com/gh/auth0/omniauth-auth0)
|
|
6
8
|
[](https://codecov.io/gh/auth0/omniauth-auth0)
|
|
7
9
|
[](https://badge.fury.io/rb/omniauth-auth0)
|
|
@@ -31,6 +33,12 @@ Add the following line to your `Gemfile`:
|
|
|
31
33
|
gem 'omniauth-auth0'
|
|
32
34
|
```
|
|
33
35
|
|
|
36
|
+
If you're using this strategy with Rails, also add the following for CSRF protection:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
gem 'omniauth-rails_csrf_protection'
|
|
40
|
+
```
|
|
41
|
+
|
|
34
42
|
Then install:
|
|
35
43
|
|
|
36
44
|
```bash
|
|
@@ -63,19 +71,13 @@ provider
|
|
|
63
71
|
{
|
|
64
72
|
authorize_params: {
|
|
65
73
|
scope: 'openid read:users write:order',
|
|
66
|
-
audience: 'https://mydomain/api'
|
|
74
|
+
audience: 'https://mydomain/api',
|
|
75
|
+
max_age: 3600 # time in seconds authentication is valid
|
|
67
76
|
}
|
|
68
77
|
}
|
|
69
78
|
```
|
|
70
79
|
|
|
71
|
-
... which will tell the strategy to send those parameters on every
|
|
72
|
-
|
|
73
|
-
Or you can do it for a specific authentication request by adding them to the query parameters of the redirect URL. Allowed parameters are `connection` and `prompt`:
|
|
74
|
-
|
|
75
|
-
```ruby
|
|
76
|
-
redirect_to '/auth/auth0?connection=google-oauth2'
|
|
77
|
-
redirect_to '/auth/auth0?prompt=none'
|
|
78
|
-
```
|
|
80
|
+
... which will tell the strategy to send those parameters on every authentication request.
|
|
79
81
|
|
|
80
82
|
### Authentication hash
|
|
81
83
|
|
|
@@ -129,7 +131,6 @@ We appreciate feedback and contribution to this repo! Before you get started, pl
|
|
|
129
131
|
|
|
130
132
|
## Support + Feedback
|
|
131
133
|
|
|
132
|
-
|
|
133
134
|
- Use [Community](https://community.auth0.com/) for usage, questions, specific cases.
|
|
134
135
|
- Use [Issues](https://github.com/auth0/omniauth-auth0/issues) here for code-level support and bug reports.
|
|
135
136
|
- Paid customers can use [Support](https://support.auth0.com/) to submit a trouble ticket for production-affecting issues.
|
|
@@ -15,7 +15,8 @@ module OmniAuth
|
|
|
15
15
|
# options.issuer - Application issuer (optional).
|
|
16
16
|
# options.client_id - Application Client ID.
|
|
17
17
|
# options.client_secret - Application Client Secret.
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
def initialize(options, authorize_params = {})
|
|
19
20
|
@domain = uri_string(options.domain)
|
|
20
21
|
|
|
21
22
|
# Use custom issuer if provided, otherwise use domain
|
|
@@ -26,23 +27,38 @@ module OmniAuth
|
|
|
26
27
|
@client_secret = options.client_secret
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
# @param jwt string - JWT to decode.
|
|
31
|
-
# @return hash - The decoded token, if there were no exceptions.
|
|
32
|
-
# @see https://github.com/jwt/ruby-jwt
|
|
33
|
-
def decode(jwt)
|
|
30
|
+
def verify_signature(jwt)
|
|
34
31
|
head = token_head(jwt)
|
|
35
32
|
|
|
36
33
|
# Make sure the algorithm is supported and get the decode key.
|
|
37
|
-
decode_key = @client_secret
|
|
38
34
|
if head[:alg] == 'RS256'
|
|
39
|
-
|
|
40
|
-
elsif head[:alg]
|
|
41
|
-
|
|
35
|
+
[rs256_decode_key(head[:kid]), head[:alg]]
|
|
36
|
+
elsif head[:alg] == 'HS256'
|
|
37
|
+
[@client_secret, head[:alg]]
|
|
38
|
+
else
|
|
39
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Signature algorithm of #{head[:alg]} is not supported. Expected the ID token to be signed with RS256 or HS256")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Verify a JWT.
|
|
44
|
+
# @param jwt string - JWT to verify.
|
|
45
|
+
# @param authorize_params hash - Authorization params to verify on the JWT
|
|
46
|
+
# @return hash - The verified token, if there were no exceptions.
|
|
47
|
+
def verify(jwt, authorize_params = {})
|
|
48
|
+
if !jwt
|
|
49
|
+
raise OmniAuth::Auth0::TokenValidationError.new('ID token is required but missing')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
parts = jwt.split('.')
|
|
53
|
+
if parts.length != 3
|
|
54
|
+
raise OmniAuth::Auth0::TokenValidationError.new('ID token could not be decoded')
|
|
42
55
|
end
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
JWT.decode(jwt,
|
|
57
|
+
key, alg = verify_signature(jwt)
|
|
58
|
+
id_token, header = JWT.decode(jwt, key, false)
|
|
59
|
+
verify_claims(id_token, authorize_params)
|
|
60
|
+
|
|
61
|
+
return id_token
|
|
46
62
|
end
|
|
47
63
|
|
|
48
64
|
# Get the decoded head segment from a JWT.
|
|
@@ -76,26 +92,12 @@ module OmniAuth
|
|
|
76
92
|
end
|
|
77
93
|
|
|
78
94
|
private
|
|
79
|
-
|
|
80
|
-
# Get the JWT decode options
|
|
81
|
-
# Docs: https://github.com/jwt/ruby-jwt#add-custom-header-fields
|
|
82
|
-
# @return hash
|
|
83
|
-
def decode_opts(alg)
|
|
84
|
-
{
|
|
85
|
-
algorithm: alg,
|
|
86
|
-
leeway: 30,
|
|
87
|
-
verify_expiration: true,
|
|
88
|
-
verify_iss: true,
|
|
89
|
-
iss: @issuer,
|
|
90
|
-
verify_aud: true,
|
|
91
|
-
aud: @client_id,
|
|
92
|
-
verify_not_before: true
|
|
93
|
-
}
|
|
94
|
-
end
|
|
95
|
-
|
|
96
95
|
def rs256_decode_key(kid)
|
|
97
96
|
jwks_x5c = jwks_key(:x5c, kid)
|
|
98
|
-
|
|
97
|
+
|
|
98
|
+
if jwks_x5c.nil?
|
|
99
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Could not find a public key for Key ID (kid) '#{kid}''")
|
|
100
|
+
end
|
|
99
101
|
|
|
100
102
|
jwks_public_cert(jwks_x5c.first)
|
|
101
103
|
end
|
|
@@ -129,6 +131,97 @@ module OmniAuth
|
|
|
129
131
|
temp_domain = URI("https://#{uri}") unless temp_domain.scheme
|
|
130
132
|
"#{temp_domain}/"
|
|
131
133
|
end
|
|
134
|
+
|
|
135
|
+
def verify_claims(id_token, authorize_params)
|
|
136
|
+
leeway = authorize_params[:leeway] || 60
|
|
137
|
+
max_age = authorize_params[:max_age]
|
|
138
|
+
nonce = authorize_params[:nonce]
|
|
139
|
+
|
|
140
|
+
verify_iss(id_token)
|
|
141
|
+
verify_sub(id_token)
|
|
142
|
+
verify_aud(id_token)
|
|
143
|
+
verify_expiration(id_token, leeway)
|
|
144
|
+
verify_iat(id_token)
|
|
145
|
+
verify_nonce(id_token, nonce)
|
|
146
|
+
verify_azp(id_token)
|
|
147
|
+
verify_auth_time(id_token, leeway, max_age)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def verify_iss(id_token)
|
|
151
|
+
issuer = id_token['iss']
|
|
152
|
+
if !issuer
|
|
153
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Issuer (iss) claim must be a string present in the ID token")
|
|
154
|
+
elsif @issuer != issuer
|
|
155
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Issuer (iss) claim mismatch in the ID token, expected (#{@issuer}), found (#{id_token['iss']})")
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def verify_sub(id_token)
|
|
160
|
+
subject = id_token['sub']
|
|
161
|
+
if !subject || !subject.is_a?(String) || subject.empty?
|
|
162
|
+
raise OmniAuth::Auth0::TokenValidationError.new('Subject (sub) claim must be a string present in the ID token')
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def verify_aud(id_token)
|
|
167
|
+
audience = id_token['aud']
|
|
168
|
+
if !audience || !(audience.is_a?(String) || audience.is_a?(Array))
|
|
169
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Audience (aud) claim must be a string or array of strings present in the ID token")
|
|
170
|
+
elsif audience.is_a?(Array) && !audience.include?(@client_id)
|
|
171
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Audience (aud) claim mismatch in the ID token; expected #{@client_id} but was not one of #{audience.join(', ')}")
|
|
172
|
+
elsif audience.is_a?(String) && audience != @client_id
|
|
173
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Audience (aud) claim mismatch in the ID token; expected #{@client_id} but found #{audience}")
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def verify_expiration(id_token, leeway)
|
|
178
|
+
expiration = id_token['exp']
|
|
179
|
+
if !expiration || !expiration.is_a?(Integer)
|
|
180
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Expiration time (exp) claim must be a number present in the ID token")
|
|
181
|
+
elsif expiration <= Time.now.to_i - leeway
|
|
182
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Expiration time (exp) claim error in the ID token; current time (#{Time.now}) is after expiration time (#{Time.at(expiration + leeway)})")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def verify_iat(id_token)
|
|
187
|
+
if !id_token['iat']
|
|
188
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Issued At (iat) claim must be a number present in the ID token")
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def verify_nonce(id_token, nonce)
|
|
193
|
+
if nonce
|
|
194
|
+
received_nonce = id_token['nonce']
|
|
195
|
+
if !received_nonce
|
|
196
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Nonce (nonce) claim must be a string present in the ID token")
|
|
197
|
+
elsif nonce != received_nonce
|
|
198
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Nonce (nonce) claim value mismatch in the ID token; expected (#{nonce}), found (#{received_nonce})")
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def verify_azp(id_token)
|
|
204
|
+
audience = id_token['aud']
|
|
205
|
+
if audience.is_a?(Array) && audience.length > 1
|
|
206
|
+
azp = id_token['azp']
|
|
207
|
+
if !azp || !azp.is_a?(String)
|
|
208
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values")
|
|
209
|
+
elsif azp != @client_id
|
|
210
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Authorized Party (azp) claim mismatch in the ID token; expected (#{@client_id}), found (#{azp})")
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def verify_auth_time(id_token, leeway, max_age)
|
|
216
|
+
if max_age
|
|
217
|
+
auth_time = id_token['auth_time']
|
|
218
|
+
if !auth_time || !auth_time.is_a?(Integer)
|
|
219
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified")
|
|
220
|
+
elsif Time.now.to_i > auth_time + max_age + leeway;
|
|
221
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (#{Time.now}) is after last auth time (#{Time.at(auth_time + max_age + leeway)})")
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
132
225
|
end
|
|
133
226
|
end
|
|
134
227
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'base64'
|
|
4
4
|
require 'uri'
|
|
5
|
+
require 'securerandom'
|
|
5
6
|
require 'omniauth-oauth2'
|
|
6
7
|
require 'omniauth/auth0/jwt_validator'
|
|
7
8
|
require 'omniauth/auth0/telemetry'
|
|
@@ -48,10 +49,16 @@ module OmniAuth
|
|
|
48
49
|
)
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
# Retrieve and remove authorization params from the session
|
|
53
|
+
session_authorize_params = session['authorize_params'] || {}
|
|
54
|
+
session.delete('authorize_params')
|
|
55
|
+
|
|
56
|
+
auth_scope = session_authorize_params[:scope]
|
|
57
|
+
if auth_scope.respond_to?(:include?) && auth_scope.include?('openid')
|
|
58
|
+
# Make sure the ID token can be verified and decoded.
|
|
59
|
+
auth0_jwt = OmniAuth::Auth0::JWTValidator.new(options)
|
|
60
|
+
auth0_jwt.verify(credentials['id_token'], session_authorize_params)
|
|
61
|
+
end
|
|
55
62
|
|
|
56
63
|
credentials
|
|
57
64
|
end
|
|
@@ -78,8 +85,18 @@ module OmniAuth
|
|
|
78
85
|
def authorize_params
|
|
79
86
|
params = super
|
|
80
87
|
parsed_query = Rack::Utils.parse_query(request.query_string)
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
%w[connection prompt].each do |key|
|
|
89
|
+
params[key] = parsed_query[key] if parsed_query.key?(key)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Generate nonce
|
|
93
|
+
params[:nonce] = SecureRandom.hex
|
|
94
|
+
# Generate leeway if none exists
|
|
95
|
+
params[:leeway] = 60 unless params[:leeway]
|
|
96
|
+
|
|
97
|
+
# Store authorize params in the session for token verification
|
|
98
|
+
session['authorize_params'] = params
|
|
99
|
+
|
|
83
100
|
params
|
|
84
101
|
end
|
|
85
102
|
|
|
@@ -105,6 +122,12 @@ module OmniAuth
|
|
|
105
122
|
end
|
|
106
123
|
end
|
|
107
124
|
|
|
125
|
+
def callback_phase
|
|
126
|
+
super
|
|
127
|
+
rescue OmniAuth::Auth0::TokenValidationError => e
|
|
128
|
+
fail!(:token_validation_error, e)
|
|
129
|
+
end
|
|
130
|
+
|
|
108
131
|
private
|
|
109
132
|
|
|
110
133
|
# Parse the raw user info.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
require 'json'
|
|
3
3
|
require 'jwt'
|
|
4
|
+
require 'omniauth/auth0/errors'
|
|
4
5
|
|
|
5
6
|
describe OmniAuth::Auth0::JWTValidator do
|
|
6
7
|
#
|
|
@@ -147,7 +148,7 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
147
148
|
end
|
|
148
149
|
end
|
|
149
150
|
|
|
150
|
-
describe 'JWT verifier
|
|
151
|
+
describe 'JWT verifier verify' do
|
|
151
152
|
let(:jwt_validator) do
|
|
152
153
|
make_jwt_validator
|
|
153
154
|
end
|
|
@@ -157,91 +158,259 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
157
158
|
stub_dummy_jwks
|
|
158
159
|
end
|
|
159
160
|
|
|
160
|
-
it 'should fail with
|
|
161
|
+
it 'should fail with missing issuer' do
|
|
162
|
+
expect do
|
|
163
|
+
jwt_validator.verify(make_hs256_token)
|
|
164
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
165
|
+
message: "Issuer (iss) claim must be a string present in the ID token"
|
|
166
|
+
}))
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it 'should fail with invalid issuer' do
|
|
170
|
+
payload = {
|
|
171
|
+
iss: 'https://auth0.com/'
|
|
172
|
+
}
|
|
173
|
+
token = make_hs256_token(payload)
|
|
174
|
+
expect do
|
|
175
|
+
jwt_validator.verify(token)
|
|
176
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
177
|
+
message: "Issuer (iss) claim mismatch in the ID token, expected (https://samples.auth0.com/), found (https://auth0.com/)"
|
|
178
|
+
}))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it 'should fail when subject is missing' do
|
|
161
182
|
payload = {
|
|
183
|
+
iss: "https://#{domain}/",
|
|
184
|
+
sub: ''
|
|
185
|
+
}
|
|
186
|
+
token = make_hs256_token(payload)
|
|
187
|
+
expect do
|
|
188
|
+
jwt_validator.verify(token)
|
|
189
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
190
|
+
message: "Subject (sub) claim must be a string present in the ID token"
|
|
191
|
+
}))
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'should fail with missing audience' do
|
|
195
|
+
payload = {
|
|
196
|
+
iss: "https://#{domain}/",
|
|
197
|
+
sub: 'sub'
|
|
198
|
+
}
|
|
199
|
+
token = make_hs256_token(payload)
|
|
200
|
+
expect do
|
|
201
|
+
jwt_validator.verify(token)
|
|
202
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
203
|
+
message: "Audience (aud) claim must be a string or array of strings present in the ID token"
|
|
204
|
+
}))
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'should fail with invalid audience' do
|
|
208
|
+
payload = {
|
|
209
|
+
iss: "https://#{domain}/",
|
|
210
|
+
sub: 'sub',
|
|
211
|
+
aud: 'Auth0'
|
|
212
|
+
}
|
|
213
|
+
token = make_hs256_token(payload)
|
|
214
|
+
expect do
|
|
215
|
+
jwt_validator.verify(token)
|
|
216
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
217
|
+
message: "Audience (aud) claim mismatch in the ID token; expected #{client_id} but found Auth0"
|
|
218
|
+
}))
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
it 'should fail when missing expiration' do
|
|
222
|
+
payload = {
|
|
223
|
+
iss: "https://#{domain}/",
|
|
224
|
+
sub: 'sub',
|
|
225
|
+
aud: client_id
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
token = make_hs256_token(payload)
|
|
229
|
+
expect do
|
|
230
|
+
jwt_validator.verify(token)
|
|
231
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
232
|
+
message: "Expiration time (exp) claim must be a number present in the ID token"
|
|
233
|
+
}))
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it 'should fail when past expiration' do
|
|
237
|
+
payload = {
|
|
238
|
+
iss: "https://#{domain}/",
|
|
239
|
+
sub: 'sub',
|
|
240
|
+
aud: client_id,
|
|
162
241
|
exp: past_timecode
|
|
163
242
|
}
|
|
243
|
+
|
|
164
244
|
token = make_hs256_token(payload)
|
|
165
245
|
expect do
|
|
166
|
-
jwt_validator.
|
|
167
|
-
end.to raise_error(
|
|
246
|
+
jwt_validator.verify(token)
|
|
247
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
248
|
+
message: "Expiration time (exp) claim error in the ID token; current time (#{Time.now}) is after expiration time (#{Time.at(past_timecode + 60)})"
|
|
249
|
+
}))
|
|
168
250
|
end
|
|
169
251
|
|
|
170
|
-
it 'should fail
|
|
252
|
+
it 'should fail when missing iat' do
|
|
253
|
+
payload = {
|
|
254
|
+
iss: "https://#{domain}/",
|
|
255
|
+
sub: 'sub',
|
|
256
|
+
aud: client_id,
|
|
257
|
+
exp: future_timecode
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
token = make_hs256_token(payload)
|
|
171
261
|
expect do
|
|
172
|
-
jwt_validator.
|
|
173
|
-
end.to raise_error(
|
|
262
|
+
jwt_validator.verify(token)
|
|
263
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
264
|
+
message: "Issued At (iat) claim must be a number present in the ID token"
|
|
265
|
+
}))
|
|
174
266
|
end
|
|
175
267
|
|
|
176
|
-
it 'should fail
|
|
268
|
+
it 'should fail when authorize params has nonce but nonce is missing in the token' do
|
|
177
269
|
payload = {
|
|
178
|
-
iss:
|
|
270
|
+
iss: "https://#{domain}/",
|
|
271
|
+
sub: 'sub',
|
|
272
|
+
aud: client_id,
|
|
273
|
+
exp: future_timecode,
|
|
274
|
+
iat: past_timecode
|
|
179
275
|
}
|
|
276
|
+
|
|
180
277
|
token = make_hs256_token(payload)
|
|
181
278
|
expect do
|
|
182
|
-
jwt_validator.
|
|
183
|
-
end.to raise_error(
|
|
279
|
+
jwt_validator.verify(token, { nonce: 'noncey' })
|
|
280
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
281
|
+
message: "Nonce (nonce) claim must be a string present in the ID token"
|
|
282
|
+
}))
|
|
184
283
|
end
|
|
185
284
|
|
|
186
|
-
it 'should fail
|
|
285
|
+
it 'should fail when authorize params has nonce but token nonce does not match' do
|
|
286
|
+
payload = {
|
|
287
|
+
iss: "https://#{domain}/",
|
|
288
|
+
sub: 'sub',
|
|
289
|
+
aud: client_id,
|
|
290
|
+
exp: future_timecode,
|
|
291
|
+
iat: past_timecode,
|
|
292
|
+
nonce: 'mismatch'
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
token = make_hs256_token(payload)
|
|
296
|
+
expect do
|
|
297
|
+
jwt_validator.verify(token, { nonce: 'noncey' })
|
|
298
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
299
|
+
message: "Nonce (nonce) claim value mismatch in the ID token; expected (noncey), found (mismatch)"
|
|
300
|
+
}))
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it 'should fail when “aud” is an array of strings and azp claim is not present' do
|
|
304
|
+
aud = [
|
|
305
|
+
client_id,
|
|
306
|
+
"https://#{domain}/userinfo"
|
|
307
|
+
]
|
|
187
308
|
payload = {
|
|
188
|
-
|
|
189
|
-
|
|
309
|
+
iss: "https://#{domain}/",
|
|
310
|
+
sub: 'sub',
|
|
311
|
+
aud: aud,
|
|
312
|
+
exp: future_timecode,
|
|
313
|
+
iat: past_timecode
|
|
190
314
|
}
|
|
315
|
+
|
|
191
316
|
token = make_hs256_token(payload)
|
|
192
317
|
expect do
|
|
193
|
-
jwt_validator.
|
|
194
|
-
end.to raise_error(
|
|
318
|
+
jwt_validator.verify(token)
|
|
319
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
320
|
+
message: "Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values"
|
|
321
|
+
}))
|
|
195
322
|
end
|
|
196
323
|
|
|
197
|
-
it 'should fail
|
|
324
|
+
it 'should fail when "azp" claim doesnt match the expected aud' do
|
|
325
|
+
aud = [
|
|
326
|
+
client_id,
|
|
327
|
+
"https://#{domain}/userinfo"
|
|
328
|
+
]
|
|
329
|
+
payload = {
|
|
330
|
+
iss: "https://#{domain}/",
|
|
331
|
+
sub: 'sub',
|
|
332
|
+
aud: aud,
|
|
333
|
+
exp: future_timecode,
|
|
334
|
+
iat: past_timecode,
|
|
335
|
+
azp: 'not_expected'
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
token = make_hs256_token(payload)
|
|
339
|
+
expect do
|
|
340
|
+
jwt_validator.verify(token)
|
|
341
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
342
|
+
message: "Authorized Party (azp) claim mismatch in the ID token; expected (#{client_id}), found (not_expected)"
|
|
343
|
+
}))
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it 'should fail when “max_age” sent on the authentication request and this claim is not present' do
|
|
198
347
|
payload = {
|
|
199
|
-
iss: "https://#{domain}/"
|
|
348
|
+
iss: "https://#{domain}/",
|
|
349
|
+
sub: 'sub',
|
|
350
|
+
aud: client_id,
|
|
351
|
+
exp: future_timecode,
|
|
352
|
+
iat: past_timecode
|
|
200
353
|
}
|
|
354
|
+
|
|
201
355
|
token = make_hs256_token(payload)
|
|
202
356
|
expect do
|
|
203
|
-
jwt_validator.
|
|
204
|
-
end.to raise_error(
|
|
357
|
+
jwt_validator.verify(token, { max_age: 60 })
|
|
358
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
359
|
+
message: "Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified"
|
|
360
|
+
}))
|
|
205
361
|
end
|
|
206
362
|
|
|
207
|
-
it 'should fail
|
|
363
|
+
it 'should fail when “max_age” sent on the authentication request and this claim added the “max_age” value doesn’t represent a date in the future' do
|
|
208
364
|
payload = {
|
|
209
365
|
iss: "https://#{domain}/",
|
|
210
|
-
|
|
366
|
+
sub: 'sub',
|
|
367
|
+
aud: client_id,
|
|
368
|
+
exp: future_timecode,
|
|
369
|
+
iat: past_timecode,
|
|
370
|
+
auth_time: past_timecode
|
|
211
371
|
}
|
|
372
|
+
|
|
212
373
|
token = make_hs256_token(payload)
|
|
213
374
|
expect do
|
|
214
|
-
jwt_validator.
|
|
215
|
-
end.to raise_error(
|
|
375
|
+
jwt_validator.verify(token, { max_age: 60 })
|
|
376
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
377
|
+
message: "Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (#{Time.now}) is after last auth time (#{Time.at(past_timecode + 60 + 60)})"
|
|
378
|
+
}))
|
|
216
379
|
end
|
|
217
380
|
|
|
218
|
-
it 'should
|
|
381
|
+
it 'should verify a valid HS256 token with multiple audiences' do
|
|
382
|
+
audience = [
|
|
383
|
+
client_id,
|
|
384
|
+
"https://#{domain}/userinfo"
|
|
385
|
+
]
|
|
219
386
|
payload = {
|
|
220
387
|
iss: "https://#{domain}/",
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
388
|
+
sub: 'sub',
|
|
389
|
+
aud: audience,
|
|
390
|
+
exp: future_timecode,
|
|
391
|
+
iat: past_timecode,
|
|
392
|
+
azp: client_id
|
|
225
393
|
}
|
|
226
394
|
token = make_hs256_token(payload)
|
|
227
|
-
|
|
395
|
+
id_token = jwt_validator.verify(token)
|
|
396
|
+
expect(id_token['aud']).to eq(audience)
|
|
228
397
|
end
|
|
229
398
|
|
|
230
|
-
it 'should
|
|
399
|
+
it 'should verify a standard HS256 token' do
|
|
231
400
|
sub = 'abc123'
|
|
232
401
|
payload = {
|
|
402
|
+
iss: "https://#{domain}/",
|
|
233
403
|
sub: sub,
|
|
404
|
+
aud: client_id,
|
|
234
405
|
exp: future_timecode,
|
|
235
|
-
|
|
236
|
-
iat: past_timecode,
|
|
237
|
-
aud: client_id
|
|
406
|
+
iat: past_timecode
|
|
238
407
|
}
|
|
239
408
|
token = make_hs256_token(payload)
|
|
240
|
-
|
|
241
|
-
expect(
|
|
409
|
+
verified_token = jwt_validator.verify(token)
|
|
410
|
+
expect(verified_token['sub']).to eq(sub)
|
|
242
411
|
end
|
|
243
412
|
|
|
244
|
-
it 'should
|
|
413
|
+
it 'should verify a standard RS256 token' do
|
|
245
414
|
domain = 'example.org'
|
|
246
415
|
sub = 'abc123'
|
|
247
416
|
payload = {
|
|
@@ -253,8 +422,8 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
253
422
|
kid: jwks_kid
|
|
254
423
|
}
|
|
255
424
|
token = make_rs256_token(payload)
|
|
256
|
-
|
|
257
|
-
expect(
|
|
425
|
+
verified_token = make_jwt_validator(opt_domain: domain).verify(token)
|
|
426
|
+
expect(verified_token['sub']).to eq(sub)
|
|
258
427
|
end
|
|
259
428
|
end
|
|
260
429
|
|
|
@@ -82,6 +82,8 @@ describe OmniAuth::Strategies::Auth0 do
|
|
|
82
82
|
expect(redirect_url).to have_query('client_id')
|
|
83
83
|
expect(redirect_url).to have_query('redirect_uri')
|
|
84
84
|
expect(redirect_url).not_to have_query('auth0Client')
|
|
85
|
+
expect(redirect_url).not_to have_query('connection')
|
|
86
|
+
expect(redirect_url).not_to have_query('prompt')
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
it 'redirects to hosted login page' do
|
|
@@ -95,6 +97,21 @@ describe OmniAuth::Strategies::Auth0 do
|
|
|
95
97
|
expect(redirect_url).to have_query('redirect_uri')
|
|
96
98
|
expect(redirect_url).to have_query('connection', 'abcd')
|
|
97
99
|
expect(redirect_url).not_to have_query('auth0Client')
|
|
100
|
+
expect(redirect_url).not_to have_query('prompt')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'redirects to hosted login page with prompt=login' do
|
|
104
|
+
get 'auth/auth0?prompt=login'
|
|
105
|
+
expect(last_response.status).to eq(302)
|
|
106
|
+
redirect_url = last_response.headers['Location']
|
|
107
|
+
expect(redirect_url).to start_with('https://samples.auth0.com/authorize')
|
|
108
|
+
expect(redirect_url).to have_query('response_type', 'code')
|
|
109
|
+
expect(redirect_url).to have_query('state')
|
|
110
|
+
expect(redirect_url).to have_query('client_id')
|
|
111
|
+
expect(redirect_url).to have_query('redirect_uri')
|
|
112
|
+
expect(redirect_url).to have_query('prompt', 'login')
|
|
113
|
+
expect(redirect_url).not_to have_query('auth0Client')
|
|
114
|
+
expect(redirect_url).not_to have_query('connection')
|
|
98
115
|
end
|
|
99
116
|
|
|
100
117
|
describe 'callback' do
|
|
@@ -300,7 +317,7 @@ RSpec::Matchers.define :have_query do |key, value|
|
|
|
300
317
|
uri = redirect_uri(actual)
|
|
301
318
|
query = query(uri)
|
|
302
319
|
if value.nil?
|
|
303
|
-
query
|
|
320
|
+
query.key?(key)
|
|
304
321
|
else
|
|
305
322
|
query[key] == [value]
|
|
306
323
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniauth-auth0
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Auth0
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-03-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: omniauth-oauth2
|
|
@@ -52,11 +52,14 @@ extra_rdoc_files: []
|
|
|
52
52
|
files:
|
|
53
53
|
- ".circleci/config.yml"
|
|
54
54
|
- ".gemrelease"
|
|
55
|
+
- ".github/CODEOWNERS"
|
|
55
56
|
- ".github/ISSUE_TEMPLATE.md"
|
|
56
57
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
|
58
|
+
- ".github/stale.yml"
|
|
57
59
|
- ".gitignore"
|
|
58
60
|
- ".rspec"
|
|
59
61
|
- ".rubocop.yml"
|
|
62
|
+
- ".snyk"
|
|
60
63
|
- CHANGELOG.md
|
|
61
64
|
- CODE_OF_CONDUCT.md
|
|
62
65
|
- CONTRIBUTING.md
|
|
@@ -71,6 +74,7 @@ files:
|
|
|
71
74
|
- examples/sinatra/config.ru
|
|
72
75
|
- lib/omniauth-auth0.rb
|
|
73
76
|
- lib/omniauth-auth0/version.rb
|
|
77
|
+
- lib/omniauth/auth0/errors.rb
|
|
74
78
|
- lib/omniauth/auth0/jwt_validator.rb
|
|
75
79
|
- lib/omniauth/auth0/telemetry.rb
|
|
76
80
|
- lib/omniauth/strategies/auth0.rb
|
|
@@ -99,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
99
103
|
- !ruby/object:Gem::Version
|
|
100
104
|
version: '0'
|
|
101
105
|
requirements: []
|
|
102
|
-
rubygems_version: 3.0.
|
|
106
|
+
rubygems_version: 3.0.1
|
|
103
107
|
signing_key:
|
|
104
108
|
specification_version: 4
|
|
105
109
|
summary: OmniAuth OAuth2 strategy for the Auth0 platform.
|