omniauth-auth0 2.0.0 → 2.4.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.
Potentially problematic release.
This version of omniauth-auth0 might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.circleci/config.yml +22 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/ISSUE_TEMPLATE.md +39 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +32 -0
- data/.github/stale.yml +20 -0
- data/.gitignore +5 -2
- data/.snyk +9 -0
- data/CHANGELOG.md +91 -1
- data/CODE_OF_CONDUCT.md +3 -0
- data/CONTRIBUTING.md +71 -0
- data/Gemfile +4 -4
- data/Gemfile.lock +167 -0
- data/README.md +114 -85
- data/Rakefile +2 -2
- data/codecov.yml +22 -0
- data/lib/omniauth-auth0.rb +1 -1
- data/lib/omniauth-auth0/version.rb +1 -1
- data/lib/omniauth/auth0/errors.rb +11 -0
- data/lib/omniauth/auth0/jwt_validator.rb +228 -0
- data/lib/omniauth/auth0/telemetry.rb +36 -0
- data/lib/omniauth/strategies/auth0.rb +77 -19
- data/omniauth-auth0.gemspec +3 -5
- data/spec/omniauth/auth0/jwt_validator_spec.rb +501 -0
- data/spec/omniauth/auth0/telemetry_spec.rb +28 -0
- data/spec/omniauth/strategies/auth0_spec.rb +73 -2
- data/spec/resources/jwks.json +28 -0
- data/spec/spec_helper.rb +8 -6
- metadata +29 -12
- data/.travis.yml +0 -6
data/README.md
CHANGED
@@ -1,57 +1,70 @@
|
|
1
|
-
[](https://travis-ci.org/auth0/omniauth-auth0)
|
2
|
-
|
3
1
|
# OmniAuth Auth0
|
4
2
|
|
5
|
-
|
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.
|
6
4
|
|
7
|
-
|
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.
|
8
6
|
|
9
|
-
|
7
|
+
[](https://circleci.com/gh/auth0/omniauth-auth0)
|
8
|
+
[](https://codecov.io/gh/auth0/omniauth-auth0)
|
9
|
+
[](https://badge.fury.io/rb/omniauth-auth0)
|
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)
|
10
12
|
|
11
|
-
|
12
|
-
gem 'omniauth-auth0'
|
13
|
-
```
|
13
|
+
## Table of Contents
|
14
14
|
|
15
|
-
|
15
|
+
- [Documentation](#documentation)
|
16
|
+
- [Installation](#installation)
|
17
|
+
- [Getting Started](#getting-started)
|
18
|
+
- [Contribution](#contribution)
|
19
|
+
- [Support + Feedback](#support--feedback)
|
20
|
+
- [Vulnerability Reporting](#vulnerability-reporting)
|
21
|
+
- [What is Auth0](#what-is-auth0)
|
22
|
+
- [License](#license)
|
16
23
|
|
17
|
-
##
|
24
|
+
## Documentation
|
18
25
|
|
19
|
-
|
26
|
+
- [Ruby on Rails Quickstart](https://auth0.com/docs/quickstart/webapp/rails)
|
27
|
+
- [Sample projects](https://github.com/auth0-samples/auth0-rubyonrails-sample)
|
20
28
|
|
21
|
-
|
22
|
-
Rails.application.config.middleware.use OmniAuth::Builder do
|
23
|
-
provider :auth0, ENV['AUTH0_CLIENT_ID'], ENV['AUTH0_CLIENT_SECRET'], ENV['AUTH0_DOMAIN']
|
24
|
-
end
|
25
|
-
```
|
29
|
+
## Installation
|
26
30
|
|
27
|
-
|
31
|
+
Add the following line to your `Gemfile`:
|
28
32
|
|
29
33
|
```ruby
|
30
|
-
|
34
|
+
gem 'omniauth-auth0'
|
31
35
|
```
|
32
36
|
|
33
|
-
|
37
|
+
If you're using this strategy with Rails, also add the following for CSRF protection:
|
34
38
|
|
35
39
|
```ruby
|
36
|
-
|
37
|
-
provider :auth0, ENV['AUTH0_CLIENT_ID'], ENV['AUTH0_CLIENT_SECRET'], ENV['AUTH0_DOMAIN']
|
38
|
-
end
|
40
|
+
gem 'omniauth-rails_csrf_protection'
|
39
41
|
```
|
40
42
|
|
41
|
-
Then
|
43
|
+
Then install:
|
42
44
|
|
43
|
-
```
|
44
|
-
|
45
|
+
```bash
|
46
|
+
$ bundle install
|
45
47
|
```
|
46
48
|
|
47
|
-
|
49
|
+
See our [contributing guide](CONTRIBUTING.md) for information on local installation for development.
|
50
|
+
|
51
|
+
## Getting Started
|
52
|
+
|
53
|
+
To start processing authentication requests, the following steps must be performed:
|
48
54
|
|
49
|
-
|
55
|
+
1. Initialize the strategy
|
56
|
+
2. Configure the callback controller
|
57
|
+
3. Add the required routes
|
58
|
+
4. Trigger an authentication request
|
50
59
|
|
51
|
-
|
60
|
+
All of these tasks and more are covered in our [Ruby on Rails Quickstart](https://auth0.com/docs/quickstart/webapp/rails).
|
61
|
+
|
62
|
+
### Additional authentication parameters
|
63
|
+
|
64
|
+
To send additional parameters during login, you can specify them when you register the provider:
|
52
65
|
|
53
66
|
```ruby
|
54
|
-
provider
|
67
|
+
provider
|
55
68
|
:auth0,
|
56
69
|
ENV['AUTH0_CLIENT_ID'],
|
57
70
|
ENV['AUTH0_CLIENT_SECRET'],
|
@@ -59,85 +72,101 @@ provider
|
|
59
72
|
{
|
60
73
|
authorize_params: {
|
61
74
|
scope: 'openid read:users write:order',
|
62
|
-
audience: 'https://mydomain/api'
|
75
|
+
audience: 'https://mydomain/api',
|
76
|
+
max_age: 3600 # time in seconds authentication is valid
|
63
77
|
}
|
64
78
|
}
|
65
79
|
```
|
66
80
|
|
67
|
-
|
81
|
+
... which will tell the strategy to send those parameters on every authentication request.
|
82
|
+
|
83
|
+
### Authentication hash
|
68
84
|
|
69
|
-
|
85
|
+
The Auth0 strategy will provide the standard OmniAuth hash attributes:
|
86
|
+
|
87
|
+
- `:provider` - the name of the strategy, in this case `auth0`
|
88
|
+
- `:uid` - the user identifier
|
89
|
+
- `:info` - the result of the call to `/userinfo` using OmniAuth standard attributes
|
90
|
+
- `:credentials` - tokens requested and data
|
91
|
+
- `:extra` - Additional info obtained from calling `/userinfo` in the `:raw_info` property
|
70
92
|
|
71
93
|
```ruby
|
72
|
-
|
94
|
+
{
|
95
|
+
:provider => 'auth0',
|
96
|
+
:uid => 'auth0|USER_ID',
|
97
|
+
:info => {
|
98
|
+
:name => 'John Foo',
|
99
|
+
:email => 'johnfoo@example.org',
|
100
|
+
:nickname => 'john',
|
101
|
+
:image => 'https://example.org/john.jpg'
|
102
|
+
},
|
103
|
+
:credentials => {
|
104
|
+
:token => 'ACCESS_TOKEN',
|
105
|
+
:expires_at => 1485373937,
|
106
|
+
:expires => true,
|
107
|
+
:refresh_token => 'REFRESH_TOKEN',
|
108
|
+
:id_token => 'JWT_ID_TOKEN',
|
109
|
+
:token_type => 'bearer',
|
110
|
+
},
|
111
|
+
:extra => {
|
112
|
+
:raw_info => {
|
113
|
+
:email => 'johnfoo@example.org',
|
114
|
+
:email_verified => 'true',
|
115
|
+
:name => 'John Foo',
|
116
|
+
:picture => 'https://example.org/john.jpg',
|
117
|
+
:user_id => 'auth0|USER_ID',
|
118
|
+
:nickname => 'john',
|
119
|
+
:created_at => '2014-07-15T17:19:50.387Z'
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
73
123
|
```
|
74
124
|
|
75
|
-
###
|
125
|
+
### Query Parameter Options
|
76
126
|
|
77
|
-
|
127
|
+
In some scenarios, you may need to pass specific query parameters to `/authorize`. The following parameters are available to enable this:
|
78
128
|
|
79
|
-
-
|
80
|
-
-
|
81
|
-
-
|
82
|
-
-
|
83
|
-
- extra: Additional info obtained from calling /userinfo in the attribute `raw_info`
|
129
|
+
- `connection`
|
130
|
+
- `connection_scope`
|
131
|
+
- `prompt`
|
132
|
+
- `screen_hint` (only relevant to New Universal Login Experience)
|
84
133
|
|
85
|
-
|
86
|
-
{
|
87
|
-
:provider => 'auth0',
|
88
|
-
:uid => 'google-oauth2|this-is-the-google-id',
|
89
|
-
:info => {
|
90
|
-
:name => 'John Foo',
|
91
|
-
:email => 'johnfoo@example.org',
|
92
|
-
:nickname => 'john',
|
93
|
-
:image => 'https://example.org/john.jpg'
|
94
|
-
},
|
95
|
-
:credentials => {
|
96
|
-
:token => 'XdDadllcas2134rdfdsI',
|
97
|
-
:expires_at => 1485373937,
|
98
|
-
:expires => true,
|
99
|
-
:refresh_token => 'aKNajdjfj123nBasd',
|
100
|
-
:id_token => 'eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBGb28ifQ.lxAiy1rqve8ZHQEQVehUlP1sommPHVJDhgPgFPnDosg',
|
101
|
-
:token_type => 'bearer',
|
102
|
-
},
|
103
|
-
:extra => {
|
104
|
-
:raw_info => {
|
105
|
-
:email => 'johnfoo@example.org',
|
106
|
-
:email_verified => 'true',
|
107
|
-
:name => 'John Foo',
|
108
|
-
:picture => 'https://example.org/john.jpg',
|
109
|
-
:user_id => 'google-oauth2|this-is-the-google-id',
|
110
|
-
:nickname => 'john',
|
111
|
-
:created_at: '2014-07-15T17:19:50.387Z'
|
112
|
-
}
|
113
|
-
}
|
114
|
-
}
|
115
|
-
```
|
134
|
+
Simply pass these query parameters to your OmniAuth redirect endpoint to enable their behavior.
|
116
135
|
|
117
|
-
|
136
|
+
## Contribution
|
118
137
|
|
119
|
-
|
138
|
+
We appreciate feedback and contribution to this repo! Before you get started, please see the following:
|
120
139
|
|
121
|
-
|
140
|
+
- [Auth0's contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
|
141
|
+
- [Auth0's Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
|
142
|
+
- [This repo's contribution guide](CONTRIBUTING.md)
|
122
143
|
|
123
|
-
|
124
|
-
CrazyApp::Application.config.session_store :cache_store
|
144
|
+
## Support + Feedback
|
125
145
|
|
126
|
-
|
127
|
-
|
146
|
+
- Use [Community](https://community.auth0.com/) for usage, questions, specific cases.
|
147
|
+
- Use [Issues](https://github.com/auth0/omniauth-auth0/issues) here for code-level support and bug reports.
|
148
|
+
- Paid customers can use [Support](https://support.auth0.com/) to submit a trouble ticket for production-affecting issues.
|
128
149
|
|
129
|
-
##
|
150
|
+
## Vulnerability Reporting
|
130
151
|
|
131
|
-
|
152
|
+
Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues.
|
132
153
|
|
133
|
-
##
|
154
|
+
## What is Auth0?
|
134
155
|
|
135
|
-
|
156
|
+
Auth0 helps you to easily:
|
136
157
|
|
137
|
-
|
158
|
+
- implement authentication with multiple identity providers, including social (e.g., Google, Facebook, Microsoft, LinkedIn, GitHub, Twitter, etc), or enterprise (e.g., Windows Azure AD, Google Apps, Active Directory, ADFS, SAML, etc.)
|
159
|
+
- log in users with username/password databases, passwordless, or multi-factor authentication
|
160
|
+
- link multiple user accounts together
|
161
|
+
- generate signed JSON Web Tokens to authorize your API calls and flow the user identity securely
|
162
|
+
- access demographics and analytics detailing how, when, and where users are logging in
|
163
|
+
- enrich user profiles from other data sources using customizable JavaScript rules
|
138
164
|
|
139
|
-
[Auth0](https://auth0.com)
|
165
|
+
[Why Auth0?](https://auth0.com/why-auth0)
|
140
166
|
|
141
167
|
## License
|
142
168
|
|
143
|
-
|
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)
|
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ begin
|
|
10
10
|
RuboCop::RakeTask.new
|
11
11
|
rescue LoadError
|
12
12
|
task :rubocop do
|
13
|
-
|
13
|
+
warn 'Rubocop is disabled'
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -23,7 +23,7 @@ namespace :sinatra do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
desc 'Run specs'
|
26
|
-
task default: [
|
26
|
+
task default: %i[spec rubocop]
|
27
27
|
task test: :spec
|
28
28
|
task :guard do
|
29
29
|
system 'bundle exec guard'
|
data/codecov.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
coverage:
|
2
|
+
precision: 2
|
3
|
+
round: down
|
4
|
+
range: "60...100"
|
5
|
+
status:
|
6
|
+
project:
|
7
|
+
default:
|
8
|
+
enabled: true
|
9
|
+
target: auto
|
10
|
+
threshold: 5%
|
11
|
+
if_no_uploads: error
|
12
|
+
patch:
|
13
|
+
default:
|
14
|
+
enabled: true
|
15
|
+
target: 80%
|
16
|
+
threshold: 30%
|
17
|
+
if_no_uploads: error
|
18
|
+
changes:
|
19
|
+
default:
|
20
|
+
enabled: true
|
21
|
+
if_no_uploads: error
|
22
|
+
comment: false
|
data/lib/omniauth-auth0.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require 'omniauth-auth0/version'
|
1
|
+
require 'omniauth-auth0/version'
|
2
2
|
require 'omniauth/strategies/auth0'
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
require 'omniauth'
|
5
|
+
require 'omniauth/auth0/errors'
|
6
|
+
|
7
|
+
module OmniAuth
|
8
|
+
module Auth0
|
9
|
+
# JWT Validator class
|
10
|
+
class JWTValidator
|
11
|
+
attr_accessor :issuer, :domain
|
12
|
+
|
13
|
+
# Initializer
|
14
|
+
# @param options object
|
15
|
+
# options.domain - Application domain.
|
16
|
+
# options.issuer - Application issuer (optional).
|
17
|
+
# options.client_id - Application Client ID.
|
18
|
+
# options.client_secret - Application Client Secret.
|
19
|
+
|
20
|
+
def initialize(options, authorize_params = {})
|
21
|
+
@domain = uri_string(options.domain)
|
22
|
+
|
23
|
+
# Use custom issuer if provided, otherwise use domain
|
24
|
+
@issuer = @domain
|
25
|
+
@issuer = uri_string(options.issuer) if options.respond_to?(:issuer)
|
26
|
+
|
27
|
+
@client_id = options.client_id
|
28
|
+
@client_secret = options.client_secret
|
29
|
+
end
|
30
|
+
|
31
|
+
def verify_signature(jwt)
|
32
|
+
head = token_head(jwt)
|
33
|
+
|
34
|
+
# Make sure the algorithm is supported and get the decode key.
|
35
|
+
if head[:alg] == 'RS256'
|
36
|
+
[rs256_decode_key(head[:kid]), head[:alg]]
|
37
|
+
elsif head[:alg] == 'HS256'
|
38
|
+
[@client_secret, head[:alg]]
|
39
|
+
else
|
40
|
+
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
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Verify a JWT.
|
45
|
+
# @param jwt string - JWT to verify.
|
46
|
+
# @param authorize_params hash - Authorization params to verify on the JWT
|
47
|
+
# @return hash - The verified token, if there were no exceptions.
|
48
|
+
def verify(jwt, authorize_params = {})
|
49
|
+
if !jwt
|
50
|
+
raise OmniAuth::Auth0::TokenValidationError.new('ID token is required but missing')
|
51
|
+
end
|
52
|
+
|
53
|
+
parts = jwt.split('.')
|
54
|
+
if parts.length != 3
|
55
|
+
raise OmniAuth::Auth0::TokenValidationError.new('ID token could not be decoded')
|
56
|
+
end
|
57
|
+
|
58
|
+
key, alg = verify_signature(jwt)
|
59
|
+
id_token, header = JWT.decode(jwt, key, false)
|
60
|
+
verify_claims(id_token, authorize_params)
|
61
|
+
|
62
|
+
return id_token
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get the decoded head segment from a JWT.
|
66
|
+
# @return hash - The parsed head of the JWT passed, empty hash if not.
|
67
|
+
def token_head(jwt)
|
68
|
+
jwt_parts = jwt.split('.')
|
69
|
+
return {} if blank?(jwt_parts) || blank?(jwt_parts[0])
|
70
|
+
|
71
|
+
json_parse(Base64.decode64(jwt_parts[0]))
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get the JWKS from the issuer and return a public key.
|
75
|
+
# @param x5c string - X.509 certificate chain from a JWKS.
|
76
|
+
# @return key - The X.509 certificate public key.
|
77
|
+
def jwks_public_cert(x5c)
|
78
|
+
x5c = Base64.decode64(x5c)
|
79
|
+
|
80
|
+
# https://docs.ruby-lang.org/en/2.4.0/OpenSSL/X509/Certificate.html
|
81
|
+
OpenSSL::X509::Certificate.new(x5c).public_key
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return a specific key from a JWKS object.
|
85
|
+
# @param key string - Key to find in the JWKS.
|
86
|
+
# @param kid string - Key ID to identify the right JWK.
|
87
|
+
# @return nil|string
|
88
|
+
def jwks_key(key, kid)
|
89
|
+
return nil if blank?(jwks[:keys])
|
90
|
+
|
91
|
+
matching_jwk = jwks[:keys].find { |jwk| jwk[:kid] == kid }
|
92
|
+
matching_jwk[key] if matching_jwk
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def rs256_decode_key(kid)
|
97
|
+
jwks_x5c = jwks_key(:x5c, kid)
|
98
|
+
|
99
|
+
if jwks_x5c.nil?
|
100
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Could not find a public key for Key ID (kid) '#{kid}''")
|
101
|
+
end
|
102
|
+
|
103
|
+
jwks_public_cert(jwks_x5c.first)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get a JWKS from the domain
|
107
|
+
# @return void
|
108
|
+
def jwks
|
109
|
+
jwks_uri = URI(@domain + '.well-known/jwks.json')
|
110
|
+
@jwks ||= json_parse(Net::HTTP.get(jwks_uri))
|
111
|
+
end
|
112
|
+
|
113
|
+
# Rails Active Support blank method.
|
114
|
+
# @param obj object - Object to check for blankness.
|
115
|
+
# @return boolean
|
116
|
+
def blank?(obj)
|
117
|
+
obj.respond_to?(:empty?) ? obj.empty? : !obj
|
118
|
+
end
|
119
|
+
|
120
|
+
# Parse JSON with symbolized names.
|
121
|
+
# @param json string - JSON to parse.
|
122
|
+
# @return hash
|
123
|
+
def json_parse(json)
|
124
|
+
JSON.parse(json, symbolize_names: true)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Parse a URI into the desired string format
|
128
|
+
# @param uri - the URI to parse
|
129
|
+
# @return string
|
130
|
+
def uri_string(uri)
|
131
|
+
temp_domain = URI(uri)
|
132
|
+
temp_domain = URI("https://#{uri}") unless temp_domain.scheme
|
133
|
+
"#{temp_domain}/"
|
134
|
+
end
|
135
|
+
|
136
|
+
def verify_claims(id_token, authorize_params)
|
137
|
+
leeway = authorize_params[:leeway] || 60
|
138
|
+
max_age = authorize_params[:max_age]
|
139
|
+
nonce = authorize_params[:nonce]
|
140
|
+
|
141
|
+
verify_iss(id_token)
|
142
|
+
verify_sub(id_token)
|
143
|
+
verify_aud(id_token)
|
144
|
+
verify_expiration(id_token, leeway)
|
145
|
+
verify_iat(id_token)
|
146
|
+
verify_nonce(id_token, nonce)
|
147
|
+
verify_azp(id_token)
|
148
|
+
verify_auth_time(id_token, leeway, max_age)
|
149
|
+
end
|
150
|
+
|
151
|
+
def verify_iss(id_token)
|
152
|
+
issuer = id_token['iss']
|
153
|
+
if !issuer
|
154
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Issuer (iss) claim must be a string present in the ID token")
|
155
|
+
elsif @issuer != issuer
|
156
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Issuer (iss) claim mismatch in the ID token, expected (#{@issuer}), found (#{id_token['iss']})")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def verify_sub(id_token)
|
161
|
+
subject = id_token['sub']
|
162
|
+
if !subject || !subject.is_a?(String) || subject.empty?
|
163
|
+
raise OmniAuth::Auth0::TokenValidationError.new('Subject (sub) claim must be a string present in the ID token')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def verify_aud(id_token)
|
168
|
+
audience = id_token['aud']
|
169
|
+
if !audience || !(audience.is_a?(String) || audience.is_a?(Array))
|
170
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Audience (aud) claim must be a string or array of strings present in the ID token")
|
171
|
+
elsif audience.is_a?(Array) && !audience.include?(@client_id)
|
172
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Audience (aud) claim mismatch in the ID token; expected #{@client_id} but was not one of #{audience.join(', ')}")
|
173
|
+
elsif audience.is_a?(String) && audience != @client_id
|
174
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Audience (aud) claim mismatch in the ID token; expected #{@client_id} but found #{audience}")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def verify_expiration(id_token, leeway)
|
179
|
+
expiration = id_token['exp']
|
180
|
+
if !expiration || !expiration.is_a?(Integer)
|
181
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Expiration time (exp) claim must be a number present in the ID token")
|
182
|
+
elsif expiration <= Time.now.to_i - leeway
|
183
|
+
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)})")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def verify_iat(id_token)
|
188
|
+
if !id_token['iat']
|
189
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Issued At (iat) claim must be a number present in the ID token")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def verify_nonce(id_token, nonce)
|
194
|
+
if nonce
|
195
|
+
received_nonce = id_token['nonce']
|
196
|
+
if !received_nonce
|
197
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Nonce (nonce) claim must be a string present in the ID token")
|
198
|
+
elsif nonce != received_nonce
|
199
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Nonce (nonce) claim value mismatch in the ID token; expected (#{nonce}), found (#{received_nonce})")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def verify_azp(id_token)
|
205
|
+
audience = id_token['aud']
|
206
|
+
if audience.is_a?(Array) && audience.length > 1
|
207
|
+
azp = id_token['azp']
|
208
|
+
if !azp || !azp.is_a?(String)
|
209
|
+
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")
|
210
|
+
elsif azp != @client_id
|
211
|
+
raise OmniAuth::Auth0::TokenValidationError.new("Authorized Party (azp) claim mismatch in the ID token; expected (#{@client_id}), found (#{azp})")
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def verify_auth_time(id_token, leeway, max_age)
|
217
|
+
if max_age
|
218
|
+
auth_time = id_token['auth_time']
|
219
|
+
if !auth_time || !auth_time.is_a?(Integer)
|
220
|
+
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")
|
221
|
+
elsif Time.now.to_i > auth_time + max_age + leeway;
|
222
|
+
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)})")
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|