Skip to content
Kenta Ishizaki edited this page Aug 15, 2026 · 6 revisions

Troubleshooting

Common integration pitfalls, listed by symptom. Most of them come down to a small wiring detail in the host application rather than a bug in the gem.

The authorization endpoint crashes, or tokens belong to a nonsense "user"

Cause: resource_owner_authenticator returning a truthy non-user value. redirect_to returns its argument, so a callback that ends with redirect_to(new_user_session_url) hands that return value on as if it were the authenticated user.

Before v1.10.0, authorization requests carrying prompt or max_age then crashed with NoMethodError (e.g. undefined method 'id' for a String) or ActionController::DoubleRenderError. The redirect does halt Doorkeeper's before_action chain before any code or token is issued — but this gem's prompt / max_age handlers wrap authenticate_resource_owner! and run on the leaked value inside that same callback, before the halt takes effect. #281 (v1.10.0) normalizes the owner to nil whenever the callback has already redirected or rendered, so on current versions the redirect simply wins.

What still bites on every version: returning a truthy non-model value without redirecting (a string, say). Nothing halts the chain, so the value reaches token issuance and surfaces as a NoMethodError while building the sub claim, or as tokens tied to a bogus resource owner.

Fix: return a falsey value explicitly after redirecting, as shown in Configuration:

resource_owner_authenticator do
  if current_user
    current_user
  else
    redirect_to(new_user_session_url)
    nil
  end
end

unsupported_response_type when requesting id_token or id_token token

Cause: the implicit OIDC flows are not enabled in Doorkeeper.

Fix: add implicit_oidc to grant_flows in config/initializers/doorkeeper.rb (see Configuration). Note that this internal flow name only appears in the Doorkeeper configuration — the discovery document and Dynamic Client Registration advertise and accept the standard implicit grant type name (doorkeeper-openid_connect >= 2.0).

invalid_scope for the openid scope

Cause: the openid scope is not enabled, either globally or for the requesting application.

Fix: add it to optional_scopes (or default_scopes) in the Doorkeeper initializer, or to the application's own scopes attribute. Beware that an application defining its own scopes does not inherit the initializer's scopes — see Scopes. Relatedly, Dynamic Client Registration silently drops requested scopes the server doesn't support (v1.10.4+), so a dynamically registered client can end up without a scope you expected it to have.

The client rejects the ID Token because the nonce is missing or mismatched

Cause: custom Doorkeeper authorization views that don't forward the nonce parameter through the consent form — the parameter never reaches the grant, so the ID Token is issued without the claim, and spec-conforming client libraries reject it.

Fix: add the hidden nonce field to both the authorize and deny forms, as shown in Nonces.

The token response contains no id_token

Cause: on doorkeeper-openid_connect >= 2.0 this is intentional whenever a REQUIRED ID Token claim cannot be sourced (OIDC Core 1.0 §2): a client_credentials token carrying the openid scope has no resource owner for sub; a password-grant token issued with skip_client_authentication_for_password_grant has no application for aud; and a refreshed token whose resource owner was deleted after issuance can no longer resolve sub. Several of these cases produced 500 errors on 1.10.x instead.

Fix: nothing to fix on the server — request the openid scope from a flow that has both an authenticated end-user and a client application.

The same tokens are also not valid at the UserInfo endpoint, which is a statement about the End-User: since 2.0 they are answered with RFC 6750's 401 invalid_token rather than the 500 that 1.10.x raised while dereferencing the missing owner.

The client rejects the ID Token over at_hash, or the implicit access token is not accepted

Cause: Doorkeeper's hash_token_secrets combined with response_type=id_token token, on doorkeeper-openid_connect < 2.0. With that strategy the token attribute stored in the database is a digest of the value handed to the client, and the OpenID Connect implicit response returned the stored value: the client received a digest as its access_token (rejected as unknown at every endpoint), and the ID Token's at_hash was computed over that digest, so it never matched the hash the client computes from the access token it holds.

Fix: upgrade to doorkeeper-openid_connect >= 2.0, where both the response's access_token and at_hash are derived from the plaintext token (#351, #355), matching Doorkeeper's own CodeResponse / TokenResponse. Nothing stored needs to change. On older versions, the only workarounds are to disable hash_token_secrets or to avoid the hybrid response type.

Relatedly, an after_successful_authorization hook calling context.issued_token raised NoMethodError for the id_token / id_token token response types before 2.0, which defines #issued_token on those responses.

500 with Doorkeeper::OpenidConnect::Errors::InvalidConfiguration

Cause: issuer or signing_key resolving to nothing. Since v1.10.1 (#299, fixing a regression introduced in v1.10.0) a blank issuer raises instead of silently advertising an empty issuer in the discovery document — the classic trigger is an issuer block relying on an argument that is nil in the discovery context (see the arity notes in Configuration).

Fix: make the issuer block return a value in every context (the arity-3 form receives request for discovery), and configure signing_key.

/.well-known/oauth-authorization-server disagrees with /.well-known/openid-configuration

Cause: on Doorkeeper 6.0+ that path is answered by Doorkeeper's own RFC 8414 metadata route, which shadows the one this gem serves and knows nothing about the OIDC fields.

Fix: doorkeeper-openid_connect >= 2.0 injects the OpenID Connect metadata into Doorkeeper's document via its custom_metadata seam, so the two agree again — see the note in Routes.

prompt=consent answers with an empty 200 in API mode (api_only)

Cause: on doorkeeper-openid_connect < 2.0 the consent step rendered Doorkeeper's :new template directly. Under api_only those controllers descend from ActionController::API, which carries no template rendering, so the render produced an empty 200 text/plain and raised nothing — the client is told its authorization request succeeded and receives nothing it can act on.

Fix: upgrade to doorkeeper-openid_connect >= 2.0, where the consent step answers with the pre-authorization as JSON in API mode, exactly as Doorkeeper's own AuthorizationsController presents that step. On older versions, run Doorkeeper in standard (non-api_only) mode, or keep relying parties from sending prompt=consent.

This was the only response this gem rendered as a view: discovery, JWKS, UserInfo and Dynamic Client Registration answer with JSON in both modes. The remaining differences you see under api_only come from Doorkeeper itself answering with JSON instead of redirects and rendered forms, which is what the mode is for.

See also

Clone this wiki locally