omniauth-auth0 2.3.1 → 2.4.1
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 -7
- data/CHANGELOG.md +21 -0
- data/Gemfile.lock +42 -43
- data/README.md +20 -5
- data/lib/omniauth-auth0/version.rb +1 -1
- data/lib/omniauth/auth0/jwt_validator.rb +26 -3
- data/lib/omniauth/strategies/auth0.rb +2 -2
- data/spec/omniauth/auth0/jwt_validator_spec.rb +210 -27
- data/spec/omniauth/strategies/auth0_spec.rb +27 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: baf9ee46a227506a7f43d571bbf9b6afd3639f8bf83cb32cc8ef8a55af5041ab
|
|
4
|
+
data.tar.gz: a7529eca35711ab1217e9946c4c5872a4a8d5296773bc49425f63a2792bf40f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b315bd912671314bcb0fd2f3bb19878ee1029fe3738c7887b732c3c346794011e23c26a39d0d77f9644846c302480469d00d1b307d6d72e6be61d9a6aa8b9e37
|
|
7
|
+
data.tar.gz: e29b464b82e4d4c3ef8870d06e1eee5f279afaf3330afa8a1167dbf4bfe0795ae4c966006c8bc6ebd993ac579ffe1cbd9cecb4aceb827a153af9e651020ea8cd
|
data/.circleci/config.yml
CHANGED
|
@@ -2,7 +2,7 @@ version: 2.1
|
|
|
2
2
|
jobs:
|
|
3
3
|
run-tests:
|
|
4
4
|
docker:
|
|
5
|
-
- image: circleci/ruby:2.
|
|
5
|
+
- image: circleci/ruby:2.5.7-buster
|
|
6
6
|
steps:
|
|
7
7
|
- checkout
|
|
8
8
|
- restore_cache:
|
|
@@ -10,12 +10,6 @@ jobs:
|
|
|
10
10
|
- gems-v2-{{ checksum "Gemfile.lock" }}
|
|
11
11
|
- gems-v2-
|
|
12
12
|
- run: bundle check || bundle install
|
|
13
|
-
- persist_to_workspace:
|
|
14
|
-
root: .
|
|
15
|
-
paths:
|
|
16
|
-
- Gemfile
|
|
17
|
-
- Gemfile.lock
|
|
18
|
-
- .snyk
|
|
19
13
|
- save_cache:
|
|
20
14
|
key: gems-v2--{{ checksum "Gemfile.lock" }}
|
|
21
15
|
paths:
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [v2.4.1](https://github.com/auth0/omniauth-auth0/tree/v2.4.1) (2020-10-08)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v2.4.0...v2.4.1)
|
|
6
|
+
|
|
7
|
+
**Fixed**
|
|
8
|
+
- Verify the JWT Signature [\#109](https://github.com/auth0/omniauth-auth0/pull/109) ([jimmyjames](https://github.com/jimmyjames))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [v2.4.0](https://github.com/auth0/omniauth-auth0/tree/v2.4.0) (2020-09-22)
|
|
12
|
+
|
|
13
|
+
[Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v2.3.1...v2.4.0)
|
|
14
|
+
|
|
15
|
+
**Security**
|
|
16
|
+
- Bump rack from 2.2.2 to 2.2.3 [\#107](https://github.com/auth0/omniauth-auth0/pull/107) ([dependabot](https://github.com/dependabot))
|
|
17
|
+
- Update dependencies [\#100](https://github.com/auth0/omniauth-auth0/pull/100) ([Albalmaceda](https://github.com/Albalmaceda))
|
|
18
|
+
|
|
19
|
+
**Added**
|
|
20
|
+
- Add support for screen_hint=signup param [\#103](https://github.com/auth0/omniauth-auth0/pull/103) ([bbean86](https://github.com/bbean86))
|
|
21
|
+
- Add support for `connection_scope` in params [\#99](https://github.com/auth0/omniauth-auth0/pull/99) ([felixclack](https://github.com/felixclack))
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
## [v2.3.1](https://github.com/auth0/omniauth-auth0/tree/v2.3.1) (2020-03-27)
|
|
4
25
|
|
|
5
26
|
[Full Changelog](https://github.com/auth0/omniauth-auth0/compare/v2.3.0...v2.3.1)
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
omniauth-auth0 (2.
|
|
4
|
+
omniauth-auth0 (2.4.1)
|
|
5
5
|
omniauth-oauth2 (~> 1.5)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -9,22 +9,20 @@ GEM
|
|
|
9
9
|
specs:
|
|
10
10
|
addressable (2.7.0)
|
|
11
11
|
public_suffix (>= 2.0.2, < 5.0)
|
|
12
|
-
ast (2.4.
|
|
13
|
-
codecov (0.
|
|
12
|
+
ast (2.4.1)
|
|
13
|
+
codecov (0.2.11)
|
|
14
14
|
json
|
|
15
15
|
simplecov
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
crack (0.4.3)
|
|
19
|
-
safe_yaml (~> 1.0.0)
|
|
16
|
+
coderay (1.1.3)
|
|
17
|
+
crack (0.4.4)
|
|
20
18
|
daemons (1.3.1)
|
|
21
|
-
diff-lcs (1.
|
|
19
|
+
diff-lcs (1.4.4)
|
|
22
20
|
docile (1.3.2)
|
|
23
|
-
dotenv (2.7.
|
|
21
|
+
dotenv (2.7.6)
|
|
24
22
|
eventmachine (1.2.7)
|
|
25
|
-
faraday (1.0.
|
|
23
|
+
faraday (1.0.1)
|
|
26
24
|
multipart-post (>= 1.2, < 3)
|
|
27
|
-
ffi (1.
|
|
25
|
+
ffi (1.13.1)
|
|
28
26
|
formatador (0.2.5)
|
|
29
27
|
gem-release (2.1.1)
|
|
30
28
|
guard (2.16.2)
|
|
@@ -43,16 +41,15 @@ GEM
|
|
|
43
41
|
rspec (>= 2.99.0, < 4.0)
|
|
44
42
|
hashdiff (1.0.1)
|
|
45
43
|
hashie (4.1.0)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
jwt (2.2.1)
|
|
44
|
+
json (2.3.1)
|
|
45
|
+
jwt (2.2.2)
|
|
49
46
|
listen (3.1.5)
|
|
50
47
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
|
51
48
|
rb-inotify (~> 0.9, >= 0.9.7)
|
|
52
49
|
ruby_dep (~> 1.2)
|
|
53
|
-
lumberjack (1.2.
|
|
50
|
+
lumberjack (1.2.8)
|
|
54
51
|
method_source (1.0.0)
|
|
55
|
-
multi_json (1.
|
|
52
|
+
multi_json (1.15.0)
|
|
56
53
|
multi_xml (0.6.0)
|
|
57
54
|
multipart-post (2.1.1)
|
|
58
55
|
mustermann (1.1.1)
|
|
@@ -70,63 +67,66 @@ GEM
|
|
|
70
67
|
omniauth (1.9.1)
|
|
71
68
|
hashie (>= 3.4.6)
|
|
72
69
|
rack (>= 1.6.2, < 3)
|
|
73
|
-
omniauth-oauth2 (1.
|
|
74
|
-
oauth2 (~> 1.
|
|
70
|
+
omniauth-oauth2 (1.7.0)
|
|
71
|
+
oauth2 (~> 1.4)
|
|
75
72
|
omniauth (~> 1.9)
|
|
76
|
-
parallel (1.19.
|
|
77
|
-
parser (2.7.0
|
|
78
|
-
ast (~> 2.4.
|
|
79
|
-
pry (0.13.
|
|
73
|
+
parallel (1.19.2)
|
|
74
|
+
parser (2.7.2.0)
|
|
75
|
+
ast (~> 2.4.1)
|
|
76
|
+
pry (0.13.1)
|
|
80
77
|
coderay (~> 1.1)
|
|
81
78
|
method_source (~> 1.0)
|
|
82
|
-
public_suffix (4.0.
|
|
83
|
-
rack (2.2.
|
|
84
|
-
rack-protection (2.0
|
|
79
|
+
public_suffix (4.0.6)
|
|
80
|
+
rack (2.2.3)
|
|
81
|
+
rack-protection (2.1.0)
|
|
85
82
|
rack
|
|
86
83
|
rack-test (1.1.0)
|
|
87
84
|
rack (>= 1.0, < 3)
|
|
88
85
|
rainbow (3.0.0)
|
|
89
86
|
rake (13.0.1)
|
|
90
|
-
rb-fsevent (0.10.
|
|
87
|
+
rb-fsevent (0.10.4)
|
|
91
88
|
rb-inotify (0.10.1)
|
|
92
89
|
ffi (~> 1.0)
|
|
90
|
+
regexp_parser (1.8.1)
|
|
93
91
|
rexml (3.2.4)
|
|
94
92
|
rspec (3.9.0)
|
|
95
93
|
rspec-core (~> 3.9.0)
|
|
96
94
|
rspec-expectations (~> 3.9.0)
|
|
97
95
|
rspec-mocks (~> 3.9.0)
|
|
98
|
-
rspec-core (3.9.
|
|
99
|
-
rspec-support (~> 3.9.
|
|
100
|
-
rspec-expectations (3.9.
|
|
96
|
+
rspec-core (3.9.3)
|
|
97
|
+
rspec-support (~> 3.9.3)
|
|
98
|
+
rspec-expectations (3.9.2)
|
|
101
99
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
102
100
|
rspec-support (~> 3.9.0)
|
|
103
101
|
rspec-mocks (3.9.1)
|
|
104
102
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
105
103
|
rspec-support (~> 3.9.0)
|
|
106
|
-
rspec-support (3.9.
|
|
107
|
-
rubocop (0.
|
|
108
|
-
jaro_winkler (~> 1.5.1)
|
|
104
|
+
rspec-support (3.9.3)
|
|
105
|
+
rubocop (0.93.0)
|
|
109
106
|
parallel (~> 1.10)
|
|
110
|
-
parser (>= 2.7.
|
|
107
|
+
parser (>= 2.7.1.5)
|
|
111
108
|
rainbow (>= 2.2.2, < 4.0)
|
|
109
|
+
regexp_parser (>= 1.8)
|
|
112
110
|
rexml
|
|
111
|
+
rubocop-ast (>= 0.6.0)
|
|
113
112
|
ruby-progressbar (~> 1.7)
|
|
114
|
-
unicode-display_width (>= 1.4.0, <
|
|
113
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
|
114
|
+
rubocop-ast (0.7.1)
|
|
115
|
+
parser (>= 2.7.1.5)
|
|
115
116
|
ruby-progressbar (1.10.1)
|
|
116
117
|
ruby2_keywords (0.0.2)
|
|
117
118
|
ruby_dep (1.5.0)
|
|
118
|
-
safe_yaml (1.0.5)
|
|
119
119
|
shellany (0.0.1)
|
|
120
120
|
shotgun (0.9.2)
|
|
121
121
|
rack (>= 1.0)
|
|
122
|
-
simplecov (0.
|
|
122
|
+
simplecov (0.19.0)
|
|
123
123
|
docile (~> 1.1)
|
|
124
124
|
simplecov-html (~> 0.11)
|
|
125
|
-
simplecov-html (0.12.
|
|
126
|
-
sinatra (2.0
|
|
125
|
+
simplecov-html (0.12.3)
|
|
126
|
+
sinatra (2.1.0)
|
|
127
127
|
mustermann (~> 1.0)
|
|
128
|
-
rack (~> 2.
|
|
129
|
-
rack-protection (= 2.0
|
|
128
|
+
rack (~> 2.2)
|
|
129
|
+
rack-protection (= 2.1.0)
|
|
130
130
|
tilt (~> 2.0)
|
|
131
131
|
thin (1.7.2)
|
|
132
132
|
daemons (~> 1.0, >= 1.0.9)
|
|
@@ -134,9 +134,8 @@ GEM
|
|
|
134
134
|
rack (>= 1, < 3)
|
|
135
135
|
thor (1.0.1)
|
|
136
136
|
tilt (2.0.10)
|
|
137
|
-
unicode-display_width (1.
|
|
138
|
-
|
|
139
|
-
webmock (3.8.3)
|
|
137
|
+
unicode-display_width (1.7.0)
|
|
138
|
+
webmock (3.9.1)
|
|
140
139
|
addressable (>= 2.3.6)
|
|
141
140
|
crack (>= 0.3.2)
|
|
142
141
|
hashdiff (>= 0.4.0, < 2.0.0)
|
data/README.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# OmniAuth Auth0
|
|
2
2
|
|
|
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.
|
|
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:**
|
|
5
|
+
> :warning: **Important security note:** This solution uses a 3rd party library with an unresolved [security issue(s)](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-9284). Please review the details of the vulnerability, including [Auth0](https://github.com/auth0/omniauth-auth0/issues/82 ) and other recommended [mitigations](https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284), before implementing the solution.
|
|
6
6
|
|
|
7
7
|
[](https://circleci.com/gh/auth0/omniauth-auth0)
|
|
8
8
|
[](https://codecov.io/gh/auth0/omniauth-auth0)
|
|
9
9
|
[](https://badge.fury.io/rb/omniauth-auth0)
|
|
10
10
|
[](https://github.com/auth0/omniauth-auth0/blob/master/LICENSE)
|
|
11
|
+
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fauth0%2Fomniauth-auth0?ref=badge_shield)
|
|
11
12
|
|
|
12
13
|
## Table of Contents
|
|
13
14
|
|
|
@@ -45,7 +46,7 @@ Then install:
|
|
|
45
46
|
$ bundle install
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
See our [contributing guide](CONTRIBUTING.md) for information on local installation for development.
|
|
49
|
+
See our [contributing guide](CONTRIBUTING.md) for information on local installation for development.
|
|
49
50
|
|
|
50
51
|
## Getting Started
|
|
51
52
|
|
|
@@ -63,7 +64,7 @@ All of these tasks and more are covered in our [Ruby on Rails Quickstart](https:
|
|
|
63
64
|
To send additional parameters during login, you can specify them when you register the provider:
|
|
64
65
|
|
|
65
66
|
```ruby
|
|
66
|
-
provider
|
|
67
|
+
provider
|
|
67
68
|
:auth0,
|
|
68
69
|
ENV['AUTH0_CLIENT_ID'],
|
|
69
70
|
ENV['AUTH0_CLIENT_SECRET'],
|
|
@@ -121,6 +122,17 @@ The Auth0 strategy will provide the standard OmniAuth hash attributes:
|
|
|
121
122
|
}
|
|
122
123
|
```
|
|
123
124
|
|
|
125
|
+
### Query Parameter Options
|
|
126
|
+
|
|
127
|
+
In some scenarios, you may need to pass specific query parameters to `/authorize`. The following parameters are available to enable this:
|
|
128
|
+
|
|
129
|
+
- `connection`
|
|
130
|
+
- `connection_scope`
|
|
131
|
+
- `prompt`
|
|
132
|
+
- `screen_hint` (only relevant to New Universal Login Experience)
|
|
133
|
+
|
|
134
|
+
Simply pass these query parameters to your OmniAuth redirect endpoint to enable their behavior.
|
|
135
|
+
|
|
124
136
|
## Contribution
|
|
125
137
|
|
|
126
138
|
We appreciate feedback and contribution to this repo! Before you get started, please see the following:
|
|
@@ -133,7 +145,7 @@ We appreciate feedback and contribution to this repo! Before you get started, pl
|
|
|
133
145
|
|
|
134
146
|
- Use [Community](https://community.auth0.com/) for usage, questions, specific cases.
|
|
135
147
|
- Use [Issues](https://github.com/auth0/omniauth-auth0/issues) here for code-level support and bug reports.
|
|
136
|
-
- Paid customers can use [Support](https://support.auth0.com/) to submit a trouble ticket for production-affecting issues.
|
|
148
|
+
- Paid customers can use [Support](https://support.auth0.com/) to submit a trouble ticket for production-affecting issues.
|
|
137
149
|
|
|
138
150
|
## Vulnerability Reporting
|
|
139
151
|
|
|
@@ -155,3 +167,6 @@ Auth0 helps you to easily:
|
|
|
155
167
|
## License
|
|
156
168
|
|
|
157
169
|
The OmniAuth Auth0 strategy is licensed under MIT - [LICENSE](LICENSE)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fauth0%2Fomniauth-auth0?ref=badge_large)
|
|
@@ -28,17 +28,24 @@ module OmniAuth
|
|
|
28
28
|
@client_secret = options.client_secret
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# Verify a token's signature. Only tokens signed with the RS256 or HS256 signatures are supported.
|
|
32
|
+
# @return array - The token's key and signing algorithm
|
|
31
33
|
def verify_signature(jwt)
|
|
32
34
|
head = token_head(jwt)
|
|
33
35
|
|
|
34
36
|
# Make sure the algorithm is supported and get the decode key.
|
|
35
37
|
if head[:alg] == 'RS256'
|
|
36
|
-
[rs256_decode_key(head[:kid]), head[:alg]]
|
|
38
|
+
key, alg = [rs256_decode_key(head[:kid]), head[:alg]]
|
|
37
39
|
elsif head[:alg] == 'HS256'
|
|
38
|
-
[@client_secret, head[:alg]]
|
|
40
|
+
key, alg = [@client_secret, head[:alg]]
|
|
39
41
|
else
|
|
40
42
|
raise OmniAuth::Auth0::TokenValidationError.new("Signature algorithm of #{head[:alg]} is not supported. Expected the ID token to be signed with RS256 or HS256")
|
|
41
43
|
end
|
|
44
|
+
|
|
45
|
+
# Call decode to verify the signature
|
|
46
|
+
JWT.decode(jwt, key, true, decode_opts(alg))
|
|
47
|
+
|
|
48
|
+
return key, alg
|
|
42
49
|
end
|
|
43
50
|
|
|
44
51
|
# Verify a JWT.
|
|
@@ -93,11 +100,27 @@ module OmniAuth
|
|
|
93
100
|
end
|
|
94
101
|
|
|
95
102
|
private
|
|
103
|
+
# Get the JWT decode options. We disable the claim checks since we perform our claim validation logic
|
|
104
|
+
# Docs: https://github.com/jwt/ruby-jwt
|
|
105
|
+
# @return hash
|
|
106
|
+
def decode_opts(alg)
|
|
107
|
+
{
|
|
108
|
+
algorithm: alg,
|
|
109
|
+
verify_expiration: false,
|
|
110
|
+
verify_iat: false,
|
|
111
|
+
verify_iss: false,
|
|
112
|
+
verify_aud: false,
|
|
113
|
+
verify_jti: false,
|
|
114
|
+
verify_subj: false,
|
|
115
|
+
verify_not_before: false
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
96
119
|
def rs256_decode_key(kid)
|
|
97
120
|
jwks_x5c = jwks_key(:x5c, kid)
|
|
98
121
|
|
|
99
122
|
if jwks_x5c.nil?
|
|
100
|
-
raise OmniAuth::Auth0::TokenValidationError.new("Could not find a public key for Key ID (kid) '#{kid}'
|
|
123
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Could not find a public key for Key ID (kid) '#{kid}'")
|
|
101
124
|
end
|
|
102
125
|
|
|
103
126
|
jwks_public_cert(jwks_x5c.first)
|
|
@@ -86,7 +86,7 @@ module OmniAuth
|
|
|
86
86
|
def authorize_params
|
|
87
87
|
params = super
|
|
88
88
|
parsed_query = Rack::Utils.parse_query(request.query_string)
|
|
89
|
-
%w[connection prompt].each do |key|
|
|
89
|
+
%w[connection connection_scope prompt screen_hint].each do |key|
|
|
90
90
|
params[key] = parsed_query[key] if parsed_query.key?(key)
|
|
91
91
|
end
|
|
92
92
|
|
|
@@ -94,7 +94,7 @@ module OmniAuth
|
|
|
94
94
|
params[:nonce] = SecureRandom.hex
|
|
95
95
|
# Generate leeway if none exists
|
|
96
96
|
params[:leeway] = 60 unless params[:leeway]
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
# Store authorize params in the session for token verification
|
|
99
99
|
session['authorize_params'] = params
|
|
100
100
|
|
|
@@ -12,17 +12,17 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
12
12
|
let(:domain) { 'samples.auth0.com' }
|
|
13
13
|
let(:future_timecode) { 32_503_680_000 }
|
|
14
14
|
let(:past_timecode) { 303_912_000 }
|
|
15
|
-
let(:
|
|
15
|
+
let(:valid_jwks_kid) { 'NkJCQzIyQzRBMEU4NjhGNUU4MzU4RkY0M0ZDQzkwOUQ0Q0VGNUMwQg' }
|
|
16
16
|
|
|
17
17
|
let(:rsa_private_key) do
|
|
18
18
|
OpenSSL::PKey::RSA.generate 2048
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
let(:
|
|
21
|
+
let(:valid_jwks) do
|
|
22
22
|
{
|
|
23
23
|
keys: [
|
|
24
24
|
{
|
|
25
|
-
kid:
|
|
25
|
+
kid: valid_jwks_kid,
|
|
26
26
|
x5c: [Base64.encode64(make_cert(rsa_private_key).to_der)]
|
|
27
27
|
}
|
|
28
28
|
]
|
|
@@ -91,29 +91,29 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
describe 'JWT verifier
|
|
94
|
+
describe 'JWT verifier jwks key parsing' do
|
|
95
95
|
let(:jwt_validator) do
|
|
96
96
|
make_jwt_validator
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
before do
|
|
100
|
-
|
|
100
|
+
stub_complete_jwks
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
it 'should return a key' do
|
|
104
|
-
expect(jwt_validator.jwks_key(:alg,
|
|
104
|
+
expect(jwt_validator.jwks_key(:alg, valid_jwks_kid)).to eq('RS256')
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
it 'should return an x5c key' do
|
|
108
|
-
expect(jwt_validator.jwks_key(:x5c,
|
|
108
|
+
expect(jwt_validator.jwks_key(:x5c, valid_jwks_kid).length).to eq(1)
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
it 'should return nil if there is not key' do
|
|
112
|
-
expect(jwt_validator.jwks_key(:auth0,
|
|
112
|
+
expect(jwt_validator.jwks_key(:auth0, valid_jwks_kid)).to eq(nil)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
it 'should return nil if the key ID is invalid' do
|
|
116
|
-
expect(jwt_validator.jwks_key(:alg, "#{
|
|
116
|
+
expect(jwt_validator.jwks_key(:alg, "#{valid_jwks_kid}_invalid")).to eq(nil)
|
|
117
117
|
end
|
|
118
118
|
end
|
|
119
119
|
|
|
@@ -153,8 +153,24 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
before do
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
stub_complete_jwks
|
|
157
|
+
stub_expected_jwks
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'should fail when JWT is nil' do
|
|
161
|
+
expect do
|
|
162
|
+
jwt_validator.verify(nil)
|
|
163
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
164
|
+
message: "ID token is required but missing"
|
|
165
|
+
}))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'should fail when JWT is not well-formed' do
|
|
169
|
+
expect do
|
|
170
|
+
jwt_validator.verify('abc.123')
|
|
171
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
172
|
+
message: "ID token could not be decoded"
|
|
173
|
+
}))
|
|
158
174
|
end
|
|
159
175
|
|
|
160
176
|
it 'should fail with missing issuer' do
|
|
@@ -248,6 +264,39 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
248
264
|
}))
|
|
249
265
|
end
|
|
250
266
|
|
|
267
|
+
it 'should pass when past expiration but within default leeway' do
|
|
268
|
+
exp = Time.now.to_i - 59
|
|
269
|
+
payload = {
|
|
270
|
+
iss: "https://#{domain}/",
|
|
271
|
+
sub: 'sub',
|
|
272
|
+
aud: client_id,
|
|
273
|
+
exp: exp,
|
|
274
|
+
iat: past_timecode
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
token = make_hs256_token(payload)
|
|
278
|
+
id_token = jwt_validator.verify(token)
|
|
279
|
+
expect(id_token['exp']).to eq(exp)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it 'should fail when past expiration and outside default leeway' do
|
|
283
|
+
exp = Time.now.to_i - 61
|
|
284
|
+
payload = {
|
|
285
|
+
iss: "https://#{domain}/",
|
|
286
|
+
sub: 'sub',
|
|
287
|
+
aud: client_id,
|
|
288
|
+
exp: exp,
|
|
289
|
+
iat: past_timecode
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
token = make_hs256_token(payload)
|
|
293
|
+
expect do
|
|
294
|
+
jwt_validator.verify(token)
|
|
295
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
296
|
+
message: "Expiration time (exp) claim error in the ID token; current time (#{Time.now}) is after expiration time (#{Time.at(exp + 60)})"
|
|
297
|
+
}))
|
|
298
|
+
end
|
|
299
|
+
|
|
251
300
|
it 'should fail when missing iat' do
|
|
252
301
|
payload = {
|
|
253
302
|
iss: "https://#{domain}/",
|
|
@@ -377,6 +426,114 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
377
426
|
}))
|
|
378
427
|
end
|
|
379
428
|
|
|
429
|
+
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, outside the default leeway' do
|
|
430
|
+
now = Time.now.to_i
|
|
431
|
+
auth_time = now - 121
|
|
432
|
+
max_age = 60
|
|
433
|
+
payload = {
|
|
434
|
+
iss: "https://#{domain}/",
|
|
435
|
+
sub: 'sub',
|
|
436
|
+
aud: client_id,
|
|
437
|
+
exp: future_timecode,
|
|
438
|
+
iat: past_timecode,
|
|
439
|
+
auth_time: auth_time
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
token = make_hs256_token(payload)
|
|
443
|
+
expect do
|
|
444
|
+
jwt_validator.verify(token, { max_age: max_age })
|
|
445
|
+
# Time.at(auth_time + max_age + leeway
|
|
446
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
447
|
+
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(auth_time + max_age + 60)})"
|
|
448
|
+
}))
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
it 'should verify when “max_age” sent on the authentication request and this claim added the “max_age” value doesn’t represent a date in the future, outside the default leeway' do
|
|
452
|
+
now = Time.now.to_i
|
|
453
|
+
auth_time = now - 119
|
|
454
|
+
max_age = 60
|
|
455
|
+
payload = {
|
|
456
|
+
iss: "https://#{domain}/",
|
|
457
|
+
sub: 'sub',
|
|
458
|
+
aud: client_id,
|
|
459
|
+
exp: future_timecode,
|
|
460
|
+
iat: past_timecode,
|
|
461
|
+
auth_time: auth_time
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
token = make_hs256_token(payload)
|
|
465
|
+
id_token = jwt_validator.verify(token, { max_age: max_age })
|
|
466
|
+
expect(id_token['auth_time']).to eq(auth_time)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
it 'should fail for RS256 token when kid is incorrect' do
|
|
470
|
+
domain = 'example.org'
|
|
471
|
+
sub = 'abc123'
|
|
472
|
+
payload = {
|
|
473
|
+
sub: sub,
|
|
474
|
+
exp: future_timecode,
|
|
475
|
+
iss: "https://#{domain}/",
|
|
476
|
+
iat: past_timecode,
|
|
477
|
+
aud: client_id
|
|
478
|
+
}
|
|
479
|
+
invalid_kid = 'invalid-kid'
|
|
480
|
+
token = make_rs256_token(payload, invalid_kid)
|
|
481
|
+
expect do
|
|
482
|
+
verified_token = make_jwt_validator(opt_domain: domain).verify(token)
|
|
483
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
484
|
+
message: "Could not find a public key for Key ID (kid) 'invalid-kid'"
|
|
485
|
+
}))
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
it 'should fail when RS256 token has invalid signature' do
|
|
489
|
+
domain = 'example.org'
|
|
490
|
+
sub = 'abc123'
|
|
491
|
+
payload = {
|
|
492
|
+
sub: sub,
|
|
493
|
+
exp: future_timecode,
|
|
494
|
+
iss: "https://#{domain}/",
|
|
495
|
+
iat: past_timecode,
|
|
496
|
+
aud: client_id
|
|
497
|
+
}
|
|
498
|
+
token = make_rs256_token(payload) + 'bad'
|
|
499
|
+
expect do
|
|
500
|
+
verified_token = make_jwt_validator(opt_domain: domain).verify(token)
|
|
501
|
+
end.to raise_error(an_instance_of(JWT::VerificationError).and having_attributes({
|
|
502
|
+
message: "Signature verification raised"
|
|
503
|
+
}))
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
it 'should fail when algorithm is not RS256 or HS256' do
|
|
507
|
+
payload = {
|
|
508
|
+
iss: "https://#{domain}/",
|
|
509
|
+
sub: 'abc123',
|
|
510
|
+
aud: client_id,
|
|
511
|
+
exp: future_timecode,
|
|
512
|
+
iat: past_timecode
|
|
513
|
+
}
|
|
514
|
+
token = JWT.encode payload, 'secret', 'HS384'
|
|
515
|
+
expect do
|
|
516
|
+
jwt_validator.verify(token)
|
|
517
|
+
end.to raise_error(an_instance_of(OmniAuth::Auth0::TokenValidationError).and having_attributes({
|
|
518
|
+
message: "Signature algorithm of HS384 is not supported. Expected the ID token to be signed with RS256 or HS256"
|
|
519
|
+
}))
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
it 'should fail when HS256 token has invalid signature' do
|
|
523
|
+
payload = {
|
|
524
|
+
iss: "https://#{domain}/",
|
|
525
|
+
sub: 'abc123',
|
|
526
|
+
aud: client_id,
|
|
527
|
+
exp: future_timecode,
|
|
528
|
+
iat: past_timecode
|
|
529
|
+
}
|
|
530
|
+
token = make_hs256_token(payload, 'bad_secret')
|
|
531
|
+
expect do
|
|
532
|
+
# validator is configured to use "CLIENT_SECRET" by default
|
|
533
|
+
jwt_validator.verify(token)
|
|
534
|
+
end.to raise_error(an_instance_of(JWT::VerificationError))
|
|
535
|
+
end
|
|
536
|
+
|
|
380
537
|
it 'should verify a valid HS256 token with multiple audiences' do
|
|
381
538
|
audience = [
|
|
382
539
|
client_id,
|
|
@@ -417,13 +574,44 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
417
574
|
exp: future_timecode,
|
|
418
575
|
iss: "https://#{domain}/",
|
|
419
576
|
iat: past_timecode,
|
|
420
|
-
aud: client_id
|
|
421
|
-
kid: jwks_kid
|
|
577
|
+
aud: client_id
|
|
422
578
|
}
|
|
423
579
|
token = make_rs256_token(payload)
|
|
424
580
|
verified_token = make_jwt_validator(opt_domain: domain).verify(token)
|
|
425
581
|
expect(verified_token['sub']).to eq(sub)
|
|
426
582
|
end
|
|
583
|
+
|
|
584
|
+
it 'should verify a HS256 JWT signature when calling verify signature directly' do
|
|
585
|
+
sub = 'abc123'
|
|
586
|
+
payload = {
|
|
587
|
+
iss: "https://#{domain}/",
|
|
588
|
+
sub: sub,
|
|
589
|
+
aud: client_id,
|
|
590
|
+
exp: future_timecode,
|
|
591
|
+
iat: past_timecode
|
|
592
|
+
}
|
|
593
|
+
token = make_hs256_token(payload)
|
|
594
|
+
verified_token_signature = jwt_validator.verify_signature(token)
|
|
595
|
+
expect(verified_token_signature[0]).to eq('CLIENT_SECRET')
|
|
596
|
+
expect(verified_token_signature[1]).to eq('HS256')
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
it 'should verify a RS256 JWT signature verify signature directly' do
|
|
600
|
+
domain = 'example.org'
|
|
601
|
+
sub = 'abc123'
|
|
602
|
+
payload = {
|
|
603
|
+
sub: sub,
|
|
604
|
+
exp: future_timecode,
|
|
605
|
+
iss: "https://#{domain}/",
|
|
606
|
+
iat: past_timecode,
|
|
607
|
+
aud: client_id
|
|
608
|
+
}
|
|
609
|
+
token = make_rs256_token(payload)
|
|
610
|
+
verified_token_signature = make_jwt_validator(opt_domain: domain).verify_signature(token)
|
|
611
|
+
expect(verified_token_signature.length).to be(2)
|
|
612
|
+
expect(verified_token_signature[0]).to be_a(OpenSSL::PKey::RSA)
|
|
613
|
+
expect(verified_token_signature[1]).to eq('RS256')
|
|
614
|
+
end
|
|
427
615
|
end
|
|
428
616
|
|
|
429
617
|
private
|
|
@@ -439,14 +627,16 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
439
627
|
OmniAuth::Auth0::JWTValidator.new(opts)
|
|
440
628
|
end
|
|
441
629
|
|
|
442
|
-
def make_hs256_token(payload = nil)
|
|
630
|
+
def make_hs256_token(payload = nil, secret = nil)
|
|
443
631
|
payload = { sub: 'abc123' } if payload.nil?
|
|
444
|
-
|
|
632
|
+
secret = client_secret if secret.nil?
|
|
633
|
+
JWT.encode payload, secret, 'HS256'
|
|
445
634
|
end
|
|
446
635
|
|
|
447
|
-
def make_rs256_token(payload = nil)
|
|
636
|
+
def make_rs256_token(payload = nil, kid = nil)
|
|
448
637
|
payload = { sub: 'abc123' } if payload.nil?
|
|
449
|
-
|
|
638
|
+
kid = valid_jwks_kid if kid.nil?
|
|
639
|
+
JWT.encode payload, rsa_private_key, 'RS256', kid: kid
|
|
450
640
|
end
|
|
451
641
|
|
|
452
642
|
def make_cert(private_key)
|
|
@@ -474,7 +664,7 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
474
664
|
cert.sign private_key, OpenSSL::Digest::SHA1.new
|
|
475
665
|
end
|
|
476
666
|
|
|
477
|
-
def
|
|
667
|
+
def stub_complete_jwks
|
|
478
668
|
stub_request(:get, 'https://samples.auth0.com/.well-known/jwks.json')
|
|
479
669
|
.to_return(
|
|
480
670
|
headers: { 'Content-Type' => 'application/json' },
|
|
@@ -483,18 +673,11 @@ describe OmniAuth::Auth0::JWTValidator do
|
|
|
483
673
|
)
|
|
484
674
|
end
|
|
485
675
|
|
|
486
|
-
def
|
|
487
|
-
stub_request(:get, 'https://samples.auth0.com/.well-known/jwks-bad.json')
|
|
488
|
-
.to_return(
|
|
489
|
-
status: 404
|
|
490
|
-
)
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
def stub_dummy_jwks
|
|
676
|
+
def stub_expected_jwks
|
|
494
677
|
stub_request(:get, 'https://example.org/.well-known/jwks.json')
|
|
495
678
|
.to_return(
|
|
496
679
|
headers: { 'Content-Type' => 'application/json' },
|
|
497
|
-
body:
|
|
680
|
+
body: valid_jwks,
|
|
498
681
|
status: 200
|
|
499
682
|
)
|
|
500
683
|
end
|
|
@@ -83,7 +83,9 @@ describe OmniAuth::Strategies::Auth0 do
|
|
|
83
83
|
expect(redirect_url).to have_query('redirect_uri')
|
|
84
84
|
expect(redirect_url).not_to have_query('auth0Client')
|
|
85
85
|
expect(redirect_url).not_to have_query('connection')
|
|
86
|
+
expect(redirect_url).not_to have_query('connection_scope')
|
|
86
87
|
expect(redirect_url).not_to have_query('prompt')
|
|
88
|
+
expect(redirect_url).not_to have_query('screen_hint')
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
it 'redirects to hosted login page' do
|
|
@@ -97,7 +99,18 @@ describe OmniAuth::Strategies::Auth0 do
|
|
|
97
99
|
expect(redirect_url).to have_query('redirect_uri')
|
|
98
100
|
expect(redirect_url).to have_query('connection', 'abcd')
|
|
99
101
|
expect(redirect_url).not_to have_query('auth0Client')
|
|
102
|
+
expect(redirect_url).not_to have_query('connection_scope')
|
|
100
103
|
expect(redirect_url).not_to have_query('prompt')
|
|
104
|
+
expect(redirect_url).not_to have_query('screen_hint')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'redirects to the hosted login page with connection_scope' do
|
|
108
|
+
get 'auth/auth0?connection_scope=identity_provider_scope'
|
|
109
|
+
expect(last_response.status).to eq(302)
|
|
110
|
+
redirect_url = last_response.headers['Location']
|
|
111
|
+
expect(redirect_url).to start_with('https://samples.auth0.com/authorize')
|
|
112
|
+
expect(redirect_url)
|
|
113
|
+
.to have_query('connection_scope', 'identity_provider_scope')
|
|
101
114
|
end
|
|
102
115
|
|
|
103
116
|
it 'redirects to hosted login page with prompt=login' do
|
|
@@ -114,6 +127,20 @@ describe OmniAuth::Strategies::Auth0 do
|
|
|
114
127
|
expect(redirect_url).not_to have_query('connection')
|
|
115
128
|
end
|
|
116
129
|
|
|
130
|
+
it 'redirects to hosted login page with screen_hint=signup' do
|
|
131
|
+
get 'auth/auth0?screen_hint=signup'
|
|
132
|
+
expect(last_response.status).to eq(302)
|
|
133
|
+
redirect_url = last_response.headers['Location']
|
|
134
|
+
expect(redirect_url).to start_with('https://samples.auth0.com/authorize')
|
|
135
|
+
expect(redirect_url).to have_query('response_type', 'code')
|
|
136
|
+
expect(redirect_url).to have_query('state')
|
|
137
|
+
expect(redirect_url).to have_query('client_id')
|
|
138
|
+
expect(redirect_url).to have_query('redirect_uri')
|
|
139
|
+
expect(redirect_url).to have_query('screen_hint', 'signup')
|
|
140
|
+
expect(redirect_url).not_to have_query('auth0Client')
|
|
141
|
+
expect(redirect_url).not_to have_query('connection')
|
|
142
|
+
end
|
|
143
|
+
|
|
117
144
|
describe 'callback' do
|
|
118
145
|
let(:access_token) { 'access token' }
|
|
119
146
|
let(:expires_in) { 2000 }
|
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.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Auth0
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-10-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: omniauth-oauth2
|
|
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
103
103
|
- !ruby/object:Gem::Version
|
|
104
104
|
version: '0'
|
|
105
105
|
requirements: []
|
|
106
|
-
rubygems_version: 3.
|
|
106
|
+
rubygems_version: 3.1.2
|
|
107
107
|
signing_key:
|
|
108
108
|
specification_version: 4
|
|
109
109
|
summary: OmniAuth OAuth2 strategy for the Auth0 platform.
|