doorkeeper-openid_connect 1.9.0 → 1.10.5
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/CHANGELOG.md +81 -0
- data/README.md +13 -242
- data/app/controllers/concerns/doorkeeper/openid_connect/authorizations_extension.rb +7 -1
- data/app/controllers/doorkeeper/openid_connect/discovery_controller.rb +19 -30
- data/app/controllers/doorkeeper/openid_connect/dynamic_client_registration_controller.rb +59 -18
- data/config/locales/en.yml +5 -0
- data/lib/doorkeeper/oauth/id_token_response.rb +2 -2
- data/lib/doorkeeper/oauth/id_token_token_response.rb +2 -1
- data/lib/doorkeeper/openid_connect/claims/claim.rb +21 -9
- data/lib/doorkeeper/openid_connect/claims_builder.rb +4 -3
- data/lib/doorkeeper/openid_connect/config.rb +21 -9
- data/lib/doorkeeper/openid_connect/engine.rb +1 -1
- data/lib/doorkeeper/openid_connect/errors.rb +15 -1
- data/{app/controllers/concerns → lib}/doorkeeper/openid_connect/grant_types_supported_mixin.rb +1 -1
- data/lib/doorkeeper/openid_connect/helpers/controller/error_response.rb +45 -0
- data/lib/doorkeeper/openid_connect/helpers/controller/max_age.rb +69 -0
- data/lib/doorkeeper/openid_connect/helpers/controller/prompt.rb +113 -0
- data/lib/doorkeeper/openid_connect/helpers/controller/token_matching.rb +59 -0
- data/lib/doorkeeper/openid_connect/helpers/controller.rb +65 -121
- data/lib/doorkeeper/openid_connect/id_token.rb +43 -15
- data/lib/doorkeeper/openid_connect/id_token_token.rb +11 -4
- data/lib/doorkeeper/openid_connect/oauth/authorization/code.rb +1 -1
- data/lib/doorkeeper/openid_connect/oauth/authorization_code_request.rb +10 -5
- data/lib/doorkeeper/openid_connect/oauth/dynamic_registration_request.rb +142 -0
- data/lib/doorkeeper/openid_connect/oauth/password_access_token_request.rb +5 -3
- data/lib/doorkeeper/openid_connect/oauth/pre_authorization.rb +2 -2
- data/lib/doorkeeper/openid_connect/oauth/token_response.rb +5 -1
- data/lib/doorkeeper/openid_connect/orm/active_record/access_grant.rb +4 -4
- data/lib/doorkeeper/openid_connect/orm/active_record/mixins/openid_request.rb +14 -2
- data/lib/doorkeeper/openid_connect/orm/active_record/request.rb +1 -1
- data/lib/doorkeeper/openid_connect/orm/active_record.rb +42 -37
- data/lib/doorkeeper/openid_connect/rails/routes/mapping.rb +5 -5
- data/lib/doorkeeper/openid_connect/rails/routes.rb +11 -11
- data/lib/doorkeeper/openid_connect/token_endpoint_auth_methods_supported_mixin.rb +18 -0
- data/lib/doorkeeper/openid_connect/user_info.rb +7 -4
- data/lib/doorkeeper/openid_connect/version.rb +2 -2
- data/lib/doorkeeper/openid_connect.rb +139 -48
- data/lib/doorkeeper/request/id_token.rb +1 -1
- data/lib/doorkeeper/request/id_token_token.rb +1 -1
- data/lib/generators/doorkeeper/openid_connect/install_generator.rb +6 -5
- data/lib/generators/doorkeeper/openid_connect/migration_generator.rb +9 -9
- data/lib/generators/doorkeeper/openid_connect/templates/initializer.rb +109 -4
- metadata +22 -13
- data/lib/doorkeeper/openid_connect/claims/aggregated_claim.rb +0 -11
- data/lib/doorkeeper/openid_connect/claims/distributed_claim.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc9243cc386621bb9b9474617c885f398632d60fdeabc7ecb50deab1364bb787
|
|
4
|
+
data.tar.gz: e33f32f4992a35ec43762c4c5c60d0ba6e98bb8b4f7c60c8cd0d4d69b4c1f535
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca8e15dde2fb57d5badf44ddb67f5109eb7b419e92ae33b4f7c3bee6cc998ae4ec50a31bc57aefae40578c5c094a85ac5cfc8cabdbbd4a1c77316fcc7651faf4
|
|
7
|
+
data.tar.gz: 7ac0048a88dc1968427dbb67ab814e85ebc361d3210d2d6d6bf29e52bd4f09fd94f6eb9792ab1c495ca7ef9323a9d0ca29d43aba8c13e6658cfe97c17f2a2230
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,87 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
## v1.10.5 (2026-07-09)
|
|
4
|
+
|
|
5
|
+
- [#329] Restore compatibility with the full `doorkeeper >= 5.5` range declared in the gemspec ([#328](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/328)). Doorkeeper 5.5.x raised `NameError: uninitialized constant Doorkeeper::Orm::ActiveRecord::Mixins` at load time (the mixin file ships since 5.5.0 but is only autoloadable since 5.6.0 — it is now required explicitly when absent), and Doorkeeper 5.6/5.7 broke the discovery endpoint because `pkce_code_challenge_methods` only exists since 5.8.0 (the discovery response now falls back to `plain S256`, which is what those versions accept). CI now exercises the oldest supported Doorkeeper series via `gemfiles/doorkeeper_5.{5,6,7}.gemfile`
|
|
3
6
|
- Please add here
|
|
7
|
+
- [#325] Remove the dead `Claims::AggregatedClaim` and `Claims::DistributedClaim` classes. They have never been required, instantiated, or reachable through the claims DSL (`ClaimsBuilder` only builds `NormalClaim`), and the discovery document only advertises `claim_types_supported: ["normal"]` — the files just shipped in the gem unused since 2016
|
|
8
|
+
- [#324] Split `Helpers::Controller` (294 lines, one module) into focused submodules — `Prompt`, `MaxAge`, `ErrorResponse`, `TokenMatching` — and decompose the two most complex methods (`handle_oidc_prompt_param!`, `handle_oidc_max_age_param!`). This is a behavior-preserving refactor that allows dropping the `Metrics/ModuleLength`, `Metrics/CyclomaticComplexity` and `Metrics/PerceivedComplexity` overrides from `.rubocop_todo.yml`; the codebase now passes those cops at RuboCop's default thresholds
|
|
9
|
+
- [#326] Make the dynamic client registration spec robust against pre-existing `Doorkeeper::Application` rows: replace absolute `Application.count` assertions and `Application.first` lookups with relative `change` matchers and `Application.find_by(uid:)` lookups keyed on the `client_id` returned by each request. With a polluted `test.sqlite3` (e.g. left behind by a `rake server` session against the same dummy app) 12 of the 20 examples failed spuriously; they now pass regardless of prior DB state
|
|
10
|
+
- [#327] Document previously-undocumented config options in the generated initializer template: `authorize_dynamic_client_registration` (DCR endpoint authorization gate), `apply_prompt_to_non_oidc_requests`, `end_session_endpoint`, `discovery_url_options`, and the `response:` option for claims (controlling whether a claim appears in the ID Token, UserInfo, or both). No behavior change
|
|
11
|
+
|
|
12
|
+
## v1.10.4 (2026-07-07)
|
|
13
|
+
|
|
14
|
+
- Validate the `scope` supplied to Dynamic Client Registration against the server's configured scopes (`default_scopes` + `optional_scopes`) before persisting it. Unrecognized scopes are silently dropped and the confirmed set is echoed in the registration response (RFC 7591 §2); a request whose scopes are all unsupported is rejected with `invalid_client_metadata` instead of being registered with an empty scope set.
|
|
15
|
+
|
|
16
|
+
## v1.10.3 (2026-06-23)
|
|
17
|
+
|
|
18
|
+
- [#308] Fix `NameError: uninitialized constant Auth::ApplicationRecord` on boot when using a namespaced custom access grant model (e.g. `Auth::OAuthAccessGrant < ApplicationRecord`). Since v1.10.0 ([#241]) the `openid_request` association was wired inside an `ActiveSupport.on_load(:active_record)` block, which fires while `ActiveRecord::Base` is first loaded and constantizes the grant model too early. The association is now added from Doorkeeper's `AccessGrant` mixin `included` callback — at the model's own load time, without constantizing — mirroring the fix doorkeeper made in [#1830](https://github.com/doorkeeper-gem/doorkeeper/pull/1830) ([#306](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/306))
|
|
19
|
+
|
|
20
|
+
## v1.10.2 (2026-06-22)
|
|
21
|
+
|
|
22
|
+
- [#315] Drop support for EOL Ruby 3.1 (EOL 2025-03-25) and require Ruby `>= 3.2`. `i18n 1.15.0` uses the `Fiber[]` storage API which only exists on Ruby 3.2+, so the Ruby 3.1 CI row no longer loads; the matrix now tests Ruby 3.2 as the minimum
|
|
23
|
+
- [#316] Set `fail-fast: false` in CI matrix so a single failing job no longer cancels the rest
|
|
24
|
+
- [#303] execute account selection even without owner, and `select_account_for_resource_owner` can now receive `nil` as the first argument.
|
|
25
|
+
- [#304] allow handle auth_time per grant
|
|
26
|
+
- [#305] Document the `auth_time_from_access_token` config option in the README (per-grant `auth_time`), clarifying that it only affects the ID Token `auth_time` claim and not `max_age` enforcement
|
|
27
|
+
- [#307] Fix `bundle exec rake server` for the test application
|
|
28
|
+
- [#313] Move Configuration documentation from README to Wiki
|
|
29
|
+
- [#312] Raise `Errors::MissingRequiredClaim` instead of silently dropping a blank REQUIRED ID Token claim (`iss`/`sub`/`aud`/`exp`/`iat`) in `IdToken#as_json`, which previously could emit a non-conformant ID Token (OIDC Core 1.0 §2). OPTIONAL claims such as `nonce`/`auth_time` are still omitted when blank
|
|
30
|
+
- [#311] Include the REQUIRED `client_secret_expires_at` member (value `0`, never expires) in the Dynamic Client Registration response whenever a `client_secret` is issued (RFC 7591 §3.2.1 / OpenID Connect Dynamic Client Registration 1.0 §3.2)
|
|
31
|
+
- [#309] Add a browser dashboard to the test application (`spec/dummy`) for exercising the OpenID Connect endpoints by hand — replacing the rails console + curl workflow with forms for Setup, Discovery, Authorization (code / implicit / PKCE / nonce / prompt / `max_age`), token exchange, UserInfo, introspection and revocation
|
|
32
|
+
|
|
33
|
+
## v1.10.1 (2026-06-03)
|
|
34
|
+
|
|
35
|
+
- [#294] Drop stale `Metrics/ClassLength` and `Metrics/BlockLength` overrides from `.rubocop_todo.yml`
|
|
36
|
+
- [#293] Drop `Naming/VariableNumber` from `.rubocop_todo.yml` and normalise test variable names
|
|
37
|
+
- [#291] Document multi-namespace mount pattern for multiple resource owner models ([#192](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/192))
|
|
38
|
+
- [#292] Drop formatting cops from `.rubocop_todo.yml` and align trailing-comma style with upstream doorkeeper
|
|
39
|
+
- [#296] Fix the `prompt` parameter being rejected with `invalid_request` when it contains leading or duplicate spaces (e.g. `prompt=%20none`) — blank entries in the space-delimited value are now ignored
|
|
40
|
+
- [#299] Raise `InvalidConfiguration` when the `issuer` config resolves to a blank value instead of silently advertising an empty `issuer` in the discovery document. Since v1.10.0 an arity-2 `issuer` block receives `(resource_owner, application)` — both `nil` in the discovery context — so a block relying on the old v1.9.0 request argument could return `nil` and produce a discovery `issuer` that mismatched the ID token `iss` ([#298](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/298))
|
|
41
|
+
|
|
42
|
+
## v1.10.0 (2026-06-01)
|
|
43
|
+
|
|
44
|
+
>[!IMPORTANT]
|
|
45
|
+
>
|
|
46
|
+
>- **Breaking (arity-2 issuer blocks):** `resolve_issuer` now dispatches arity-2 blocks with `(resource_owner, application)` in all contexts, including discovery. In v1.9.0 `DiscoveryController` passed `request` as the first argument; existing arity-2 blocks that relied on this receive `(nil, nil)` in v1.10.0 and should migrate to arity-3 — see [#298](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/298) for details and migration examples
|
|
47
|
+
|
|
48
|
+
- [#241] Fix NameError on doorkeeper master by deferring AR model loading in run_hooks (see [Doorkeeper PR](https://github.com/doorkeeper-gem/doorkeeper/pull/1804))
|
|
49
|
+
- [#242] Fix `NoMethodError` for openid_request in testing environments.
|
|
50
|
+
- [#246] Fix `at_hash` to use correct hash algorithm based on `signing_algorithm`
|
|
51
|
+
- [#250] Return configured `issuer` instead of `root_url` in WebFinger response (thanks to @sato11 for the original work in #172)
|
|
52
|
+
- [#248] Fix `max_age` always triggering reauthentication when `auth_time_from_resource_owner` returns Integer
|
|
53
|
+
- [#254] **Breaking:** Omit `expires_in` from the `response_type=id_token` response (OIDC Core §3.2.2.5 — `expires_in` represents the Access Token lifetime; it is still returned for `response_type=id_token token`)
|
|
54
|
+
- [#252] Treat `auth_time_from_resource_owner` as optional in `IdToken` — omit `auth_time` claim when unconfigured instead of raising `InvalidConfiguration`
|
|
55
|
+
- [#256] Accept non-callable values (symbol / string) for the `protocol` config option, matching the pattern used by `issuer` / `signing_algorithm` / `signing_key` / `expiration`
|
|
56
|
+
- [#258] Skip `IdToken` construction on password grants without the `openid` scope
|
|
57
|
+
- [#259] Skip `IdToken` construction on authorization code grants without the `openid` scope
|
|
58
|
+
- [#261] Fix obsolete RuboCop configuration (`require:` → `plugins:`, `RSpec/FilePath` split, remove `Capybara/FeatureMethods`)
|
|
59
|
+
- [#263] **Security/Breaking:** Determine dynamically registered client's `confidential` flag from `token_endpoint_auth_method` per RFC 7591 — previously every dynamically registered client was created as public (`confidential: false`), which let callers authenticate with only `client_id` (`by_uid_and_secret(uid, nil)` bypass). Default is now `client_secret_basic` (confidential); `none` produces a public client; unsupported values (e.g. `private_key_jwt`) are rejected with `invalid_client_metadata`. Also derive `token_endpoint_auth_methods_supported` in the response from `Doorkeeper.configuration.client_credentials_methods` instead of a hardcoded list, matching #236
|
|
60
|
+
- [#264] Apply safe RuboCop autocorrections and fix resulting artifacts
|
|
61
|
+
- [#265] Add Dynamic Client Registration section to README
|
|
62
|
+
- [#266] Validate `application_type`, `response_types`, and `grant_types` parameters in dynamic client registration per RFC 7591 — reject unsupported values with `invalid_client_metadata` and echo the requested values back in the registration response, instead of silently ignoring them and returning the server's global configuration
|
|
63
|
+
- [#267] Add `authorize_dynamic_client_registration` config option to gate the dynamic client registration endpoint per RFC 7591 §3.1 — when set to a callable, the block is evaluated in the controller scope (with access to `request`, `params`, `request.headers`, etc.) and falsy return values reject the request with `401 invalid_token`. Default is `nil` so the endpoint remains open for backward compatibility; consumers should configure this to validate an Initial Access Token (or any other authorization scheme) before allowing client registration
|
|
64
|
+
- [#268] Update Dynamic Client Registration README for validated metadata parameters
|
|
65
|
+
- [#269] Document `authorize_dynamic_client_registration` in README
|
|
66
|
+
- [#270] Document the unified issuer block signature in README
|
|
67
|
+
- [#278] Test against Ruby 4.0.
|
|
68
|
+
- [#271] **Security:** Add `auth_time_from_session` config for per-session `max_age` enforcement. The legacy `auth_time_from_resource_owner` cannot distinguish between concurrent sessions and is now deprecated for `max_age` use (see [#150](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/150))
|
|
69
|
+
- [#272] Document `auth_time_from_session` in README (follow-up to [#271](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/pull/271))
|
|
70
|
+
- [#273] **Security/Hardening:** Merge framework-controlled registered claims last — `iss`/`sub`/`aud`/`exp`/`iat`/`nonce`/`auth_time` for the ID Token and `sub` for UserInfo — so a custom claim block can no longer override security-critical values. No legitimate configuration relied on this; custom claims that intentionally shadowed a registered claim name will now be ignored for that key (OIDC Core §2 / §3.1.3.7 / §5.3.2).
|
|
71
|
+
- [#276] Get RuboCop to zero offenses: fix `Lint/MissingSuper` in `IdTokenResponse`, replace `puts` with `warn` for deprecation notices, and modernise spec style
|
|
72
|
+
- [#277] Fix README inaccuracies (`signing_algorithm` description and link, `discovery_url_options` endpoint list, `oauth-authorization-server` route) and use constant-time comparison in the DCR authorization example to prevent timing attacks on the Initial Access Token
|
|
73
|
+
- [#279] Return `account_selection_required` when a `prompt=select_account` handler does not generate a response, per [OIDC Core 1.0 §3.1.2.6](https://openid.net/specs/openid-connect-core-1_0.html#AuthError) — previously the authorization silently continued without account selection. Adds the missing `Errors::AccountSelectionRequired` class, mirroring the existing `login_required` backstop for `reauthenticate_resource_owner`
|
|
74
|
+
- [#275] Return `login_required` for `max_age` reauthentication when `prompt=none`, instead of triggering the interactive `reauthenticate_resource_owner` flow (OIDC Core §3.1.2.1)
|
|
75
|
+
- [#284] Document `acr` / `amr` claims in README — show how to expose Authentication Context Class Reference and Authentication Methods References via the `claim` DSL, with callouts for the `response:` and `scope:` defaults that silently bite
|
|
76
|
+
- [#288] Document `offline_access` scope recipe in README — show how to wire `use_refresh_token` with scope-based filtering for OIDC offline access
|
|
77
|
+
- [#281] Fix `NoMethodError` / `DoubleRenderError` when `resource_owner_authenticator` redirects with a truthy non-model value (e.g. `current_user || redirect_to(login_url)`). Normalize the leaked value to `nil` when `performed?` and add missing `if owner` guard on `select_account`.
|
|
78
|
+
- [#285] Document custom `jwks_uri` path pattern in README — show how to advertise a non-default path in the discovery document using Rails' `direct` URL helper
|
|
79
|
+
- [#283] Support multiple signing keys in the JWKS response — `signing_key` now also accepts an array (and callables returning an array). The first entry is the active key used to sign new ID tokens; the remaining entries are published in the JWKS so clients can still validate tokens signed with a retired key during a rotation window. Single-value and callable forms continue to work unchanged
|
|
80
|
+
- [#286] Allow claims to be assigned to multiple scopes via `scope: [:profile, :all_data]` — the claim is returned whenever the access token grants any of the listed scopes. **Note:** the previously implicit `Claim#scope=` writer (from `attr_accessor :scope`) is no longer provided; rebuild the claim instead of mutating it
|
|
81
|
+
- [#287] Add `apply_prompt_to_non_oidc_requests` option to honor the `prompt` parameter on plain OAuth requests that do not include the `openid` scope
|
|
82
|
+
- [#282] Allow `prompt=none` reauthorization with a narrower subset of previously-granted scopes (issue #63). Per RFC 6749 §1.5, narrower-or-equal scopes do not require fresh user consent; previously these requests returned `consent_required`.
|
|
83
|
+
- [#290] Freeze `Claim#scopes` and `Claim#response` arrays at construction so callers can't accidentally mutate the claim's internal state from outside
|
|
84
|
+
- [#297] Fix the generated initializer's `issuer` example referencing an undefined `request` local (the block parameter is `_request`), which raised `NameError` when copied verbatim
|
|
4
85
|
|
|
5
86
|
## v1.9.0 (2026-03-16)
|
|
6
87
|
|
data/README.md
CHANGED
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
[](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper-openid_connect)
|
|
5
5
|
[](https://rubygems.org/gems/doorkeeper-openid_connect)
|
|
6
6
|
|
|
7
|
-
#### :warning: **This project is looking for maintainers, see [this issue](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/89).**
|
|
8
|
-
|
|
9
7
|
This library implements an [OpenID Connect](http://openid.net/connect/) authentication provider for Rails applications on top of the [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) OAuth 2.0 framework.
|
|
10
8
|
|
|
11
9
|
OpenID Connect is a single-sign-on and identity layer with a [growing list of server and client implementations](http://openid.net/developers/libraries/). If you're looking for a client in Ruby check out [omniauth_openid_connect](https://github.com/m0n9oose/omniauth_openid_connect/).
|
|
@@ -16,12 +14,7 @@ OpenID Connect is a single-sign-on and identity layer with a [growing list of se
|
|
|
16
14
|
- [Known Issues](#known-issues)
|
|
17
15
|
- [Example Applications](#example-applications)
|
|
18
16
|
- [Installation](#installation)
|
|
19
|
-
- [Configuration](
|
|
20
|
-
- [Scopes](#scopes)
|
|
21
|
-
- [Claims](#claims)
|
|
22
|
-
- [Routes](#routes)
|
|
23
|
-
- [Nonces](#nonces)
|
|
24
|
-
- [Internationalization (I18n)](#internationalization-i18n)
|
|
17
|
+
- [Configuration](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Configuration)
|
|
25
18
|
- [Development](#development)
|
|
26
19
|
- [License](#license)
|
|
27
20
|
- [Sponsors](#sponsors)
|
|
@@ -35,8 +28,9 @@ The following parts of [OpenID Connect Core 1.0](http://openid.net/specs/openid-
|
|
|
35
28
|
- [UserInfo Endpoint](http://openid.net/specs/openid-connect-core-1_0.html#UserInfo)
|
|
36
29
|
- [Normal Claims](http://openid.net/specs/openid-connect-core-1_0.html#NormalClaims)
|
|
37
30
|
- [OAuth 2.0 Form Post Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html)
|
|
31
|
+
- [OAuth 2.0 Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591)
|
|
38
32
|
|
|
39
|
-
In addition we also support most of [OpenID Connect Discovery 1.0](http://openid.net/specs/openid-connect-discovery-1_0.html) for automatic configuration discovery.
|
|
33
|
+
In addition, we also support most of [OpenID Connect Discovery 1.0](http://openid.net/specs/openid-connect-discovery-1_0.html) for automatic configuration discovery.
|
|
40
34
|
|
|
41
35
|
Take a look at the [DiscoveryController](app/controllers/doorkeeper/openid_connect/discovery_controller.rb) for more details on supported features.
|
|
42
36
|
|
|
@@ -72,242 +66,19 @@ rails generate doorkeeper:openid_connect:migration
|
|
|
72
66
|
rake db:migrate
|
|
73
67
|
```
|
|
74
68
|
|
|
75
|
-
If you're upgrading from an earlier version, check [
|
|
69
|
+
If you're upgrading from an earlier version, check [Migration from Old Versions](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Migration-from-Old-Versions)
|
|
70
|
+
wiki and [CHANGELOG.md](CHANGELOG.md) for upgrade instructions.
|
|
76
71
|
|
|
77
72
|
## Configuration
|
|
78
73
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Verify your settings in `config/initializers/doorkeeper.rb`:
|
|
82
|
-
|
|
83
|
-
- `resource_owner_authenticator`
|
|
84
|
-
- This callback needs to returns a falsey value if the current user can't be determined:
|
|
85
|
-
|
|
86
|
-
```ruby
|
|
87
|
-
resource_owner_authenticator do
|
|
88
|
-
if current_user
|
|
89
|
-
current_user
|
|
90
|
-
else
|
|
91
|
-
redirect_to(new_user_session_url)
|
|
92
|
-
nil
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
```
|
|
96
|
-
- `grant_flows`
|
|
97
|
-
- If you want to use `id_token` or `id_token token` response types you need to add `implicit_oidc` to `grant_flows`:
|
|
98
|
-
|
|
99
|
-
```ruby
|
|
100
|
-
grant_flows %w(authorization_code implicit_oidc)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
The following settings are required in `config/initializers/doorkeeper_openid_connect.rb`:
|
|
104
|
-
|
|
105
|
-
- `issuer`
|
|
106
|
-
- Identifier for the issuer of the response (i.e. your application URL). The value is a case sensitive URL using the `https` scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components.
|
|
107
|
-
- You can either pass a string value, or a block to generate the issuer dynamically based on the `resource_owner` and `application` or [request](app/controllers/doorkeeper/openid_connect/discovery_controller.rb#L123) passed to the block.
|
|
108
|
-
- `subject`
|
|
109
|
-
- Identifier for the resource owner (i.e. the authenticated user). A locally unique and never reassigned identifier within the issuer for the end-user, which is intended to be consumed by the client. The value is a case-sensitive string and must not exceed 255 ASCII characters in length.
|
|
110
|
-
- The database ID of the user is an acceptable choice if you don't mind leaking that information.
|
|
111
|
-
- If you want to provide a different subject identifier to each client, use [pairwise subject identifier](http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes) with configurations like below.
|
|
112
|
-
|
|
113
|
-
```ruby
|
|
114
|
-
# config/initializers/doorkeeper_openid_connect.rb
|
|
115
|
-
Doorkeeper::OpenidConnect.configure do
|
|
116
|
-
# ...
|
|
117
|
-
subject_types_supported [:pairwise]
|
|
118
|
-
|
|
119
|
-
subject do |resource_owner, application|
|
|
120
|
-
Digest::SHA256.hexdigest("#{resource_owner.id}#{URI.parse(application.redirect_uri).host}#{'your_secret_salt'}")
|
|
121
|
-
end
|
|
122
|
-
# ...
|
|
123
|
-
end
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
- `signing_key`
|
|
127
|
-
- Private key to be used for [JSON Web Signature](https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31).
|
|
128
|
-
- You can generate a private key with the `openssl` command, see e.g. [Generate an RSA keypair using OpenSSL](https://en.wikibooks.org/wiki/Cryptography/Generate_a_keypair_using_OpenSSL).
|
|
129
|
-
- You should not commit the key to your repository, but use an external file (in combination with `File.read`) and/or the [dotenv-rails](https://github.com/bkeepers/dotenv) gem (in combination with `ENV[...]`).
|
|
130
|
-
- `signing_algorithm`
|
|
131
|
-
- The encryption type of the private key which defaults to `:rs256`. The list of supported algorithms can be found [here](https://github.com/nov/json-jwt/wiki/JWE#supported-algorithms)
|
|
132
|
-
- `resource_owner_from_access_token`
|
|
133
|
-
- Defines how to translate the Doorkeeper access token to a resource owner model.
|
|
134
|
-
|
|
135
|
-
The following settings are optional, but recommended for better client compatibility:
|
|
136
|
-
|
|
137
|
-
- `auth_time_from_resource_owner`
|
|
138
|
-
- Returns the time of the user's last login, this can be a `Time`, `DateTime`, or any other class that responds to `to_i`
|
|
139
|
-
- Required to support the `max_age` parameter and the `auth_time` claim.
|
|
140
|
-
- `reauthenticate_resource_owner`
|
|
141
|
-
- Defines how to trigger reauthentication for the current user (e.g. display a password prompt, or sign-out the user and redirect to the login form).
|
|
142
|
-
- Required to support the `max_age` and `prompt=login` parameters.
|
|
143
|
-
- The block is executed in the controller's scope, so you have access to methods like `params`, `redirect_to` etc.
|
|
144
|
-
- `select_account_for_resource_owner`
|
|
145
|
-
- Defines how to trigger account selection to choose the current login user.
|
|
146
|
-
- Required to support the `prompt=select_account` parameter.
|
|
147
|
-
- The block is executed in the controller's scope, so you have access to methods like `params`, `redirect_to` etc.
|
|
148
|
-
|
|
149
|
-
The following settings are optional:
|
|
150
|
-
|
|
151
|
-
- `expiration`
|
|
152
|
-
- Expiration time after which the ID Token must not be accepted for processing by clients.
|
|
153
|
-
- The default is 120 seconds, it can be configured using a value or block.
|
|
154
|
-
```ruby
|
|
155
|
-
# config/initializers/doorkeeper_openid_connect.rb
|
|
156
|
-
Doorkeeper::OpenidConnect.configure do
|
|
157
|
-
# ...
|
|
158
|
-
expiration do |resource_owner, application|
|
|
159
|
-
# You will have to ensure the application model implements an expiration method
|
|
160
|
-
application.expiration
|
|
161
|
-
end
|
|
162
|
-
# ...
|
|
163
|
-
end
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
- `protocol`
|
|
167
|
-
- The protocol to use when generating URIs for the discovery endpoints.
|
|
168
|
-
- The default is `https` for production, and `http` for all other environments
|
|
169
|
-
- Note that the OIDC specification mandates HTTPS, so you shouldn't change this
|
|
170
|
-
for production environments unless you have a really good reason!
|
|
171
|
-
|
|
172
|
-
- `end_session_endpoint`
|
|
173
|
-
- The URL that the user is redirected to after ending the session on the client.
|
|
174
|
-
- Used by implementations like https://github.com/IdentityModel/oidc-client-js.
|
|
175
|
-
- The block is executed in the controller's scope, so you have access to your route helpers.
|
|
176
|
-
|
|
177
|
-
- `discovery_url_options`
|
|
178
|
-
- The URL options for every available endpoint to use when generating the endpoint URL in the
|
|
179
|
-
discovery response. Available endpoints: `authorization`, `token`, `revocation`,
|
|
180
|
-
`introspection`, `userinfo`, `jwks`, `webfinger`.
|
|
181
|
-
- This option requires option keys with an available endpoint and
|
|
182
|
-
[URL options](https://api.rubyonrails.org/v6.0.3.3/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for)
|
|
183
|
-
as value.
|
|
184
|
-
- The default is to use the request host, just like all the other URLs in the discovery response.
|
|
185
|
-
- This is useful when you want endpoints to use a different URL than other requests.
|
|
186
|
-
For example, if your Doorkeeper server is behind a firewall with other servers, you might want
|
|
187
|
-
other servers to use an "internal" URL to communicate with Doorkeeper, but you want to present
|
|
188
|
-
an "external" URL to end-users for authentication requests. Note that this setting does not
|
|
189
|
-
actually change the URL that your Doorkeeper server responds on - that is outside the scope of
|
|
190
|
-
Doorkeeper.
|
|
191
|
-
|
|
192
|
-
```ruby
|
|
193
|
-
# config/initializers/doorkeeper_openid_connect.rb
|
|
194
|
-
Doorkeeper::OpenidConnect.configure do
|
|
195
|
-
# ...
|
|
196
|
-
discovery_url_options do |request|
|
|
197
|
-
{
|
|
198
|
-
authorization: { host: 'host.example.com' },
|
|
199
|
-
jwks: { protocol: request.ssl? ? :https : :http }
|
|
200
|
-
}
|
|
201
|
-
end
|
|
202
|
-
# ...
|
|
203
|
-
end
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Scopes
|
|
207
|
-
|
|
208
|
-
To perform authentication over OpenID Connect, an OAuth client needs to request the `openid` scope. This scope needs to be enabled using either `optional_scopes` in the global Doorkeeper configuration in `config/initializers/doorkeeper.rb`, or by adding it to any OAuth application's `scope` attribute.
|
|
209
|
-
|
|
210
|
-
> Note that any application defining its own scopes won't inherit the scopes defined in the initializer, so you might have to update existing applications as well.
|
|
211
|
-
>
|
|
212
|
-
> See [Using Scopes](https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes) in the Doorkeeper wiki for more information.
|
|
213
|
-
|
|
214
|
-
### Claims
|
|
215
|
-
|
|
216
|
-
Claims can be defined in a `claims` block inside `config/initializers/doorkeeper_openid_connect.rb`:
|
|
217
|
-
|
|
218
|
-
```ruby
|
|
219
|
-
Doorkeeper::OpenidConnect.configure do
|
|
220
|
-
claims do
|
|
221
|
-
claim :email do |resource_owner|
|
|
222
|
-
resource_owner.email
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
claim :full_name do |resource_owner|
|
|
226
|
-
"#{resource_owner.first_name} #{resource_owner.last_name}"
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
claim :preferred_username, scope: :openid do |resource_owner, scopes, access_token|
|
|
230
|
-
# Pass the resource_owner's preferred_username if the application has
|
|
231
|
-
# `profile` scope access. Otherwise, provide a more generic alternative.
|
|
232
|
-
scopes.exists?(:profile) ? resource_owner.preferred_username : "summer-sun-9449"
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
claim :groups, response: [:id_token, :user_info] do |resource_owner|
|
|
236
|
-
resource_owner.groups
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
Each claim block will be passed:
|
|
243
|
-
|
|
244
|
-
- the `resource_owner`, which is the return value of `resource_owner_authenticator` in your initializer
|
|
245
|
-
- the `scopes` granted by the access token, which is an instance of `Doorkeeper::OAuth::Scopes`
|
|
246
|
-
- the `access_token` itself, which is an instance of `Doorkeeper::AccessToken`
|
|
247
|
-
|
|
248
|
-
By default all custom claims are only returned from the `UserInfo` endpoint and not included in the ID token. You can optionally pass a `response:` keyword with one or both of the symbols `:id_token` or `:user_info` to specify where the claim should be returned.
|
|
249
|
-
|
|
250
|
-
You can also pass a `scope:` keyword argument on each claim to specify which OAuth scope should be required to access the claim. If you define any of the defined [Standard Claims](http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) they will by default use their [corresponding scopes](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) (`profile`, `email`, `address` and `phone`), and any other claims will by default use the `profile` scope. Again, to use any of these scopes you need to enable them as described above.
|
|
251
|
-
|
|
252
|
-
### Routes
|
|
253
|
-
|
|
254
|
-
The installation generator will update your `config/routes.rb` to define all required routes:
|
|
255
|
-
|
|
256
|
-
``` ruby
|
|
257
|
-
Rails.application.routes.draw do
|
|
258
|
-
use_doorkeeper_openid_connect
|
|
259
|
-
# your routes
|
|
260
|
-
end
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
This will mount the following routes:
|
|
264
|
-
|
|
265
|
-
```
|
|
266
|
-
GET /oauth/userinfo
|
|
267
|
-
POST /oauth/userinfo
|
|
268
|
-
GET /oauth/discovery/keys
|
|
269
|
-
GET /.well-known/openid-configuration
|
|
270
|
-
GET /.well-known/webfinger
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
With the exception of the hard-coded `/.well-known` paths (see [RFC 5785](https://tools.ietf.org/html/rfc5785)) you can customize routes in the same way as with Doorkeeper, please refer to [this page on their wiki](https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-routes#version--05-1).
|
|
274
|
-
|
|
275
|
-
### Nonces
|
|
276
|
-
|
|
277
|
-
To support clients who send nonces you have to tweak Doorkeeper's authorization view so the parameter is passed on.
|
|
278
|
-
|
|
279
|
-
If you don't already have custom templates, run this generator in your Rails application to add them:
|
|
280
|
-
|
|
281
|
-
```sh
|
|
282
|
-
rails generate doorkeeper:views
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
Then tweak the template as follows:
|
|
286
|
-
|
|
287
|
-
```patch
|
|
288
|
-
--- i/app/views/doorkeeper/authorizations/new.html.erb
|
|
289
|
-
+++ w/app/views/doorkeeper/authorizations/new.html.erb
|
|
290
|
-
@@ -26,6 +26,7 @@
|
|
291
|
-
<%= hidden_field_tag :state, @pre_auth.state %>
|
|
292
|
-
<%= hidden_field_tag :response_type, @pre_auth.response_type %>
|
|
293
|
-
<%= hidden_field_tag :scope, @pre_auth.scope %>
|
|
294
|
-
+ <%= hidden_field_tag :nonce, @pre_auth.nonce %>
|
|
295
|
-
<%= submit_tag t('doorkeeper.authorizations.buttons.authorize'), class: "btn btn-success btn-lg btn-block" %>
|
|
296
|
-
<% end %>
|
|
297
|
-
<%= form_tag oauth_authorization_path, method: :delete do %>
|
|
298
|
-
@@ -34,6 +35,7 @@
|
|
299
|
-
<%= hidden_field_tag :state, @pre_auth.state %>
|
|
300
|
-
<%= hidden_field_tag :response_type, @pre_auth.response_type %>
|
|
301
|
-
<%= hidden_field_tag :scope, @pre_auth.scope %>
|
|
302
|
-
+ <%= hidden_field_tag :nonce, @pre_auth.nonce %>
|
|
303
|
-
<%= submit_tag t('doorkeeper.authorizations.buttons.deny'), class: "btn btn-danger btn-lg btn-block" %>
|
|
304
|
-
<% end %>
|
|
305
|
-
</div>
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Internationalization (I18n)
|
|
74
|
+
See the [wiki](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Configuration) for detailed configuration instructions, including:
|
|
309
75
|
|
|
310
|
-
|
|
76
|
+
- [Scopes](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Scopes)
|
|
77
|
+
- [Claims](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Claims)
|
|
78
|
+
- [Routes](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Routes)
|
|
79
|
+
- [Nonces](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Nonces)
|
|
80
|
+
- [Internationalization (I18n)](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/I18n)
|
|
81
|
+
- [Dynamic Client Registration](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/wiki/Dynamic-Client-Registration)
|
|
311
82
|
|
|
312
83
|
## Development
|
|
313
84
|
|
|
@@ -334,7 +105,7 @@ bundle exec rake server
|
|
|
334
105
|
By default, the latest Rails version is used. To use a specific version run:
|
|
335
106
|
|
|
336
107
|
```
|
|
337
|
-
rails=
|
|
108
|
+
rails=7.2 bundle update
|
|
338
109
|
```
|
|
339
110
|
|
|
340
111
|
## License
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module OpenidConnect
|
|
3
5
|
module AuthorizationsExtension
|
|
4
6
|
private
|
|
5
7
|
|
|
8
|
+
# Whitelist the OIDC `nonce` authorization parameter so Doorkeeper
|
|
9
|
+
# carries it into the PreAuthorization (see
|
|
10
|
+
# Doorkeeper::OpenidConnect::OAuth::PreAuthorization#initialize, which
|
|
11
|
+
# reads `attrs[:nonce]`). Without this the nonce would be dropped and
|
|
12
|
+
# never make it onto the issued ID Token.
|
|
6
13
|
def pre_auth_param_fields
|
|
7
14
|
super.append(:nonce)
|
|
8
15
|
end
|
|
9
16
|
end
|
|
10
17
|
end
|
|
11
18
|
end
|
|
12
|
-
|
|
@@ -5,8 +5,9 @@ module Doorkeeper
|
|
|
5
5
|
class DiscoveryController < ::Doorkeeper::ApplicationMetalController
|
|
6
6
|
include Doorkeeper::Helpers::Controller
|
|
7
7
|
include GrantTypesSupportedMixin
|
|
8
|
+
include TokenEndpointAuthMethodsSupportedMixin
|
|
8
9
|
|
|
9
|
-
WEBFINGER_RELATION =
|
|
10
|
+
WEBFINGER_RELATION = "http://openid.net/specs/connect/1.0/issuer"
|
|
10
11
|
|
|
11
12
|
def provider
|
|
12
13
|
render json: provider_response
|
|
@@ -47,16 +48,16 @@ module Doorkeeper
|
|
|
47
48
|
# TODO: look into doorkeeper-jwt_assertion for these
|
|
48
49
|
# 'client_secret_jwt',
|
|
49
50
|
# 'private_key_jwt'
|
|
50
|
-
token_endpoint_auth_methods_supported: token_endpoint_auth_methods_supported
|
|
51
|
+
token_endpoint_auth_methods_supported: token_endpoint_auth_methods_supported,
|
|
51
52
|
|
|
52
53
|
subject_types_supported: openid_connect.subject_types_supported,
|
|
53
54
|
|
|
54
55
|
id_token_signing_alg_values_supported: [
|
|
55
|
-
::Doorkeeper::OpenidConnect.signing_algorithm
|
|
56
|
+
::Doorkeeper::OpenidConnect.signing_algorithm,
|
|
56
57
|
],
|
|
57
58
|
|
|
58
59
|
claim_types_supported: [
|
|
59
|
-
|
|
60
|
+
"normal",
|
|
60
61
|
|
|
61
62
|
# TODO: support these
|
|
62
63
|
# 'aggregated',
|
|
@@ -79,14 +80,13 @@ module Doorkeeper
|
|
|
79
80
|
doorkeeper.authorization_response_flows.flat_map(&:response_mode_matches).uniq
|
|
80
81
|
end
|
|
81
82
|
|
|
82
|
-
def token_endpoint_auth_methods_supported(doorkeeper)
|
|
83
|
-
mapping = { from_basic: 'client_secret_basic', from_params: 'client_secret_post' }
|
|
84
|
-
doorkeeper.client_credentials_methods.filter_map { |method| mapping[method] }
|
|
85
|
-
end
|
|
86
|
-
|
|
87
83
|
def code_challenge_methods_supported(doorkeeper)
|
|
88
84
|
return unless doorkeeper.access_grant_model.pkce_supported?
|
|
89
85
|
|
|
86
|
+
# Doorkeeper < 5.8 has no `pkce_code_challenge_methods` option and
|
|
87
|
+
# always accepts both methods whenever PKCE is available.
|
|
88
|
+
return %w[plain S256] unless doorkeeper.respond_to?(:pkce_code_challenge_methods)
|
|
89
|
+
|
|
90
90
|
doorkeeper.pkce_code_challenge_methods
|
|
91
91
|
end
|
|
92
92
|
|
|
@@ -96,27 +96,19 @@ module Doorkeeper
|
|
|
96
96
|
links: [
|
|
97
97
|
{
|
|
98
98
|
rel: WEBFINGER_RELATION,
|
|
99
|
-
href:
|
|
100
|
-
}
|
|
101
|
-
]
|
|
99
|
+
href: issuer,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
102
|
}
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
def keys_response
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
keys: [
|
|
110
|
-
signing_key.merge(
|
|
111
|
-
use: 'sig',
|
|
112
|
-
alg: Doorkeeper::OpenidConnect.signing_algorithm
|
|
113
|
-
)
|
|
114
|
-
]
|
|
115
|
-
}
|
|
106
|
+
{ keys: Doorkeeper::OpenidConnect.signing_keys_normalized }
|
|
116
107
|
end
|
|
117
108
|
|
|
118
109
|
def protocol
|
|
119
|
-
Doorkeeper::OpenidConnect.configuration.protocol
|
|
110
|
+
configured = Doorkeeper::OpenidConnect.configuration.protocol
|
|
111
|
+
configured.respond_to?(:call) ? configured.call : configured
|
|
120
112
|
end
|
|
121
113
|
|
|
122
114
|
def discovery_url_options
|
|
@@ -125,19 +117,16 @@ module Doorkeeper
|
|
|
125
117
|
|
|
126
118
|
def discovery_url_default_options
|
|
127
119
|
{
|
|
128
|
-
protocol: protocol
|
|
120
|
+
protocol: protocol,
|
|
129
121
|
}
|
|
130
122
|
end
|
|
131
123
|
|
|
132
124
|
def issuer
|
|
133
|
-
|
|
134
|
-
Doorkeeper::OpenidConnect.configuration.issuer.call(request).to_s
|
|
135
|
-
else
|
|
136
|
-
Doorkeeper::OpenidConnect.configuration.issuer
|
|
137
|
-
end
|
|
125
|
+
Doorkeeper::OpenidConnect.resolve_issuer(request: request)
|
|
138
126
|
end
|
|
139
127
|
|
|
140
|
-
%i[authorization token revocation introspection userinfo jwks
|
|
128
|
+
%i[authorization token revocation introspection userinfo jwks
|
|
129
|
+
dynamic_client_registration].each do |endpoint|
|
|
141
130
|
define_method :"#{endpoint}_url_options" do
|
|
142
131
|
discovery_url_default_options.merge(discovery_url_options[endpoint.to_sym] || {})
|
|
143
132
|
end
|
|
@@ -3,41 +3,82 @@
|
|
|
3
3
|
module Doorkeeper
|
|
4
4
|
module OpenidConnect
|
|
5
5
|
class DynamicClientRegistrationController < ::Doorkeeper::ApplicationMetalController
|
|
6
|
-
|
|
6
|
+
before_action :authorize_dynamic_client_registration!
|
|
7
7
|
|
|
8
8
|
def register
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
registration = OAuth::DynamicRegistrationRequest.new(::Doorkeeper.configuration, params)
|
|
10
|
+
|
|
11
|
+
unless registration.valid?
|
|
12
|
+
render json: registration.error_response, status: :bad_request
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
client = Doorkeeper::Application.create!(application_params(registration))
|
|
17
|
+
render json: registration_response(client, registration), status: :created
|
|
11
18
|
rescue ActiveRecord::RecordInvalid => e
|
|
12
19
|
render json: { error: "invalid_client_params", error_description: e.record.errors.full_messages.join(", ") },
|
|
13
|
-
|
|
20
|
+
status: :bad_request
|
|
14
21
|
end
|
|
15
22
|
|
|
16
23
|
private
|
|
17
24
|
|
|
18
|
-
def
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
def authorize_dynamic_client_registration!
|
|
26
|
+
authorizer = ::Doorkeeper::OpenidConnect.configuration.authorize_dynamic_client_registration
|
|
27
|
+
return if authorizer.nil?
|
|
28
|
+
|
|
29
|
+
return if authorized?(authorizer)
|
|
30
|
+
|
|
31
|
+
response.headers["WWW-Authenticate"] = "Bearer error=\"invalid_token\""
|
|
32
|
+
render json: {
|
|
33
|
+
error: "invalid_token",
|
|
34
|
+
error_description: I18n.t(
|
|
35
|
+
"doorkeeper.openid_connect.errors.messages.dynamic_client_registration_unauthorized",
|
|
36
|
+
),
|
|
37
|
+
}, status: :unauthorized
|
|
25
38
|
end
|
|
26
39
|
|
|
27
|
-
def
|
|
28
|
-
|
|
40
|
+
def authorized?(authorizer)
|
|
41
|
+
if authorizer.respond_to?(:to_proc)
|
|
42
|
+
instance_exec(&authorizer.to_proc)
|
|
43
|
+
elsif authorizer.respond_to?(:call)
|
|
44
|
+
authorizer.call(self)
|
|
45
|
+
else
|
|
46
|
+
authorizer
|
|
47
|
+
end
|
|
48
|
+
end
|
|
29
49
|
|
|
50
|
+
def application_params(registration)
|
|
30
51
|
{
|
|
31
|
-
|
|
52
|
+
name: params[:client_name],
|
|
53
|
+
redirect_uri: params[:redirect_uris] || [],
|
|
54
|
+
scopes: registration.permitted_scopes,
|
|
55
|
+
confidential: registration.confidential_client?,
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def registration_response(doorkeeper_application, registration)
|
|
60
|
+
response = {
|
|
32
61
|
client_id: doorkeeper_application.uid,
|
|
33
62
|
client_id_issued_at: doorkeeper_application.created_at.to_i,
|
|
34
63
|
redirect_uris: doorkeeper_application.redirect_uri.split,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
64
|
+
token_endpoint_auth_method: registration.token_endpoint_auth_method,
|
|
65
|
+
token_endpoint_auth_methods_supported: registration.token_endpoint_auth_methods_supported,
|
|
66
|
+
response_types: registration.requested_response_types,
|
|
67
|
+
grant_types: registration.requested_grant_types,
|
|
38
68
|
scope: doorkeeper_application.scopes.to_s,
|
|
39
|
-
application_type:
|
|
69
|
+
application_type: registration.requested_application_type,
|
|
40
70
|
}
|
|
71
|
+
|
|
72
|
+
if registration.confidential_client?
|
|
73
|
+
response[:client_secret] =
|
|
74
|
+
doorkeeper_application.plaintext_secret || doorkeeper_application.secret
|
|
75
|
+
# RFC 7591 §3.2.1 / OIDC Dynamic Client Registration 1.0 §3.2:
|
|
76
|
+
# client_secret_expires_at is REQUIRED when a client_secret is issued.
|
|
77
|
+
# Doorkeeper secrets never expire, so the value is 0 (no expiration).
|
|
78
|
+
response[:client_secret_expires_at] = 0
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
response
|
|
41
82
|
end
|
|
42
83
|
end
|
|
43
84
|
end
|
data/config/locales/en.yml
CHANGED
|
@@ -21,3 +21,8 @@ en:
|
|
|
21
21
|
reauthenticate_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.reauthenticate_resource_owner missing configuration.'
|
|
22
22
|
select_account_for_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.select_account_for_resource_owner missing configuration.'
|
|
23
23
|
subject_not_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.subject missing configuration.'
|
|
24
|
+
signing_key_not_configured: 'Doorkeeper::OpenidConnect.configure.signing_key must resolve to at least one key.'
|
|
25
|
+
issuer_not_configured: 'Doorkeeper::OpenidConnect.configure.issuer must resolve to a non-blank value.'
|
|
26
|
+
dynamic_client_registration_unauthorized: 'Authorization required for client registration'
|
|
27
|
+
# ID Token claim error messages
|
|
28
|
+
missing_required_claim: 'Required ID Token claim `%{claim}` is missing or blank'
|