Skip to main content

Solutions for common OAuth2-related problems

Spec-compliant OAuth 2.0 and OpenID Connect is hard. Let's take a look how to resolve certain issues.

OpenID Connect ID Token missing​

If you expect an OAuth 2.0 ID Token but aren't receiving one, this can have multiple reasons:

  1. You are using the client_credentials grant which can't return an ID token.
  2. You forgot to request the openid scope when calling /oauth2/auth?response_type=code (Authorize Code Flow - correct would be /oauth2/auth?response_type=code&scope=openid) or the id_token response type when calling /oauth2/auth?response_type=token (Implicit/Hybrid flow - correct would be /oauth2/auth?response_type=token+id_token&scope=openid or any other combination such as response_type=id_token, response_type=token+code+id_token).
  3. You forgot to include nonce when calling /oauth2/auth?response_type=id_token or any combination of id_token (token+id_token, token+code+id_token etc.) nonce is a value sent by the client application and included as a claim in the returned id_token. The nonce claim can then be verified by the client application to mitigate any replay attacks. Optional for Authoize Code Flow, but mandatory for Implicit/Hybrid flow. Correct request would be - /oauth2/auth?response_type=id_token&nonce=some-value. You can read more about nonce here OpenId Connect Core.
  4. Your consent app didn't send granted_scope: ["openid"] or when accepting the consent request.
  5. The OAuth 2.0 Client making the request isn't allowed to request the scope openid.

OAuth 2.0 Refresh Token is missing​

If you expect an OAuth 2.0 Refresh Token but aren't receiving one, this can have multiple reasons:

  1. You are using an implicit or hybrid flow. These flows never return a refresh token!
  2. You are using the client_credentials grant which can't return a refresh token.
  3. You forgot to request the offline or offline_access scope when calling /oauth2/auth.
  4. You consent app didn't send granted_scope: ["offline"] or granted_scope: ["offline_access"] when accepting the consent request.
  5. The OAuth 2.0 Client making the request isn't allowed to grant type refresh_token.

OAuth 2.0 authorize code flow fails​

The most likely cause is misconfiguration, summarized in the next sections.

Refresh Token flow fails​

Refresh tokens can become invalid if abuse is detected, but coding issues may also trigger this scenario, for example if a client makes multiple requests.

Some common examples:

  1. Replay of authorization code grant.
  2. Replay of refresh token grant.

Wrong or misconfigured OAuth 2.0 client​

You are using the wrong OAuth 2.0 Client or the OAuth 2.0 Client has a broken configuration. To check that you're using the right client, run:

ory get oauth2-client {client.id}}

The result shows you the whole client (excluding its secret). Check that the values are correct. Example:

{
"client_id": "my-client",
"grant_types": [
"authorization_code"
],
"jwks": {},
"redirect_uris": [
"http://127.0.0.1:5556/callback"
],
"response_types": [
"code"
],
"scope": "openid offline",
"subject_type": "pairwise",
"token_endpoint_auth_method": "client_secret_basic",
"userinfo_signed_response_alg": "none"
}

Redirect URL isn't whitelisted​

A likely cause of your request failing is that you are using the wrong redirect URL. Assuming your OAuth 2.0 URL looks like https://$PROJECT_SLUG.projects.oryapis.com/oauth2/auth?client_id=my-client&...&redirect_uri=http://my-url/callback

The redirect URL http://my-url/callback must be whitelisted in your client configuration. The URLs must match. URL http://my-url/callback and http://my-url/callback?foo=bar are different URLs!

To see the whitelisted redirect_uris, check the client:

ory get oauth2-client {client.id}

{
// ...
"redirect_uris": [
"http://127.0.0.1:5556/callback"
],
// ...
}

Here you see that http://my-url/callback isn't in the list, which is why the request fails.

/oauth2/token endpoint fails for JWKS based client​

When trying to get an access token for a client registered with "token_endpoint_auth_method": "private_key_jwt" it's possible that the provided jwt has expired.

The response body that's sent to the client is like below

{
"error": "error",
"error_description": "The error is unrecognizable: Token is expired"
}

The debug error messages isn't provided to avoid divulging more information to a malicious client.

If you need more information in response for debugging, switch the project to development mode. Now you will be able to see the error_debug field in the response like below:

{
"error": "error",
"error_description": "The error is unrecognizable",
"status_code": 500,
"error_debug": "Token is expired"
}