OAuth / OIDC – Access Token and ID Token Identical, "/userinfo" Fails with JWT

Hi,

During our OAuth 2.0 / OpenID Connect integration with OpenIAM(Autorization server and OIDC identity provider), we expirience the following issues:

Issue 1 :

When executing a successful Authorization Code or PKCE flow, OpenIAM returns both an id_token and an access_token also refresh_token when is set to on; however, both tokens id_token and an access_token are identical (same JWT).

The token payload contains claims such as:

  • nonce

  • at_hash

  • c_hash

  • auth_time

These claims are ID Token specific according to the OIDC specification and should not appear in an Access Token. The acces_token should be something similar to this:

{

“iss”: “http://xxxxxxxxxxx/idp/oauth2/OAUTHOICD-SSO”,

“sub”: “8a8a8fff9c9f37c7019d069ef9440409”,

“aud”: “userinfo”,

“scope”: “openid profile email”,

“exp”: 1776327142,

“iat”: 1776325342

}..

..

ID Tokens and Access Tokens must be diferent and serve separete purposes .

Issue 2:

when “Send JWT Token as Access Token” is enabled,id_token and access_token are byte-for-byte identical, and /userinfo OpenIAM endpoint responds with:

{

“error”: “invalid_request”,

“error_description”: “Provided token is not found”

}

/userinfo strictly requires a valid Access Token, when we use am opake token works properly.

Questions

  1. Is it expected behavior that enabling “Send JWT Token as Access Token” results in the same JWT being used as both id_token and access_token?

  2. If not, what is the correct configuration to ensure separate token issuance in compliance with OAuth 2.0 / OIDC?

  3. Is there anything else i should do for using /userinfo endpoint.

  4. I follow links from OpenIAM setup, ¿is there any other source i should follow for solving these?: OpenID Connect (OIDC),oAuth 2.0,OpenIAM oAuth2.0 Scopes

We appreciate your guidance to resolve this issues, my current OpenIAM Version: 4.2.1.15.1702.

Kind regards,

Mauro

Hello @mlmoreno,

Q1: Is it expected behavior that enabling “Send JWT Token as Access Token” results in the same JWT being used as both id_token and access_token?

Yes, this is the current behavior when “Send JWT Token as Access Token” is enabled. When this option is turned on, OpenIAM sends the same JWT as both the access_token and

id_token as a convenience for clients that require a JWT-format access token.

Q2: What is the correct configuration to ensure separate token issuance in compliance with OAuth 2.0 / OIDC?

For a fully OIDC-compliant setup with distinct id_token and access_token, use the following configuration:

  • Set “Send JWT Token as Access Token” → Off

With this setting disabled, OpenIAM issues:

  • An opaque access_token — used to authorize calls to protected endpoints including /userinfo

  • A JWT id_token — used on your client to verify the user’s identity, containing OIDC-specific claims such as nonce, auth_time, at_hash, and profile information

This is the recommended and fully supported configuration for OIDC compliance. Your client application should use the id_token for identity verification, and pass the opaque

access_token as a Bearer token when calling APIs or /userinfo.

Q3: Is there anything else I should do to use the /userinfo endpoint correctly?

Yes, please verify the following checklist:

1. “Send JWT Token as Access Token” must be Off (as described above)

2. Call /userinfo using the opaque access_token as the Bearer token:

GET /idp/oauth2/userinfo

Authorization: Bearer <access_token>

3. Ensure the user has the correct entitlements to the OAuth Authentication Provider via a Role or Group assignment

4. Verify the “Is OAuth client Authorization Disabled” checkbox under Administration > System Configuration > System tab:

- If this is unchecked and the user lacks entitlements to the provider, the request will fail with insufficient_rights

5. Confirm the scopes configured on your provider include the claims you expect back (e.g., openid, profile, email)

6. Validate your provider’s supported scopes and endpoints anytime via the OpenID Connect Discovery URL (well-known URI) available on your Authentication Provider configuration page in the webconsole

Following the configuration above on OpenIAM version 4.2.1.15, /userinfo will respond correctly with the expected user profile claims.

Hope this helps resolve the integration. Feel free to follow up if you have further questions.

Hi ameet,

First of all, thank you very much for the help and guidance received.

I continue working on OAuth 2.0 token management using opaque access tokens, following the recommended and supported OpenIAM configuration you gave me (JWT ID Token + opaque access token + refresh token).

In this context, my approach is to use:

  • Token validation / introspection endpoint for runtime validation of opaque access tokens

  • Token revocation endpoint (/revoke) for logout and token lifecycle management

The token validation endpoint works correctly in my environment and behaves as expected when validating opaque access tokens.

However, I am experiencing issues specifically with the token revocation endpoint.

I am basing my implementation on the Postman collection provided by OpenIAM:

The example shown in the collection uses a call similar to:

Shell

curl --location --request POST \

https://test.openiam.com/idp/oauth2/revoke?token=<TOKEN_TO_REVOKE>’

Mostrar más líneas

When executing an equivalent call in my environment, I consistently receive the following response:

JSON

{

“code”: 403,

“error”: “insufficient_scope”,

“error_description”: “The request requires higher privileges than provided by the access token”,

“scope”: “CONTENT_PROVIDER_CP_HTTPS”

}

``

Mostrar más líneas

From my understanding so far:

  • The /revoke endpoint is part of the OAuth 2.0 Token Revocation specification (RFC 7009).

  • Token revocation is a client operation, not a user operation.

  • The error indicates that the caller lacks the required provider-level privileges (e.g. CONTENT_PROVIDER_*) associated with the OAuth Authentication Provider.

At this point, I would appreciate clarification on the following points:

  1. What is the correct and fully supported way in OpenIAM to invoke /revoke when using opaque access tokens?
    – Should the call always be authenticated using the OAuth client credentials (client_id / client_secret) rather than an access token?

  2. Which exact roles, entitlements, or provider permissions must be assigned to an OAuth client in order to successfully revoke tokens?(I add the scope on the Authentication provider CP-/…/revoke )

  3. Is the revocation example in the Postman collection incomplete without explicitly documenting the required client authentication method and provider entitlements?(
    I’ve tried several methods without success. example:

    1. POST /idp/oauth2/revoke?token=ZTLlh2yt0J6UaEHS.1jo9SNhO5V7Y1Z7MEEDE1ZXDa8jxwEMV-ZMI8At3NmSrwAv8uH-UZUd HTTP/1.1
      Host: example.com
      Content-Type: application/x-www-form-urlencoded
      Cookie: OPENIAM_AUTH_TOKEN=rz4VdCb7xf4BYCnoluveWrvP5F3WwJyRkNl8qIjUsgz35wm9hby6YtOei6J1j7jg4Bps7TA7DQvsEMMflKbM0Wxv41obiQ0LgABZoguU8OZhY46SezA0kevBH8XOHGWBpskAv3EBowpEPGvhfRrKUlX1CqrM7cWw2FMfklvYMqsT2ZRexYPRQehfTqIDNotvzhmqzZNqWPXDFODk6zFJVJ+ln5SSGko2Q8jDAsHVY2cTv1fw52noM0ORi3iSekcs6LXhUHOs/TfzAvXxtzzKE2pZlf2BRuqCoczyWUcdAys=; SESSION=MDIxZDFmMTEtYmJlMy00YzhmLTk3OGItYjUzZGFhMWYxZGNk
      Content-Length: 121

      client_id=yyyyyy&Client_secret=xxxxxx)

Once again, it is worth emphasizing that token validation works correctly with the same configuration; the issue is isolated to token revocation.

Any guidance on the recommended configuration and privilege model for a backend/middleware OAuth client performing token revocation would be greatly appreciated.

Thank you again for your support.

KindRegards,

Hi ,

Following up on the previous messages, I would like to confirm that token revocation endpoint is now working correctly, Using the approach provided in the OpenIAM Postman collection, we were able to successfully revoke tokens with the following request:

curl --location --globoff --request POST \

https://staging.openiam.com/idp/oauth2/revoke?token={{access_token}}’ \

--header ‘Authorization: *****’ \

--header ‘Cookie: SESSION=MDZmNzA5NWUtNWZmNC00ODliLTliYTctOTZmNTNkODMyN2Jl’

We are currently working on a SSO and Single Logout mechanism, since our middleware is shared by multiple applications and OpenIAM acts as the common OIDC/OAuth provider. The target logout flow we are implementing is the following:

User clicks “Logout” in an application

1º-▼ Frontend

└─ GET /idp/logout → Terminates the SSO session in OpenIAM

2º-▼ Middleware

└─ POST /oauth2/revoke → Revokes the refresh token & Acces_Token (ok)

Q1: Could you provide an example of the call to the /idp/logout → Terminates the SSO session in OpenIAM? I had been traing with this call

GET /idp/logout?access_token=-mBEHwUl1jZGPDVMotHDnAfEBF4qy9Tnd5ltYgGIh.licGZX-BYTHWpKiW9 HTTP/1.1
Host: ``staging.openiam.com
Cookie: OPENIAM_AUTH_TOKEN=rz4V…ssUcdAys=; SESSION=

Thank you again for your support.

Kind Regards.

Hello @mlmoreno,

Thank you for the detailed report. The 403 insufficient_scope error you are seeing is not generated by the /revoke endpoint logic itself — it is being returned by OpenIAM’s

Content Provider access control layer, which intercepts requests before they reach the OAuth service. The scope value CONTENT_PROVIDER_CP_HTTPS in the error response identifies the Content Provider through which the request is routed, and it means the caller has not been granted entitlement to it.

Root cause of your issue

Looking at the request you shared, you are authenticating with OPENIAM_AUTH_TOKEN and SESSION cookies. This is a session-based (user) authentication approach and is not the correct method for calling /revoke. Token revocation is a client operation as you correctly noted, and it must be authenticated using OAuth client credentials, not a user session.

Correct way to call /revoke

Per RFC 7009, authenticate the call using HTTP Basic Auth with your client_id and client_secret:

POST /idp/oauth2/revoke HTTP/1.1

Host: your-openiam-host

Content-Type: application/x-www-form-urlencoded

Authorization: Basic base64(client_id:client_secret)

token=<OPAQUE_ACCESS_TOKEN_TO_REVOKE>

Alternatively, you can pass the credentials in the POST body:

POST /idp/oauth2/revoke HTTP/1.1

Host: your-openiam-host

Content-Type: application/x-www-form-urlencoded

token=<OPAQUE_ACCESS_TOKEN_TO_REVOKE>&client_id=<YOUR_CLIENT_ID>&client_secret=<YOUR_CLIENT_SECRET>

Do not pass OPENIAM_AUTH_TOKEN or SESSION cookies — remove them from the request entirely.

Checklist to resolve the insufficient_scope error

The same entitlement model that applies to /userinfo applies here. Please verify:

1. Client entitlement to the Content Provider — ensure your OAuth client (or the Role/Group it belongs to) has been granted access to the Content Provider that fronts the

/idp/oauth2/revoke pattern.

2. “Is OAuth client Authorization Disabled” flag — if you do not require per-client entitlement checks, navigate to Administration > System Configuration > System and verify this checkbox. If unchecked, clients without explicit entitlement will be blocked.

3. Scopes on the Authentication Provider — confirm the /idp/oauth2/revoke URI pattern is included in the scopes configured on your OAuth Authentication Provider (which you mentioned you already added — just confirm the pattern matches exactly).

4. Remove session cookies from the request — the presence of OPENIAM_AUTH_TOKEN/SESSION in the call may be causing OpenIAM to evaluate the request as a user session rather than a client credential request, which would fail the content provider entitlement check.

Please let us know if you continue to see the error after applying the above changes.