Skip to content

Race condition causing invalid_token errors in OpenID Server #18520

@mvarblow

Description

@mvarblow

Describe the bug

Since updating to OC 2.1.7 we occasionally see this error in our application that relies on the Orchard Core OpenID Server:

2025-10-31 00:00:59.9707|Error||Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler|Exception occurred while processing message. |remoteip: 10.232.0.42 |session-id: 
[OpenIdConnectProtocolException] Message contains error: 'invalid_grant', error_description: 'The specified token is invalid.', error_uri: 'https://documentation.openiddict.com/errors/ID2004'.
	error: invalid_grant
	error_description: The specified token is invalid.
	error_uri: https://documentation.openiddict.com/errors/ID2004
	at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RedeemAuthorizationCodeAsync(OpenIdConnectMessage tokenEndpointRequest)
	at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync()
--- End of stack trace for exception handler ---
	at Microsoft.Extensions.Logging.Logger.Log[TState](LogLevel logLevel, EventId eventId, TState state, Exception exception, Func`3 formatter)
	at Microsoft.Extensions.Logging.LoggerMessage.<>c__DisplayClass8_0.<Define>g__Log|0(ILogger logger, Exception exception)
	at Microsoft.Extensions.Logging.LoggingExtensions.ExceptionProcessingMessage(ILogger logger, Exception ex)

After chasing this for a while via increased logging on the Orchard Core side (OpenIddict logging is tough as it's either incredibly verbose or very sparse unless you apply filters that are tricky to get right until you know what you're looking for), we've determined that the error results from the call to the /userinfo endpoint failing.

The failure looks like this in the OC logs:

2025-11-05 00:00:54.4990|6141|INFO|OpenIddict.Server.OpenIddictServerDispatcher|The response was successfully returned as a challenge response: {
  "error": "invalid_token",
  "error_description": "The specified token is invalid.",
  "error_uri": "https://documentation.openiddict.com/errors/ID2004"
}. |url: https://advantageacs.acsdomain.advantagecs.com/launchpad/connect/userinfo |action: Me|trace: 00-34884212f9cdb892aba0192f473e1d14-3fec4caf9896f05b-00|user: |ip: 10.232.2.84

It fails because the token isn't found:
Image

However, I was able to determine the token identifier that it was looking for via the OC logs:

2025-11-05 00:00:54.4520|6001|TRACE|OpenIddict.Server.OpenIddictServerDispatcher|The token '[redacted]' was successfully validated and the following claims could be extracted: iss:... updated_at: 1715097600, oc:entyp: user, sub: 4r19qa0kcjrfw5171z4wcmnqnp, ..., oi_au_id: 129982164a6c4bdc8a8f9f0624e94d1b, client_id: advantage-ui-UI-PERF, **oi_tkn_id: 72668b8758a049e381de54ea6f829677**, oi_tkn_typ: urn:ietf:params:oauth:token-type:access_token. |url: https://advantageacs.acsdomain.advantagecs.com/launchpad/connect/userinfo |action: Me|trace: 00-34884212f9cdb892aba0192f473e1d14-3fec4caf9896f05b-00|user: |ip: 10.232.2.84|OpenIddict.Server.OpenIddictServerHandlers+Protection+ValidateIdentityModelToken.HandleAsync

And that token actually is in the database now:
Image

It looks like this is almost certainly a timing issue as the call to the token endpoint to exchange the authorization code for a token is immediately followed by a call to the userinfo endpoint to exchange the access token for additional user information (all handled by the Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler).

It appears that there is effectively a race condition that was introduced by Replace unnecesary calls to SaveChangesAsync by FlushAsync

Previously SaveChangesAsync was used in the TokenStore. This would ensure that the token was committed to the DB immediately. Now FlushAsync is used which leaves the commit to happen when the YesSQL Session is disposed. I don't think that is guaranteed to happen before the response is parsed by the OpenID client which immediately issues the follow-on /userinfo endpoint request.

Orchard Core version

Reproduced in 2.1.7, 2.2.0, 2.2.1

To Reproduce

See above. Unfortunately, I don't have simple repro steps using the orchard repo. You need to set up an OpenID client that users the authorization code flow and then hit it many times and you'll eventually see the problem. It happens rarely, but several times per day for us.

Expected behavior

An access token should be immediately usable when it is returned to the caller.

Logs and screenshots

See above.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions