v6.0.0.beta1
Pre-release
Pre-release
Please make sure you read the Upgrade guides and changelog below before the update since this version includes breaking changes.
- [#1816] Fix: redirect
unauthorized_clienterrors per RFC 6749 Section 4.1.2.1; validate redirect_uri beforeclient_supports_grant_flowto prevent open redirect - [#1867] Fix: use
access_deniedinstead ofinvalid_clientforresource_owner_authorize_for_clientvalidation per RFC 6749 Section 4.1.2.1.invalid_clientis 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) andcustom_metadata(a Hash merged into the response, e.g. to advertise an OIDCuserinfo_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 throughskip_controllersare 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_urlhelpers now return a path plus a matching*_endpoint_paramsbuilder so flow specs post credentials through the body. - [#1840] Introduce a pluggable client authentication registry (RFC 6749 §2.3).
- New
client_authenticationconfig option declares which methods are accepted and in which order. Built-in strategies:client_secret_basic,client_secret_postandnone. - 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
nonestrategy rejects requests that carry a non-blankAuthorizationheader. A common casualty is a public client POSTing to/oauth/revokewith a bodyclient_idwhile still sending itsAuthorization: Bearer <token>header — drop the header from that request. - [BREAKING] The
Doorkeeper::OAuth::Client::Credentialsclass methods.from_request,.from_basicand.from_paramsare removed — extensions extracting credentials from a request should register a client authentication method instead. - Deprecated: the
client_credentialsoption — useclient_authenticationinstead. - Deprecated: the
Doorkeeper::OAuth::Client::Credentialsconstant itself — it remains as an alias ofDoorkeeper::ClientAuthentication::Credentials(the plain uid/secret struct). - See the Upgrade Guide for detailed breaking changes, deprecations and migration steps.
- New
- [#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_requesterror 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 bareclient_id(thenonemethod) is not a mechanism of its own, and deprecatedclient_credentialscallable extractors never count towards the limit (they keep the historical first-extractor-wins selection). - [#1842] [BREAKING]
force_pkcenow 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 enableforce_pkceand have confidential clients that do not yet send acode_challenge/code_verifier, their authorization requests will start to be rejected. - [#1845] Fix
NameErrorwhen the config option DSL (Doorkeeper::Config::Option) is extended into a class that does not defineself.builder_class. The guard raisedDoorkeeper::MissingConfigurationBuilderClass, a constant that was never defined, so callers sawuninitialized constantinstead of the intended message. The error is now defined asDoorkeeper::Errors::MissingConfigurationBuilderClass(aDoorkeeperError) and referenced correctly. - [#1846] Document and pin with regression specs that a
scopeparameter sent to the token endpoint is ignored for theauthorization_codegrant (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 grantedscope. No behavior change. - [#1847] Fix
Doorkeeper.config.enabled_grant_flows(andcalculate_grant_flows) not listing therefresh_tokengrant flow whenuse_refresh_tokenis 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 intoken_grant_flowswhenrefresh_tokenis also listed ingrant_flowsexplicitly, a configuration-time warning is logged whengrant_flowsenablesrefresh_tokenwithoutuse_refresh_token(no refresh tokens would ever be issued), and the initializer template documents the flow. - [#1848] Derive
token_endpoint_auth_methods_supportedin the RFC 8414 metadata response from the effective client authentication configuration (including a deprecatedclient_credentials-only setup) instead of hardcoding the default methods. Servers that customizeclient_authentication(including extension-registered methods likeprivate_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
issueris configured, theissparameter is added to the authorization responses redirected back to the client (successful and error responses alike) andauthorization_response_iss_parameter_supportedis advertised in the server metadata. Clients that parse the authorization redirect will start seeing the newissparameter. A configuredissuerthat 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_tokenreturning 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_tokenenabled) 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-requestrefresh_token_enabledcallable). 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_forandfind_matching_tokennow 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_urialready 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. nostatesent with the request) leaves the registered parameter untouched, per RFC 6749 §3.1.2. - [#1852] Fix the
pkce_code_challenge_methodsconfig 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_tokenreusing a token that was created withcustom_access_token_attributesvalues 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) whenuse_doorkeeperconfigures a custom controller whose namespace depth differs fromdoorkeeper/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_rolessetups 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 (soscope=public+writerefreshes fine), while a percent-encoded literal+(%2B) names a single scope and is rejected when unknown, per RFC 6749 §3.3. Test-only change, closes [#1686]. - [#1859] Pin with regression specs that a refresh token bound to an expired access token can be revoked (fixed by [#1744]) and that the revoked refresh token is rejected at the token endpoint afterwards. Test-only change, closes [#1671].
- [#1860] Fix introspection of refresh tokens (RFC 7662): a refresh token bound to an expired access token now introspects as
active: true, matching the token endpoint which still accepts it. The introspection response for a presented refresh token no longer includes the paired access token'stoken_typeandexp. Fixes [#1858]. - [#1862] Document that with
reuse_access_tokenenabled token matching considers only the application, resource owner, scopes and custom token attributes — separate authorization grants for the same combination intentionally share one access token — and pin the behavior with a regression spec. Docs/test-only change, closes [#1693]. - [#1864] Fix
custom_access_token_attributesvalues being dropped when the authorization goes through the consent screen: the approve/deny forms now carry the custom attributes as hidden fields, and the pre-authorization JSON (api_onlymode) includes them so custom consent UIs can send them back. - [#1869] Improve test coverage
- [#1870] Fix: raise the intended
Doorkeeper::Errors::TokenGeneratorNotFound/UnableToGenerateToken(instead of a confusingNameError) whenapplication_secret_generatoris misconfigured.