Skip to content

Releases: doorkeeper-gem/doorkeeper

v5.9.6

Choose a tag to compare

@nbulaj nbulaj released this 11 Aug 14:29
75a84fe
  • Reject requests that present more than one client identity (e.g. an Authorization: Basic header for one client and a client_id parameter naming another) with an invalid_request error, instead of authenticating the first extracted identity and silently discarding the other one. A client_id sent alongside another authentication method keeps working when it identifies the same client (RFC 7521 §4.2). Like the RFC 6749 §2.3 check released in 5.9.5, this validation does not apply when client_credentials is configured with a callable extractor, since the credentials the remaining extractors would return are never evaluated — the client_credentials option documents that now.

6.0.0.beta2

6.0.0.beta2 Pre-release
Pre-release

Choose a tag to compare

@nbulaj nbulaj released this 10 Aug 14:32
ce535d9

Please make sure you read the Upgrade guides
and changelog below before the update since this version includes breaking changes.

  • [#1865] Revoke the token issued for an authorization code when the code is exchanged more than once, per RFC 6749 §4.1.2 / §10.5. Active when the oauth_access_grants.access_token_id column exists: new installs get it from the generated migration, existing apps can add it with rails generate doorkeeper:grant_reuse_revocation. Closes [#1713].
  • [#1871] [BREAKING] redirect_uri is now compared to the registered redirect URIs with the simple string comparison required by RFC 6749 §3.1.2.3 (the RFC 8252 §7.3 loopback port exception is kept). Clients relying on the previous lenient matching must send the exact registered URI, closes [#1718].
  • [#1874] Fix force_pkce requiring a code_challenge from response types that never issue an authorization code (e.g. token, or an OIDC extension's id_token / id_token token). PKCE (RFC 7636) protects the authorization code exchange, so for code-less response types there is no token-endpoint step where a code_verifier could ever be checked — such requests were rejected over a parameter that cannot be validated. force_pkce now only enforces the challenge for response types that issue a code (code and code-carrying hybrid types such as code id_token).
  • [#1876] Fix: reject a non-string scope parameter (e.g. scope[a]=b, which Rack parses into a Hash) with invalid_request (RFC 6749 §3.3) instead of an unhandled 500. Scopes.from_string now raises Errors::InvalidScopeParameter for a non-string argument — turned into invalid_request by the token endpoint's rescue_from, so every grant type is covered — and the authorization endpoint rejects it up front in pre-authorization validation. The crash was reachable unauthenticated, before client authentication.
  • [#1877] Fix: let the none client authentication strategy match a request whose Authorization header carries a Bearer token. A bearer credential authorizes access to the endpoint itself (e.g. a bearer-protected introspection endpoint per RFC 7662 §2.1, or a revocation request) rather than authenticating the client, so it must not suppress the public-client none strategy when the client identifies itself with a body client_id. Any other non-blank Authorization value — Basic, or a Bearer with no token — is still treated as header-based client authentication and continues to bypass none. The 6.0.0.beta1 workaround of dropping the Authorization header from such a request is no longer needed.
  • [#1878] Fix: the loopback redirect URI exception (RFC 8252 §7.3) now varies by port only, not by userinfo. The port was cleared with URI#port=, which on Ruby >= 4.0 also drops the userinfo, so http://attacker@127.0.0.1/cb matched a registered http://127.0.0.1/cb. The match is now made component-by-component. The destination host is always the loopback interface, so this was not a cross-origin open redirect, and non-loopback hosts were never affected.
  • [#1879] Fix: with reuse_access_token enabled, replaying an authorization code no longer revokes an access token that another grant still shares. The single-use revocation added in [#1865] followed the grant's access_token_id, which a reused token shares across grants, so a replay could collaterally revoke a token another valid session still held. The revocation now skips a token referenced by another grant and only reaches one unique to the replayed code; a token unique to the code is still revoked as before.
  • [#1881] Fix: Doorkeeper::ApplicationsController no longer 500s on create/update/destroy in api_only mode, where ActionController::API provides no flash. Confining flash to the HTML path is not sufficient on its own, because a client that does not name JSON explicitly still negotiates its way into that path — an absent Accept header and a browser-like list such as application/json, text/plain, */* both resolve to text/html, and a bare */* resolves to the first registered format — so api_only mode now pins the response format to JSON.
  • [#1883] Internal: merge CHANGELOG.md with git's union driver, so two pull requests that each add an entry no longer conflict on the line above "Please add here".
  • [#1884] Fix: /oauth/introspect and /oauth/revoke extend the token lookup across both token types when the lookup by token_type_hint finds nothing (RFC 7662 §2.1 / RFC 7009 §2.1), so a wrong hint no longer hides a token the server knows about ([#1882])
  • [#1885] Index oauth_access_grants.access_token_id in the migration templates: since [#1879] the code-replay revocation filters access grants by that column, the "never used to filter queries" premise behind index: false no longer holds.
  • [#1886] Add support for Resource Indicators for OAuth 2.0 (RFC 8707). Clients can include a resource parameter in authorization and token requests to indicate the target protected resource(s). The authorization server validates resource URIs, enforces audience restriction on tokens, and includes aud in introspection responses. Enable by configuring resource_indicator_validator with a callable. Requires new resource columns on access grants and tokens — run rails generate doorkeeper:resource_indicators to add the migration.
  • [#1887] [test] Pin that a requested non-default scope reaches the authorization grant and the exchanged token, and that the authorization strategy shares the controller's pre-authorization — the mismatch reported in [#1576] does not reproduce. Test-only change, closes [#1576].
  • [#1888] Document custom grant flow registration (Doorkeeper::GrantFlow.register) in the README with a SAML 2.0 bearer assertion (RFC 7522) walkthrough, and pin URN-shaped custom grant types with an end-to-end request spec. Docs/test-only change, closes [#764].
  • [#1890] [test] Pin that the authorization endpoint answers invalid_redirect_uri for a client registered without a redirect URI (allow_blank_redirect_uri), whether or not the request supplies one — the behavior required by RFC 6749 §3.1.2.3. Test-only change, closes [#1682].
  • [#1898] Fix: with reuse_access_token enabled, the client_credentials grant no longer reuses a token whose resource differs from the one requested (RFC 8707), so the audience restriction the client asked for is always applied. Follow-up to [#1886].
  • [#1899] Document the client authentication methods registry (Doorkeeper::ClientAuthentication.register) in the README with a walkthrough for registering a custom method, and pin it with an end-to-end request spec. Docs/test-only change, closes [#1894].
  • [#1891] Fix: an authorization request carrying a redirect_uri for a client registered without one (redirect_uri is nil under allow_blank_redirect_uri) now answers invalid_redirect_uri instead of crashing with an unhandled 500.
  • [#1896] Add an opt-in private_key_jwt client authentication method (RFC 7523 / OIDC Core §9, requires the jwt gem >= 2.7) that verifies assertions against the client's published public keys — jwks / jwks_uri attributes you define on your Application model, the latter fetched with an SSRF-hardened HTTP client. The jti replay guard and the fetched-JWKS cache are process-local by default and can be replaced with shared stores via the private_key_jwt_replay_guard / private_key_jwt_jwks_cache config options. Part of [#1875].
  • [#1901] [test] Pin the answered behaviors behind [#984], [#1554], [#1600], [#1663], [#1759] and [#1787] with regression specs — the built-in client authentication methods, the unmodified echo of long state values, DB persistence with custom token generators, refresh token rotation/expiry semantics and the introspection asymmetry. Test-only change, closes those issues along with [#1291], [#1756] and [#1764].
  • [#1902] Fix: requests that omit scope now compute the same default scopes at the authorization and token endpoints (Scopes#common, symmetric). With dynamic scopes enabled, a scope pattern in either default_scopes or the application's scopes grants the matching concrete scope at both endpoints, closes [#1889].
  • [#1903] Fix: revoke_previous_client_credentials_token no longer revokes a client's live access token issued for a different resource ([#1886]), so a client can keep one audience-restricted token per resource server.
  • [#1905] [test] Cover private_key_jwt client authentication on the client_credentials grant, the one flow where an assertion is the client's only credential end to end. Test-only change.

v5.9.5

Choose a tag to compare

@nbulaj nbulaj released this 07 Aug 09:27
4a87582
  • [#1901] Reject requests that authenticate the client with more than one method (RFC 6749 §2.3) with an invalid_request error, instead of silently authenticating with the first method that matched and discarding the other credentials.
  • [#1853] Fix reuse_access_token reusing a token that was created with custom_access_token_attributes values when the new request doesn't specify any custom attributes. Such requests now only match tokens without custom attributes.

v6.0.0.beta1

v6.0.0.beta1 Pre-release
Pre-release

Choose a tag to compare

@nbulaj nbulaj released this 22 Jul 13:25
3de27a9

Please make sure you read the Upgrade guides and changelog below before the update since this version includes breaking changes.

  • [#1816] Fix: redirect unauthorized_client errors per RFC 6749 Section 4.1.2.1; validate redirect_uri before client_supports_grant_flow to prevent open redirect
  • [#1867] Fix: use access_denied instead of invalid_client for resource_owner_authorize_for_client validation per RFC 6749 Section 4.1.2.1. invalid_client is a token endpoint error (Section 5.2), not an authorization endpoint error.
  • [#1838] Add OAuth 2.0 Authorization Server Metadata endpoint (RFC 8414) served at /.well-known/oauth-authorization-server. The response is built from your Doorkeeper configuration and advertises the authorization, token, revocation and (when token introspection is enabled) introspection endpoints, supported scopes, response/grant types and PKCE code challenge methods. Two new config options are available: issuer (defaults to the request base URL) and custom_metadata (a Hash merged into the response, e.g. to advertise an OIDC userinfo_endpoint). The controller/response use the RFC 8414 "Metadata" naming so they don't collide with a future OpenID Connect Discovery (.well-known/openid-configuration) implementation. Endpoints disabled through skip_controllers are omitted from the response instead of raising a route-generation error.
  • [#1839] Send client credentials in the request body (not the query string) in the token endpoint specs, per RFC 6749 §2.3.1. Test-only change: the *_endpoint_url helpers now return a path plus a matching *_endpoint_params builder so flow specs post credentials through the body.
  • [#1840] Introduce a pluggable client authentication registry (RFC 6749 §2.3).
    • New client_authentication config option declares which methods are accepted and in which order. Built-in strategies: client_secret_basic, client_secret_post and none.
    • Custom strategies can be registered with Doorkeeper::ClientAuthentication.register.
    • [BREAKING] Client credentials are no longer read from the query string — send them in the request body or via HTTP Basic. This applies to every endpoint that authenticates clients: token, revocation and introspection.
    • [BREAKING] The none strategy rejects requests that carry a non-blank Authorization header. A common casualty is a public client POSTing to /oauth/revoke with a body client_id while still sending its Authorization: Bearer <token> header — drop the header from that request.
    • [BREAKING] The Doorkeeper::OAuth::Client::Credentials class methods .from_request, .from_basic and .from_params are removed — extensions extracting credentials from a request should register a client authentication method instead.
    • Deprecated: the client_credentials option — use client_authentication instead.
    • Deprecated: the Doorkeeper::OAuth::Client::Credentials constant itself — it remains as an alias of Doorkeeper::ClientAuthentication::Credentials (the plain uid/secret struct).
    • See the Upgrade Guide for detailed breaking changes, deprecations and migration steps.
  • [#1841] Reject requests that use more than one client authentication method (RFC 6749 §2.3: "The client MUST NOT use more than one authentication method in each request") with an invalid_request error instead of silently using the first match. The request payload is validated against every registered method before the configured one is selected, so a client sending e.g. both Basic and body credentials is rejected even when only one of those methods is enabled. This applies to every endpoint that authenticates clients — token, revocation and introspection. Only real authentication mechanisms count: a bare client_id (the none method) is not a mechanism of its own, and deprecated client_credentials callable extractors never count towards the limit (they keep the historical first-extractor-wins selection).
  • [#1842] [BREAKING] force_pkce now requires PKCE for all clients, including confidential ones, in line with the OAuth 2.0 Security BCP (RFC 9700) and OAuth 2.1. Previously confidential clients were exempt. If you enable force_pkce and have confidential clients that do not yet send a code_challenge/code_verifier, their authorization requests will start to be rejected.
  • [#1845] Fix NameError when the config option DSL (Doorkeeper::Config::Option) is extended into a class that does not define self.builder_class. The guard raised Doorkeeper::MissingConfigurationBuilderClass, a constant that was never defined, so callers saw uninitialized constant instead of the intended message. The error is now defined as Doorkeeper::Errors::MissingConfigurationBuilderClass (a DoorkeeperError) and referenced correctly.
  • [#1846] Document and pin with regression specs that a scope parameter sent to the token endpoint is ignored for the authorization_code grant (RFC 6749 §4.1.3 does not define one): the access token always inherits the scopes of the authorization grant, and the response reports the actual granted scope. No behavior change.
  • [#1847] Fix Doorkeeper.config.enabled_grant_flows (and calculate_grant_flows) not listing the refresh_token grant flow when use_refresh_token is configured, so consumers (e.g. RFC 8414 metadata) no longer need to append it manually — the built-in metadata endpoint now relies on this too. The flow is no longer duplicated in token_grant_flows when refresh_token is also listed in grant_flows explicitly, a configuration-time warning is logged when grant_flows enables refresh_token without use_refresh_token (no refresh tokens would ever be issued), and the initializer template documents the flow.
  • [#1848] Derive token_endpoint_auth_methods_supported in the RFC 8414 metadata response from the effective client authentication configuration (including a deprecated client_credentials-only setup) instead of hardcoding the default methods. Servers that customize client_authentication (including extension-registered methods like private_key_jwt) now see their metadata reflect what the server actually accepts; unregistered names are not advertised.
  • [#1849] Support RFC 9207 (Authorization Server Issuer Identification): when issuer is configured, the iss parameter is added to the authorization responses redirected back to the client (successful and error responses alike) and authorization_response_iss_parameter_supported is advertised in the server metadata. Clients that parse the authorization redirect will start seeing the new iss parameter. A configured issuer that is not RFC-compliant (not an https URL, or containing a query/fragment) now logs a warning at boot, as does a path-bearing issuer, which RFC 8414 clients would not discover through Doorkeeper's root-only well-known metadata route.
  • [#1850] Fix reuse_access_token returning a refresh token that doesn't match the request: token reuse now requires the candidate's refresh token presence to match what the request asks for, in both directions. A request that expects a refresh token (e.g. use_refresh_token enabled) no longer reuses a token issued without one (previously the refresh token was silently omitted), and a request that does not expect one no longer reuses a token that carries one (previously an unrequested refresh token was returned, reachable via a per-request refresh_token_enabled callable). The requirement participates in the token matching itself, so an older matching token that satisfies it is still reused; a fresh token is created only when none does. API change for ORM extensions: matching_token_for and find_matching_token now take an optional block (&filter) that a token must satisfy to count as a match — extensions overriding either method must accept the block and honor it (accept and yield).
  • [#1851] Fix duplicate query parameter in the authorization callback when a client's registered redirect_uri already contains a parameter with the same name as a response parameter (e.g. state). The redirect query was merged with string keys on one side and symbol keys on the other, so a collision emitted the parameter twice (?state=fixed&code=...&state=user); the response parameter now overrides the registered one and appears exactly once. A blank response parameter (e.g. no state sent with the request) leaves the registered parameter untouched, per RFC 6749 §3.1.2.
  • [#1852] Fix the pkce_code_challenge_methods config validator using line anchors (^/$) instead of string anchors (\A/\z), so a multi-line value such as "plain\ngarbage" passed validation and was retained as a (never-matching) challenge method instead of being rejected and reset to the default.
  • [#1853] Fix reuse_access_token reusing a token that was created with custom_access_token_attributes values when the new request doesn't specify any custom attributes. Such requests now only match tokens without custom attributes.
  • [#1854] Fix the RFC 8414 metadata endpoint raising ActionController::UrlGenerationError (HTTP 500) when use_doorkeeper configures a custom controller whose namespace depth differs from doorkeeper/metadata (e.g. controllers tokens: "custom_tokens").
  • [#1855] Perform the fallback secret upgrade-on-access write (plain → hashed token or application secret) through the primary database role, so enable_multiple_database_roles setups no longer attempt the write on a read replica when the lookup happens in a request routed to the reading role.
  • [#1857] Pin with regression specs that a + between scopes in a form-encoded token request is decoded as a space (so scope=public+write refreshes fine), while a percent-encoded literal + (%2B) name...
Read more

v5.9.3

Choose a tag to compare

@nbulaj nbulaj released this 15 Jun 20:17
4737ffe
  • [#1834] Fix default allow_token_introspection returning false when a custom application_class is configured. The default proc compared application objects with ==, which fails when the authorized client and the introspected token's application are resolved as different classes (e.g. a base Doorkeeper::Application vs. a configured subclass) even though they reference the same record. It now compares application ids instead.
  • [#1832] Fix confusing belongs_to :owner side effect: Doorkeeper::Models::Ownership is now included only when enable_application_owner? is set (read at include time), so models no longer expose a misleading owner association/reflection when the application owner feature is disabled and the schema lacks the owner columns.

v5.9.2

Choose a tag to compare

@nbulaj nbulaj released this 05 Jun 13:09
f278711
  • [#1822][#1823][#1825] Update Rubocop config, auto-corrections and codebase cleanup.
  • [#1830] Fix NameError: uninitialized constant ApplicationRecord on rails db:seed (and other non-eager-loading flows) caused by on_load(:active_record) firing re-entrantly during ApplicationRecord autoload. The orm hooks no longer depend on ActiveSupport.on_load(:active_record); model concerns (Ownership, PolymorphicResourceOwner::ForAccessGrant, PolymorphicResourceOwner::ForAccessToken) are now wired up from each Mixins::* included block, which fires at parent-class autoload time — after Doorkeeper.configure has applied user settings and without re-entering the AR load chain.
    • Upgrade note: fully custom model classes that don't include Doorkeeper::Orm::ActiveRecord::Mixins::{Application,AccessToken,AccessGrant} will no longer auto-receive Ownership / PolymorphicResourceOwner concerns (previously injected by run_orm_hooks via the configured class name). Either inherit from the Doorkeeper default model, include the corresponding Mixins::* module, or include the concerns directly.

v5.9.1

Choose a tag to compare

@nbulaj nbulaj released this 19 May 16:47
3f5a58d
  • [#1781] Honor handle_auth_errors :raise in AuthorizationsController#authorize_response

  • [#1795] Fix: detailed error 'insufficient_scope' in protected resources 403s

  • [#1797] Fix doorkeeper:db:cleanup rake task failure on PostgreSQL

  • [#1800] Set @grant_type in ClientCredentialsRequest and RefreshTokenRequest constructors so request.grant_type returns
    the correct value in hooks like before_successful_strategy_response.

  • [#1802] Fix filter_parameters not applied when Doorkeeper.configure is called inside to_prepare.

  • [#1804] Use ActiveSupport.on_load(:active_record) in ORM hooks to prevent loading ActiveRecord models too early

  • [#1806] Fix token revocation bypass for public clients (RFC 7009)

  • [#1815] Expose current_resource_owner as a view helper in Doorkeeper::ApplicationController.

  • [#1818] Fix token introspection returning exp: 0 for non-expiring tokens.

  • [#1784] Remove hardcoded colons from view templates, move punctuation to i18n translation strings.

    [IMPORTANT]: if you have customized Doorkeeper views (authorizations/new, authorizations/show,
    applications/show) or overridden the default en.yml translations, you may need to update them.
    Colons are no longer hardcoded in the views — they are now part of the translation strings.
    Update the doorkeeper-i18n gem to get the
    updated translations for all locales.

  • [#1820] Remove dead wildcard presence check in Scopes#dynamic_scope_match? (internal cleanup, no behavior change).

  • [#1822] Update Rubocop config, auto-corrections.

  • [#1823] Update Rubocop config, part 2.

  • [#1825] Update Rubocop config, part 3.

  • [#1821] Fix noisy Could not find command "no_previous_refresh_token_column?" Thor output during the
    PreviousRefreshTokenGenerator spec by stubbing the underlying DB column check instead of the generator's
    private method (test-only change).

v.5.9.0

Choose a tag to compare

@nbulaj nbulaj released this 04 Mar 12:04
6cf6c52
  • [#1791] Add support for Rails read replicas with automatic role switching via enable_multiple_database_roles configuration option
  • [#1792] Consider expires_in when clear expired tokens with StaleRecordsCleaner.
  • [#1790] Fix race condition in refresh token revocation check by moving InvalidGrantReuse check inside the lock block
  • [#1788] Fix regex for basic auth to be case-insensitive
  • [#1775] Fix Applications Secret Not Null Constraint generator
  • [#1779] Only lock previous access token model when creating a new token from its refresh token if revoke_previous_refresh_token_on_use is false
  • [#1778] Ensure that token revocation is idempotent by checking that that token has not already been revoked before revoking.

v5.8.2

Choose a tag to compare

@nbulaj nbulaj released this 04 Apr 08:29
b305358
  • [#1755] Fix the error message for force_pkce
  • [#1761] Memoize authentication failure
  • [#1762] Allow missing client to trigger invalid client error when force_pkce is enabled
  • [#1767] Make sure error handling happens on a controller level opposed to action level to account for the controller being extended

v5.8.1

Choose a tag to compare

@nbulaj nbulaj released this 09 Dec 07:54
0f0b6aa
  • [#1752] Bump the range of supported Ruby and Rails versions
  • [#1747] Fix unknown pkce method error when configured
  • [#1744] Allow for expired refresh tokens to be revoked
  • [#1754] Fix refresh tokens with dynamic scopes