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.

Hi @ameet_shah ,

Thank you very much for your detailed explanation and guidance.

We can confirm that the revocation flow is now working correctly and the insufficient_scope error has been resolved.

We would like to ask for your guidance regarding the correct approach for implementing Single Logout (SSO logout) in OpenIAM.

As we understand, SSO logout is performed through the /idp/logout endpoint, typically via browser redirection in order to properly invalidate the session and clear cookies across applications.

Could you please confirm if this is the recommended and supported approach for implementing SSO logout, especially in multi-application scenarios?

Additionally, we would like to clarify whether OpenIAM supports any back-channel or API-based mechanism to trigger SSO logout without relying on browser redirection.

If such a mechanism exists, could you please provide details about the recommended endpoint and required request format?

Thank you again for your support.

Kind Regards

mlmoreno

Hello @mlmoreno,

I will consult my team and get back to you as soon as possible.

Thanks,

Ameet

Hello @mlmoreno,

Regarding your question on Single Logout (SSO logout) in OpenIAM:

Your understanding is correct — browser redirection to the /idp/logout endpoint is the recommended and supported approach for implementing SSO logout in OpenIAM, particularly in multi-application SSO scenarios. This approach ensures that the OpenIAM session is properly invalidated and browser-side session artifacts (such as cookies) are cleared as part of the logout process.

For environments using SAML federation, OpenIAM also supports SAML Single Logout flows through the dedicated SAML logout endpoints, allowing logout propagation across participating applications where applicable.

Regarding back-channel or API-based logout:

OpenIAM does provide mechanisms for server-side session invalidation; however, these should not be considered a full replacement for browser-based SSO logout.

Available options include:

  • AuthenticationService.globalLogoutRequest(…) – server-side session invalidation through the authentication service

  • /idp/oauth2/revoke – OAuth2/OIDC token revocation for access/token invalidation

Please note that these mechanisms invalidate server-side sessions and/or tokens but do not perform browser cookie cleanup or fully propagate logout across federated applications in the same way as front-channel logout.

Therefore, for complete SSO logout behavior across multiple applications, browser redirection to /idp/logout remains the recommended implementation pattern.

If your use case requires fully automated logout without browser interaction, please share additional details about the protocol (OIDC / SAML / custom application flow), and we can help evaluate the most suitable approach.

Hi @ameet_shah ,

Thank you for your previous clarification regarding the logout behavior in OpenIAM.

To provide additional context, I have updated and attached a diagram where I try to represent the different authentication and logout flows (SAML and OAuth2/OIDC) currently in place in our futere architecture in development.

Our Understanding of SAML Single Logout:

From our analysis and configuration review, we understand that:

  • In SAML-based integrations, OpenIAM can act as the central orchestrator for Single Logout (SLO).

  • When a logout is initiated from a Service Provider , OpenIAM:

    • Terminates the SSO session

    • Propagates the logout by invoking the configured SingleLogoutService endpoints of other Service Providers within they are under the same CP with a comoun Authentication cookie.

  • This allows logout to be propagated across SAML-integrated applications, provided they properly support SAML SLO.

Our Understanding OIDC/OAuth Scenario and middleware layer:

In contrast, for applications integrated via OAuth2 / OpenID Connect, which access through our middleware layer:

  • We could say logout it must be split in two main operations:

  • /idp/logout → browser-based session invalidation (SSO)

  • /oauth2/revoke → backend token invalidation

However, unlike SAML SLO, there is no built-in mechanism to propagate logout across all OIDC-based applications automatically, that is suppoust to be done from our middelware SLO.

So, yes achieve a consistent Single Logout experience across OIDC applications, especially given the presence of a shared middleware layer without browser interaction, it will be great to achive, becouse will gave us the chance to deploy a orquestated central SLO…

Hello @mlmoreno, I am consulting my team and will have an answer for you shortly.

Hello @mlmoreno,

Thank you for sharing the updated architecture diagram — it gives a very clear picture of what you are building and makes it easy to address your question precisely.

SAML SLO — your understanding is correct. When logout is initiated from any SP under the same Content Provider sharing an authentication cookie, OpenIAM terminates the SSO session and propagates the logout to all configured SingleLogoutService endpoints. Nothing further needed on that side.

On the OIDC side — your question mark:

You are right that OIDC logout splits into two operations, and you are also right that there is no automatic propagation to apps the way SAML SLO works. However, your Middleware SLO box can handle both operations cleanly today without any browser interaction:

Terminating the OpenIAM side:

  • POST /idp/oauth2/revoke with the user’s access and refresh tokens — removes them from OpenIAM immediately. Any subsequent call the app makes using those tokens will fail.

  • AuthenticationService.globalLogoutRequest() — terminates the OpenIAM SSO session server-side. No browser redirect needed.

These two calls together give your Middleware SLO full control over the OpenIAM side of logout.

**Notifying your apps (App 1 / App 2 / App N):**OpenIAM does not send a logout signal to OIDC-integrated applications — that propagation is your middleware’s responsibility, which your diagram already correctly shows. Each app receives the notification from your Middleware SLO directly.

For your middleware-signed JWTs (the “Generate JWT / signed by middleware” box in your diagram) — since those tokens are issued and signed by your middleware rather than OpenIAM, revocation at OpenIAM has no effect on them. A short token TTL combined with your middleware’s own session invalidation is the right approach there.

In summary, your architecture is sound. The Middleware SLO box is achievable with the two OpenIAM calls above — the question mark can be resolved.

If you have further questions, please feel free to follow up.