omniauth_openid_connect 0.3.0 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/config/rubocop_linter_action.yml +59 -0
- data/.github/stale.yml +17 -0
- data/.github/workflows/rubocop.yml +22 -0
- data/.rubocop.yml +58 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +2 -0
- data/Guardfile +5 -3
- data/README.md +52 -8
- data/lib/omniauth/openid_connect.rb +2 -0
- data/lib/omniauth/openid_connect/errors.rb +3 -0
- data/lib/omniauth/openid_connect/version.rb +3 -1
- data/lib/omniauth/strategies/openid_connect.rb +127 -70
- data/lib/omniauth_openid_connect.rb +2 -0
- data/omniauth_openid_connect.gemspec +14 -13
- data/test/lib/omniauth/strategies/openid_connect_test.rb +163 -8
- metadata +44 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c030571ea9bcbd861ebe4d8455282c0b1b34c17af77294660b1c0123ed976ab
|
4
|
+
data.tar.gz: 2f4b2a8cd026797260f36e358ac7678a6827486889f23aab325d08e0d390b649
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afdf6363a18b019939a88abc612be51b20780fdeae2f1f300ef5e9df5a1002a97812b17360939e1ba9bb0b3bf4e54471e3da0ec6de858ac2afa40df15fdfb23d
|
7
|
+
data.tar.gz: e449bd59e5e75cfcce12ae27e5072fb8e3fe821e969a510856bbad0ed2553252345826ed16e97ecdbe1405a8c4be275195f759e71aed99fd699bf9509bffa0f3
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Description: The name of the check that will be created.
|
2
|
+
# Valid Options: A reasonably sized string.
|
3
|
+
# Default: 'Rubocop Action'
|
4
|
+
check_name: 'Rubocop Results'
|
5
|
+
|
6
|
+
# Description: Versions required to run your RuboCop checks.
|
7
|
+
# Valid options: RuboCop and any RuboCop extension, by default the latest gem version will be used. You can explicitly state that
|
8
|
+
# (not required) or use a version number, like '1.5.1'.
|
9
|
+
# Default:
|
10
|
+
# versions:
|
11
|
+
# - rubocop: 'latest'
|
12
|
+
versions:
|
13
|
+
- rubocop
|
14
|
+
- rubocop-minitest
|
15
|
+
- rubocop-performance: '1.5.1'
|
16
|
+
|
17
|
+
# Description: Rubocop configuration file path relative to the workspace.
|
18
|
+
# Valid options: A valid file path inside of the workspace.
|
19
|
+
# Default: nil
|
20
|
+
# Note: This does not need to be filled out for Rubocop to still find your config.
|
21
|
+
# Resource: https://rubocop.readthedocs.io/en/stable/configuration/
|
22
|
+
rubocop_config_path: '.rubocop.yml'
|
23
|
+
|
24
|
+
# Run all cops enabled by configuration except this list.
|
25
|
+
# Valid options: list of valid cop(s) and/or departments.
|
26
|
+
# Default: nil
|
27
|
+
# Resource: https://rubocop.readthedocs.io/en/stable/cops/
|
28
|
+
# rubocop_excluded_cops:
|
29
|
+
# - 'Style/FrozenStringLiteralComment'
|
30
|
+
|
31
|
+
# Minimum severity for exit with error code
|
32
|
+
# Valid options: 'refactor', 'convention', 'warning', 'error', or 'fatal'.
|
33
|
+
# Default: 'warning'
|
34
|
+
# Resource: https://rubocop.readthedocs.io/en/stable/configuration/#severity
|
35
|
+
# rubocop_fail_level: 'warning'
|
36
|
+
|
37
|
+
# Whether or not to use --force-exclusion when building the rubocop command. Use this if you are only linting modified
|
38
|
+
# files and typically excluded files have been changed. For example, if you exclude db/schema.rb in your rubocop.yml
|
39
|
+
# but a change gets made, then with the check_scope config set to 'modified' rubocop will lint db/schema.rb. If you set
|
40
|
+
# this to true, rubocop will ignore it.
|
41
|
+
# Valid options: true || false
|
42
|
+
# Default: false
|
43
|
+
|
44
|
+
# Instead of installing gems from rubygems, we can run `bundle install` on your project,
|
45
|
+
# you would need to do this if you are using something like 'rubocop-github' or if you don't
|
46
|
+
# want to list out dependencies with the `versions` key.
|
47
|
+
# Valid options: true || false
|
48
|
+
# Default: false
|
49
|
+
# bundle: false
|
50
|
+
|
51
|
+
# The scope of code that Rubocop should lint. Use this if you only want to lint changed files. If this is not set
|
52
|
+
# or not equal to 'modified', Rubocop is run against the entire codebase. Note that this will not work on the master branch.
|
53
|
+
# Valid options: 'modified'
|
54
|
+
# Default: nil
|
55
|
+
|
56
|
+
# The base branch against which changes will be compared, if check_scope config is set to 'modified'.
|
57
|
+
# This setting is not used if check_scope != 'modified'.
|
58
|
+
# Valid options: 'origin/another_branch'
|
59
|
+
# Default: 'origin/master'
|
data/.github/stale.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Number of days of inactivity before an issue becomes stale
|
2
|
+
daysUntilStale: 60
|
3
|
+
# Number of days of inactivity before a stale issue is closed
|
4
|
+
daysUntilClose: 7
|
5
|
+
# Issues with these labels will never be considered stale
|
6
|
+
exemptLabels:
|
7
|
+
- pinned
|
8
|
+
- security
|
9
|
+
# Label to use when marking an issue as stale
|
10
|
+
staleLabel: wontfix
|
11
|
+
# Comment to post when marking an issue as stale. Set to `false` to disable
|
12
|
+
markComment: >
|
13
|
+
This issue has been automatically marked as stale because it has not had
|
14
|
+
recent activity. It will be closed if no further activity occurs. Thank you
|
15
|
+
for your contributions.
|
16
|
+
# Comment to post when closing a stale issue. Set to `false` to disable
|
17
|
+
closeComment: false
|
@@ -0,0 +1,22 @@
|
|
1
|
+
name: Rubocop check
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
branches:
|
6
|
+
- "*"
|
7
|
+
push:
|
8
|
+
branches:
|
9
|
+
- master
|
10
|
+
jobs:
|
11
|
+
build:
|
12
|
+
name: RuboCop Action
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
steps:
|
15
|
+
- name: Checkout Action
|
16
|
+
uses: actions/checkout@v1
|
17
|
+
- name: Rubocop Linter Action
|
18
|
+
uses: andrewmcodes/rubocop-linter-action@v3.2.0
|
19
|
+
with:
|
20
|
+
action_config_path: '.github/config/rubocop_linter_action.yml'
|
21
|
+
env:
|
22
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
LineLength:
|
2
|
+
Description: 'Limit lines to 130 characters.'
|
3
|
+
Max: 130
|
4
|
+
|
5
|
+
Layout/SpaceInsideStringInterpolation:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
Layout/MultilineOperationIndentation:
|
9
|
+
EnforcedStyle: indented
|
10
|
+
|
11
|
+
StringLiterals:
|
12
|
+
EnforcedStyle: single_quotes
|
13
|
+
|
14
|
+
Style/TrailingCommaInArrayLiteral:
|
15
|
+
EnforcedStyleForMultiline: comma
|
16
|
+
Style/TrailingCommaInHashLiteral:
|
17
|
+
EnforcedStyleForMultiline: comma
|
18
|
+
|
19
|
+
Style/SafeNavigation:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/EmptyMethod:
|
23
|
+
Description: 'Checks the formatting of empty method definitions.'
|
24
|
+
StyleGuide: '#no-single-line-methods'
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
HashSyntax:
|
28
|
+
Description: "Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax\n{ :a => 1, :b => 2 }"
|
29
|
+
EnforcedStyle: ruby19
|
30
|
+
Enabled: true
|
31
|
+
|
32
|
+
RedundantBegin:
|
33
|
+
Enabled: true
|
34
|
+
|
35
|
+
Documentation:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Metrics/AbcSize:
|
39
|
+
Max: 50
|
40
|
+
|
41
|
+
Metrics/CyclomaticComplexity:
|
42
|
+
Max: 50
|
43
|
+
|
44
|
+
Metrics/PerceivedComplexity:
|
45
|
+
Max: 15
|
46
|
+
|
47
|
+
Metrics/BlockLength:
|
48
|
+
Max: 40
|
49
|
+
|
50
|
+
Metrics/MethodLength:
|
51
|
+
Max: 45
|
52
|
+
|
53
|
+
AllCops:
|
54
|
+
Exclude:
|
55
|
+
- bin/**/*
|
56
|
+
- Rakefile
|
57
|
+
- config/**/*
|
58
|
+
- test/**/*
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
# v0.3.5 (07.06.2020)
|
2
|
+
|
3
|
+
- bugfix: Info from decoded id_token is not exposed into `request.env['omniauth.auth']` [#61](https://github.com/m0n9oose/omniauth_openid_connect/pull/61)
|
4
|
+
- bugfix: NoMethodError (`undefined method 'count' for #<OpenIDConnect::ResponseObject::IdToken>`) [#60](https://github.com/m0n9oose/omniauth_openid_connect/pull/60)
|
5
|
+
|
6
|
+
# v0.3.4 (21.05.2020)
|
7
|
+
|
8
|
+
- Try to verify id_token when response_type is code [#44](https://github.com/m0n9oose/omniauth_openid_connect/pull/44)
|
9
|
+
- Provide more information on error [#49](https://github.com/m0n9oose/omniauth_openid_connect/pull/49)
|
10
|
+
- Update configuration documentation [#53](https://github.com/m0n9oose/omniauth_openid_connect/pull/53)
|
11
|
+
- Add documentation about the send_scope_to_token_endpoint config property [#52](https://github.com/m0n9oose/omniauth_openid_connect/pull/52)
|
12
|
+
- refactor: take uid_field from raw_attributes [#54](https://github.com/m0n9oose/omniauth_openid_connect/pull/54)
|
13
|
+
- chore(ci): add 2.7, ruby-head and jruby-head [#55](https://github.com/m0n9oose/omniauth_openid_connect/pull/55)
|
14
|
+
|
15
|
+
# v0.3.3 (09.11.2019)
|
16
|
+
|
17
|
+
- Pass `acr_values` to authorize url [#43](https://github.com/m0n9oose/omniauth_openid_connect/pull/43)
|
18
|
+
- Add raw info for id token [#42](https://github.com/m0n9oose/omniauth_openid_connect/pull/42)
|
19
|
+
- Fixed `id_token` verification when `id_token` is not used [#41](https://github.com/m0n9oose/omniauth_openid_connect/pull/41)
|
20
|
+
- Cast `response_type` to string when checking if it is set in params [#36](https://github.com/m0n9oose/omniauth_openid_connect/pull/36)
|
21
|
+
- Support both symbol and string version of `response_type` option [#35](https://github.com/m0n9oose/omniauth_openid_connect/pull/35)
|
22
|
+
- Fix gemspec homepage [#33](https://github.com/m0n9oose/omniauth_openid_connect/pull/33)
|
23
|
+
- Add support for `response_type` `id_token` [#32](https://github.com/m0n9oose/omniauth_openid_connect/pull/32)
|
24
|
+
|
25
|
+
# v0.3.2 (03.08.2019)
|
26
|
+
|
27
|
+
- Use response_mode in `authorize_uri` if the option is defined [#30](https://github.com/m0n9oose/omniauth_openid_connect/pull/30)
|
28
|
+
- Move verification of `id_token` to before accessing tokens [#28](https://github.com/m0n9oose/omniauth_openid_connect/pull/28)
|
29
|
+
- Update omniauth dependency [#26](https://github.com/m0n9oose/omniauth_openid_connect/pull/26)
|
30
|
+
|
31
|
+
# v0.3.1 (08.06.2019)
|
32
|
+
|
33
|
+
- Set default OmniAuth name to openid_connect [#23](https://github.com/m0n9oose/omniauth_openid_connect/pull/23)
|
34
|
+
|
1
35
|
# v0.3.0 (27.04.2019)
|
2
36
|
|
3
37
|
- RP-Initiated Logout phase [#5](https://github.com/m0n9oose/omniauth_openid_connect/pull/5)
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# A sample Guardfile
|
2
4
|
# More info at https://github.com/guard/guard#readme
|
3
5
|
|
4
6
|
guard 'minitest' do
|
5
7
|
# with Minitest::Unit
|
6
|
-
watch(%r
|
7
|
-
watch(%r
|
8
|
-
watch(%r
|
8
|
+
watch(%r{^test/(.*)\/(.*)_test\.rb})
|
9
|
+
watch(%r{^lib/(.*)\.rb}) { |m| "test/lib/#{m[1]}_test.rb" }
|
10
|
+
watch(%r{^test/test_helper\.rb}) { 'test' }
|
9
11
|
end
|
10
12
|
|
11
13
|
guard :bundler do
|
data/README.md
CHANGED
@@ -19,6 +19,10 @@ And then execute:
|
|
19
19
|
Or install it yourself as:
|
20
20
|
|
21
21
|
$ gem install omniauth_openid_connect
|
22
|
+
|
23
|
+
## Supported Ruby Versions
|
24
|
+
|
25
|
+
OmniAuth::OpenIDConnect is tested under 2.4, 2.5, 2.6, 2.7
|
22
26
|
|
23
27
|
## Usage
|
24
28
|
|
@@ -40,7 +44,44 @@ config.omniauth :openid_connect, {
|
|
40
44
|
}
|
41
45
|
```
|
42
46
|
|
43
|
-
|
47
|
+
### Options Overview
|
48
|
+
|
49
|
+
| Field | Description | Required | Default | Example/Options |
|
50
|
+
|------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------------------------|-----------------------------------------------------|
|
51
|
+
| name | Arbitrary string to identify connection and identify it from other openid_connect providers | no | String: openid_connect | :my_idp |
|
52
|
+
| issuer | Root url for the authorization server | yes | | https://myprovider.com |
|
53
|
+
| discovery | Should OpenID discovery be used. This is recommended if the IDP provides a discovery endpoint. See client config for how to manually enter discovered values. | no | false | one of: true, false |
|
54
|
+
| client_auth_method | Which authentication method to use to authenticate your app with the authorization server | no | Sym: basic | "basic", "jwks" |
|
55
|
+
| scope | Which OpenID scopes to include (:openid is always required) | no | Array<sym> [:openid] | [:openid, :profile, :email] |
|
56
|
+
| response_type | Which OAuth2 response type to use with the authorization request | no | String: code | one of: 'code', 'id_token' |
|
57
|
+
| state | A value to be used for the OAuth2 state parameter on the authorization request. Can be a proc that generates a string. | no | Random 16 character string | Proc.new { SecureRandom.hex(32) } |
|
58
|
+
| response_mode | The response mode per [spec](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html) | no | nil | one of: :query, :fragment, :form_post, :web_message |
|
59
|
+
| display | An optional parameter to the authorization request to determine how the authorization and consent page | no | nil | one of: :page, :popup, :touch, :wap |
|
60
|
+
| prompt | An optional parameter to the authrization request to determine what pages the user will be shown | no | nil | one of: :none, :login, :consent, :select_account |
|
61
|
+
| send_scope_to_token_endpoint | Should the scope parameter be sent to the authorization token endpoint? | no | true | one of: true, false |
|
62
|
+
| post_logout_redirect_uri | The logout redirect uri to use per the [session management draft](https://openid.net/specs/openid-connect-session-1_0.html) | no | empty | https://myapp.com/logout/callback |
|
63
|
+
| uid_field | The field of the user info response to be used as a unique id | no | 'sub' | "sub", "preferred_username" |
|
64
|
+
| client_options | A hash of client options detailed in its own section | yes | | |
|
65
|
+
|
66
|
+
### Client Config Options
|
67
|
+
|
68
|
+
These are the configuration options for the client_options hash of the configuration.
|
69
|
+
|
70
|
+
| Field | Description | Default | Replaced by discovery? |
|
71
|
+
|------------------------|-----------------------------------------------------------------|------------|------------------------|
|
72
|
+
| identifier | The OAuth2 client_id | | |
|
73
|
+
| secret | The OAuth2 client secret | | |
|
74
|
+
| redirect_uri | The OAuth2 authorization callback url in your app | | |
|
75
|
+
| scheme | The http scheme to use | https | |
|
76
|
+
| host | The host of the authorization server | nil | |
|
77
|
+
| port | The port for the authorization server | 443 | |
|
78
|
+
| authorization_endpoint | The authorize endpoint on the authorization server | /authorize | yes |
|
79
|
+
| token_endpoint | The token endpoint on the authorization server | /token | yes |
|
80
|
+
| userinfo_endpoint | The user info endpoint on the authorization server | /userinfo | yes |
|
81
|
+
| jwks_uri | The jwks_uri on the authorization server | /jwk | yes |
|
82
|
+
| end_session_endpoint | The url to call to log the user out at the authorization server | nil | yes |
|
83
|
+
|
84
|
+
### Additional Configuration Notes
|
44
85
|
* `name` is arbitrary, I recommend using the name of your provider. The name
|
45
86
|
configuration exists because you could be using multiple OpenID Connect
|
46
87
|
providers in a single app.
|
@@ -48,11 +89,9 @@ Configuration details:
|
|
48
89
|
**NOTE**: if you use this gem with Devise you should use `:openid_connect` name,
|
49
90
|
or Devise would route to 'users/auth/:provider' rather than 'users/auth/openid_connect'
|
50
91
|
|
51
|
-
*
|
52
|
-
|
53
|
-
|
54
|
-
server side web apps anyway and are designed more for native/mobile apps.
|
55
|
-
* If you want to pass `state` paramete by yourself. You can set Proc Object.
|
92
|
+
* `response_type` tells the authorization server which grant type the application wants to use,
|
93
|
+
currently, only `:code` (Authorization Code grant) and `:id_token` (Implicit grant) are valid.
|
94
|
+
* If you want to pass `state` paramete by yourself. You can set Proc Object.
|
56
95
|
e.g. `state: Proc.new { SecureRandom.hex(32) }`
|
57
96
|
* `nonce` is optional. If don't want to pass "nonce" parameter to provider, You should specify
|
58
97
|
`false` to `send_nonce` option. (default true)
|
@@ -60,14 +99,19 @@ Configuration details:
|
|
60
99
|
`:client_auth_method` option, automatically set `:basic`.
|
61
100
|
* Use "OpenID Connect Discovery", You should specify `true` to `discovery` option. (default false)
|
62
101
|
* In "OpenID Connect Discovery", generally provider should have Webfinger endpoint.
|
63
|
-
If provider does not have Webfinger endpoint, You can specify "Issuer" to option.
|
64
|
-
e.g. `issuer: "https://myprovider.com"`
|
102
|
+
If provider does not have Webfinger endpoint, You can specify "Issuer" to option.
|
103
|
+
e.g. `issuer: "https://myprovider.com"`
|
65
104
|
It means to get configuration from "https://myprovider.com/.well-known/openid-configuration".
|
66
105
|
* The uid is by default using the `sub` value from the `user_info` response,
|
67
106
|
which in some applications is not the expected value. To avoid such limitations, the uid label can be
|
68
107
|
configured by providing the omniauth `uid_field` option to a different label (i.e. `preferred_username`)
|
69
108
|
that appears in the `user_info` details.
|
70
109
|
* The `issuer` property should exactly match the provider's issuer link.
|
110
|
+
* The `response_mode` option is optional and specifies how the result of the authorization request is formatted.
|
111
|
+
* Some OpenID Connect providers require the `scope` attribute in requests to the token endpoint, even if
|
112
|
+
this is not in the protocol specifications. In those cases, the `send_scope_to_token_endpoint`
|
113
|
+
property can be used to add the attribute to the token request. Initial value is `true`, which means that the
|
114
|
+
scope attribute is included by default.
|
71
115
|
|
72
116
|
For the full low down on OpenID Connect, please check out
|
73
117
|
[the spec](http://openid.net/specs/openid-connect-core-1_0.html).
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'addressable/uri'
|
2
4
|
require 'timeout'
|
3
5
|
require 'net/http'
|
@@ -12,32 +14,37 @@ module OmniAuth
|
|
12
14
|
include OmniAuth::Strategy
|
13
15
|
extend Forwardable
|
14
16
|
|
17
|
+
RESPONSE_TYPE_EXCEPTIONS = {
|
18
|
+
'id_token' => { exception_class: OmniAuth::OpenIDConnect::MissingIdTokenError, key: :missing_id_token }.freeze,
|
19
|
+
'code' => { exception_class: OmniAuth::OpenIDConnect::MissingCodeError, key: :missing_code }.freeze,
|
20
|
+
}.freeze
|
21
|
+
|
15
22
|
def_delegator :request, :params
|
16
23
|
|
17
|
-
option :
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
option :name, 'openid_connect'
|
25
|
+
option(:client_options, identifier: nil,
|
26
|
+
secret: nil,
|
27
|
+
redirect_uri: nil,
|
28
|
+
scheme: 'https',
|
29
|
+
host: nil,
|
30
|
+
port: 443,
|
31
|
+
authorization_endpoint: '/authorize',
|
32
|
+
token_endpoint: '/token',
|
33
|
+
userinfo_endpoint: '/userinfo',
|
34
|
+
jwks_uri: '/jwk',
|
35
|
+
end_session_endpoint: nil)
|
36
|
+
|
30
37
|
option :issuer
|
31
38
|
option :discovery, false
|
32
39
|
option :client_signing_alg
|
33
40
|
option :client_jwk_signing_key
|
34
41
|
option :client_x509_signing_key
|
35
42
|
option :scope, [:openid]
|
36
|
-
option :response_type,
|
43
|
+
option :response_type, 'code' # ['code', 'id_token']
|
37
44
|
option :state
|
38
|
-
option :response_mode
|
39
|
-
option :display, nil
|
40
|
-
option :prompt, nil
|
45
|
+
option :response_mode # [:query, :fragment, :form_post, :web_message]
|
46
|
+
option :display, nil # [:page, :popup, :touch, :wap]
|
47
|
+
option :prompt, nil # [:none, :login, :consent, :select_account]
|
41
48
|
option :hd, nil
|
42
49
|
option :max_age
|
43
50
|
option :ui_locales
|
@@ -47,13 +54,11 @@ module OmniAuth
|
|
47
54
|
option :send_scope_to_token_endpoint, true
|
48
55
|
option :client_auth_method
|
49
56
|
option :post_logout_redirect_uri
|
57
|
+
option :extra_authorize_params, {}
|
50
58
|
option :uid_field, 'sub'
|
51
59
|
|
52
60
|
def uid
|
53
|
-
user_info.
|
54
|
-
rescue NoMethodError
|
55
|
-
log :warn, "User sub:#{user_info.sub} missing info field: #{options.uid_field}"
|
56
|
-
user_info.sub
|
61
|
+
user_info.raw_attributes[options.uid_field.to_sym] || user_info.sub
|
57
62
|
end
|
58
63
|
|
59
64
|
info do
|
@@ -66,7 +71,7 @@ module OmniAuth
|
|
66
71
|
gender: user_info.gender,
|
67
72
|
image: user_info.picture,
|
68
73
|
phone: user_info.phone_number,
|
69
|
-
urls: { website: user_info.website }
|
74
|
+
urls: { website: user_info.website },
|
70
75
|
}
|
71
76
|
end
|
72
77
|
|
@@ -80,7 +85,7 @@ module OmniAuth
|
|
80
85
|
token: access_token.access_token,
|
81
86
|
refresh_token: access_token.refresh_token,
|
82
87
|
expires_in: access_token.expires_in,
|
83
|
-
scope: access_token.scope
|
88
|
+
scope: access_token.scope,
|
84
89
|
}
|
85
90
|
end
|
86
91
|
|
@@ -93,29 +98,36 @@ module OmniAuth
|
|
93
98
|
end
|
94
99
|
|
95
100
|
def request_phase
|
96
|
-
options.issuer = issuer if options.issuer.
|
101
|
+
options.issuer = issuer if options.issuer.to_s.empty?
|
97
102
|
discover!
|
98
103
|
redirect authorize_uri
|
99
104
|
end
|
100
105
|
|
101
106
|
def callback_phase
|
102
107
|
error = params['error_reason'] || params['error']
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
108
|
+
error_description = params['error_description'] || params['error_reason']
|
109
|
+
invalid_state = params['state'].to_s.empty? || params['state'] != stored_state
|
110
|
+
|
111
|
+
raise CallbackError, error: params['error'], reason: error_description, uri: params['error_uri'] if error
|
112
|
+
raise CallbackError, error: :csrf_detected, reason: "Invalid 'state' parameter" if invalid_state
|
113
|
+
|
114
|
+
return unless valid_response_type?
|
115
|
+
|
116
|
+
options.issuer = issuer if options.issuer.nil? || options.issuer.empty?
|
117
|
+
|
118
|
+
verify_id_token!(params['id_token']) if configured_response_type == 'id_token'
|
119
|
+
discover!
|
120
|
+
client.redirect_uri = redirect_uri
|
121
|
+
|
122
|
+
return id_token_callback_phase if configured_response_type == 'id_token'
|
123
|
+
|
124
|
+
client.authorization_code = authorization_code
|
125
|
+
access_token
|
126
|
+
super
|
127
|
+
rescue CallbackError => e
|
128
|
+
fail!(e.error, e)
|
129
|
+
rescue ::Rack::OAuth2::Client::Error => e
|
130
|
+
fail!(e.response[:error], e)
|
119
131
|
rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
|
120
132
|
fail!(:timeout, e)
|
121
133
|
rescue ::SocketError => e
|
@@ -124,7 +136,7 @@ module OmniAuth
|
|
124
136
|
|
125
137
|
def other_phase
|
126
138
|
if logout_path_pattern.match?(current_path)
|
127
|
-
options.issuer = issuer if options.issuer.
|
139
|
+
options.issuer = issuer if options.issuer.to_s.empty?
|
128
140
|
discover!
|
129
141
|
return redirect(end_session_uri) if end_session_uri
|
130
142
|
end
|
@@ -137,6 +149,7 @@ module OmniAuth
|
|
137
149
|
|
138
150
|
def end_session_uri
|
139
151
|
return unless end_session_endpoint_is_valid?
|
152
|
+
|
140
153
|
end_session_uri = URI(client_options.end_session_endpoint)
|
141
154
|
end_session_uri.query = encoded_post_logout_redirect_uri
|
142
155
|
end_session_uri.to_s
|
@@ -146,6 +159,7 @@ module OmniAuth
|
|
146
159
|
client.redirect_uri = redirect_uri
|
147
160
|
opts = {
|
148
161
|
response_type: options.response_type,
|
162
|
+
response_mode: options.response_mode,
|
149
163
|
scope: options.scope,
|
150
164
|
state: new_state,
|
151
165
|
login_hint: params['login_hint'],
|
@@ -154,12 +168,17 @@ module OmniAuth
|
|
154
168
|
prompt: options.prompt,
|
155
169
|
nonce: (new_nonce if options.send_nonce),
|
156
170
|
hd: options.hd,
|
171
|
+
acr_values: options.acr_values,
|
157
172
|
}
|
158
|
-
|
173
|
+
|
174
|
+
opts.merge!(options.extra_authorize_params) unless options.extra_authorize_params.empty?
|
175
|
+
|
176
|
+
client.authorization_uri(opts.reject { |_k, v| v.nil? })
|
159
177
|
end
|
160
178
|
|
161
179
|
def public_key
|
162
180
|
return config.jwks if options.discovery
|
181
|
+
|
163
182
|
key_or_secret
|
164
183
|
end
|
165
184
|
|
@@ -173,6 +192,7 @@ module OmniAuth
|
|
173
192
|
|
174
193
|
def discover!
|
175
194
|
return unless options.discovery
|
195
|
+
|
176
196
|
client_options.authorization_endpoint = config.authorization_endpoint
|
177
197
|
client_options.token_endpoint = config.token_endpoint
|
178
198
|
client_options.userinfo_endpoint = config.userinfo_endpoint
|
@@ -181,23 +201,28 @@ module OmniAuth
|
|
181
201
|
end
|
182
202
|
|
183
203
|
def user_info
|
184
|
-
@user_info
|
204
|
+
return @user_info if @user_info
|
205
|
+
|
206
|
+
if access_token.id_token
|
207
|
+
decoded = decode_id_token(access_token.id_token).raw_attributes
|
208
|
+
|
209
|
+
@user_info = ::OpenIDConnect::ResponseObject::UserInfo.new access_token.userinfo!.raw_attributes.merge(decoded)
|
210
|
+
else
|
211
|
+
@user_info = access_token.userinfo!
|
212
|
+
end
|
185
213
|
end
|
186
214
|
|
187
215
|
def access_token
|
188
|
-
@access_token
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
)
|
199
|
-
_access_token
|
200
|
-
end
|
216
|
+
return @access_token if @access_token
|
217
|
+
|
218
|
+
@access_token = client.access_token!(
|
219
|
+
scope: (options.scope if options.send_scope_to_token_endpoint),
|
220
|
+
client_auth_method: options.client_auth_method
|
221
|
+
)
|
222
|
+
|
223
|
+
verify_id_token!(@access_token.id_token) if configured_response_type == 'code'
|
224
|
+
|
225
|
+
@access_token
|
201
226
|
end
|
202
227
|
|
203
228
|
def decode_id_token(id_token)
|
@@ -233,20 +258,20 @@ module OmniAuth
|
|
233
258
|
|
234
259
|
def session
|
235
260
|
return {} if @env.nil?
|
261
|
+
|
236
262
|
super
|
237
263
|
end
|
238
264
|
|
239
265
|
def key_or_secret
|
240
266
|
case options.client_signing_alg
|
241
267
|
when :HS256, :HS384, :HS512
|
242
|
-
|
268
|
+
client_options.secret
|
243
269
|
when :RS256, :RS384, :RS512
|
244
270
|
if options.client_jwk_signing_key
|
245
|
-
|
271
|
+
parse_jwk_key(options.client_jwk_signing_key)
|
246
272
|
elsif options.client_x509_signing_key
|
247
|
-
|
273
|
+
parse_x509_key(options.client_x509_signing_key)
|
248
274
|
end
|
249
|
-
else
|
250
275
|
end
|
251
276
|
end
|
252
277
|
|
@@ -256,24 +281,24 @@ module OmniAuth
|
|
256
281
|
|
257
282
|
def parse_jwk_key(key)
|
258
283
|
json = JSON.parse(key)
|
259
|
-
if json.
|
260
|
-
|
261
|
-
|
262
|
-
JSON::JWK.new json
|
263
|
-
end
|
284
|
+
return JSON::JWK::Set.new(json['keys']) if json.key?('keys')
|
285
|
+
|
286
|
+
JSON::JWK.new(json)
|
264
287
|
end
|
265
288
|
|
266
289
|
def decode(str)
|
267
|
-
UrlSafeBase64.decode64(str).
|
290
|
+
UrlSafeBase64.decode64(str).unpack1('B*').to_i(2).to_s
|
268
291
|
end
|
269
292
|
|
270
293
|
def redirect_uri
|
271
294
|
return client_options.redirect_uri unless params['redirect_uri']
|
295
|
+
|
272
296
|
"#{ client_options.redirect_uri }?redirect_uri=#{ CGI.escape(params['redirect_uri']) }"
|
273
297
|
end
|
274
298
|
|
275
299
|
def encoded_post_logout_redirect_uri
|
276
300
|
return unless options.post_logout_redirect_uri
|
301
|
+
|
277
302
|
URI.encode_www_form(
|
278
303
|
post_logout_redirect_uri: options.post_logout_redirect_uri
|
279
304
|
)
|
@@ -288,13 +313,45 @@ module OmniAuth
|
|
288
313
|
@logout_path_pattern ||= %r{\A#{Regexp.quote(request_path)}(/logout)}
|
289
314
|
end
|
290
315
|
|
316
|
+
def id_token_callback_phase
|
317
|
+
user_data = decode_id_token(params['id_token']).raw_attributes
|
318
|
+
env['omniauth.auth'] = AuthHash.new(
|
319
|
+
provider: name,
|
320
|
+
uid: user_data['sub'],
|
321
|
+
info: { name: user_data['name'], email: user_data['email'] },
|
322
|
+
extra: { raw_info: user_data }
|
323
|
+
)
|
324
|
+
call_app!
|
325
|
+
end
|
326
|
+
|
327
|
+
def valid_response_type?
|
328
|
+
return true if params.key?(configured_response_type)
|
329
|
+
|
330
|
+
error_attrs = RESPONSE_TYPE_EXCEPTIONS[configured_response_type]
|
331
|
+
fail!(error_attrs[:key], error_attrs[:exception_class].new(params['error']))
|
332
|
+
|
333
|
+
false
|
334
|
+
end
|
335
|
+
|
336
|
+
def configured_response_type
|
337
|
+
@configured_response_type ||= options.response_type.to_s
|
338
|
+
end
|
339
|
+
|
340
|
+
def verify_id_token!(id_token)
|
341
|
+
return unless id_token
|
342
|
+
|
343
|
+
decode_id_token(id_token).verify!(issuer: options.issuer,
|
344
|
+
client_id: client_options.identifier,
|
345
|
+
nonce: stored_nonce)
|
346
|
+
end
|
347
|
+
|
291
348
|
class CallbackError < StandardError
|
292
349
|
attr_accessor :error, :error_reason, :error_uri
|
293
350
|
|
294
|
-
def initialize(
|
295
|
-
self.error = error
|
296
|
-
self.error_reason =
|
297
|
-
self.error_uri =
|
351
|
+
def initialize(data)
|
352
|
+
self.error = data[:error]
|
353
|
+
self.error_reason = data[:reason]
|
354
|
+
self.error_uri = data[:uri]
|
298
355
|
end
|
299
356
|
|
300
357
|
def message
|