[Logout] invalid_request error with id_token_hint
Hello,
We have encountered an issue related to the logout flow when multiple browser tabs are open.
Environment:
- Browser: Google Chrome (reproducible by duplicating a tab)
- Scenario: User session expiration followed by a logout attempt.
Steps to Reproduce:
- Log into the application in a browser tab.
- Duplicate the tab to create a second session context.
- Wait for the authentication token (or session) to expire naturally (e.g., leave the laptop idle long enough for the token to expire or enter sleep mode).
- After the token has expired, initiate a logout from the second tab.
Observed Result:
The logout request fails and returns the following error:
{
"error": "invalid_request",
"error_description": "id_token_hint is invalid"
}
Root Cause Analysis (Based on Logs):
The exception appears to originate from the CheckSessionController during the validation of the id_token_hint parameter. The stack trace points to a failure in the JwtBuilder.ReadSelfIssuedJsonWebToken(realm, idTokenHint) method. This suggests that the id_token_hint being sent in the logout request is either malformed, expired, or otherwise invalid when it is processed by the server.
Additional Context:
This problem is intermittent and seems to be specifically tied to scenarios with multiple concurrent tabs and expired sessions, often triggered by a system sleep/wake cycle. Logs confirm that a token refresh occurred prior to logout. However, the logout request is sent with an expired id_token_hint, which fails validation and causes the error.
IDX10223: Lifetime validation failed. The token is expired. ValidTo...
Thank you for looking into this. We are available to provide any additional logs or information required.
Hello,
What type of application/client is the user trying to disconnect from? Is it an SPA (Angular) application, or a standard ASP.NET Core website with controllers?
If it is a standard ASP.NET Core website, then the backchannel logout URL must be configured, for example:
https://localhost:5002/bc-logout
You must also add the following logic in your controller:
[HttpPost]
[Route("bc-logout")]
[AllowAnonymous]
public async Task<IActionResult> BackChannelLogout([FromForm] BackChannelLogoutRequest request, CancellationToken cancellationToken)
{
if (request == null || string.IsNullOrWhiteSpace(request.LogoutToken)) return BadRequest();
var logoutToken = request.LogoutToken;
var handler = new JsonWebTokenHandler();
var jwt = handler.ReadJsonWebToken(logoutToken);
var sessionId = jwt.Claims.First(c => c.Type == System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sid)?.Value;
if (!string.IsNullOrWhiteSpace(sessionId))
await _distributedCache.SetStringAsync(sessionId, "disconnected");
return Ok();
}
In your custom middleware, add the logic to disconnect the user when the sessionId is present in the distributed cache.
This step is important to ensure that the user is disconnected from all active browser tabs. Otherwise, there is a risk that the user may still appear authenticated on the client, even though the OpenID session is no longer valid.