We are running the ABP Auth Server (OpenIddict-based) inside an Azure Aspire environment with multiple instances. The system is fronted by Aspire, and we are using Azure Cache for Redis for caching and DataProtection key storage.
Since deploying to this setup, login often fails. Users frequently see authentication errors, and the logs show:
invalid_grant during /connect/token
invalid_token during /connect/userinfo
and messages like “The signing key associated to the specified token was not found.”
We already changed from a local Redis container to Azure Redis, but the problem still occurs. It seems that issued tokens or authorization codes become invalid between instances — as if keys or state are not synchronized properly. But this is also not 100% sure that this is the issue.
It looks like the Auth Server in ABP is not stable in a distributed Aspire + Azure Redis setup, and tokens become invalid after redirects or restarts.
Question: How can we configure the ABP Auth Server to work reliably in this environment? Or how can we remove redis completly Is there a recommended way to make key and token handling more robust across multiple instances or the login in general more stable?
Additional info its not always failing (at least when Iam using swagger) and if I'am using the auth server directly Im able to login but cannot use the frontend.
Here is some log from the Authserver and the deployed environment
[13:16:32 INF] Request starting HTTP/1.1 GET http://auth.main.cargonerds.dev/_content/Cargonerds.UI.Shared/rohlig/images/bg.mp4 - null null stdout F
[13:16:32 INF] Executing endpoint 'Microsoft.AspNetCore.Routing.RouteEndpoint' stdout F
[13:16:32 INF] Sending ["bytes 0-11598544/11598545"] of file /app/wwwroot/_content/Cargonerds.UI.Shared/rohlig/images/bg.mp4 stdout F
[13:16:32 INF] Executed endpoint 'Microsoft.AspNetCore.Routing.RouteEndpoint' stdout F
[13:16:33 INF] Request finished HTTP/1.1 GET https://auth.main.cargonerds.dev/_content/Cargonerds.UI.Shared/rohlig/images/bg.mp4 - 206 11598545 video/mp4 357.0839ms stdout F
[13:16:35 INF] Request starting HTTP/1.1 POST http://auth.main.cargonerds.dev/connect/token - application/x-www-form-urlencoded 292 stdout F
[13:16:35 INF] CORS policy execution successful. stdout F
[13:16:35 INF] The request URI matched a server endpoint: Token. stdout F
[13:16:35 INF] The token request was successfully extracted: {
"client_id": "admin",
"code": "[redacted]",
"redirect_uri": "https://admin.main.cargonerds.dev/authentication/login-callback",
"code_verifier": "3f8a1e597e184596b15e716fc96842e3a3af248ee8814f48888f3b4ebd3a49cc4681df1d4a8e47d59142cfa2fe083d9c",
"grant_type": "authorization_code"
}. stdout F
[13:16:35 INF] The response was successfully returned as a JSON document: {
"error": "invalid_grant",
"error_description": "The specified token is invalid.",
"error_uri": "https://documentation.openiddict.com/errors/ID2004"
}. stdout F
[13:16:35 INF] Request finished HTTP/1.1 POST https://auth.main.cargonerds.dev/connect/token - 400 157 application/json;charset=UTF-8 112.309ms stdout F
[13:16:37 INF] Request starting HTTP/1.1 GET http://auth.main.cargonerds.dev/.well-known/openid-configuration - null null stdout F
[13:16:37 INF] CORS policy execution successful. stdout F
[13:16:37 INF] The request URI matched a server endpoint: Configuration. stdout F
[13:16:37 INF] The configuration request was successfully extracted: {}. stdout F
[13:16:37 INF] The configuration request was successfully validated. stdout F
[13:16:37 INF] The response was successfully returned as a JSON document: {
"issuer": "https://auth.main.cargonerds.dev/",
"authorization_endpoint": "https://auth.main.cargonerds.dev/connect/authorize",
"token_endpoint": "https://auth.main.cargonerds.dev/connect/token",
"introspection_endpoint": "https://auth.main.cargonerds.dev/connect/introspect",
"end_session_endpoint": "https://auth.main.cargonerds.dev/connect/endsession",
"revocation_endpoint": "https://auth.main.cargonerds.dev/connect/revocat",
"userinfo_endpoint": "https://auth.main.cargonerds.dev/connect/userinfo",
"device_authorization_endpoint": "https://auth.main.cargonerds.dev/device",
"pushed_authorization_request_endpoint": "https://auth.main.cargonerds.dev/connect/par",
"jwks_uri": "https://auth.main.cargonerds.dev/.well-known/jwks",
"grant_types_supported": [
"authorization_code",
"implicit",
"password",
"client_credentials",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"LinkLogin",
"Impersonation"
],
"response_types_supported": [
"code",
"code id_token",
"code id_token token",
"code token",
"id_token",
"id_token token",
"token",
"none"
],
"response_modes_supported": [
"query",
"form_post",
"fragment"
],
"scopes_supported": [
"openid",
"offline_access",
"email",
"profile",
"phone",
"roles",
"address",
"Cargonerds"
],
"claims_supported": [
"aud",
"exp",
"iat",
"iss",
"sub"
],
"id_token_signing_alg_values_supported": ["RS256"],
"code_challenge_methods_supported": ["plain", "S256"],
"subject_types_supported": ["public"],
"prompt_values_supported": ["consent", "login", "none", "select_account"],
"token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"],
"introspection_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"],
"revocation_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"],
"device_authorization_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"],
"pushed_authorization_request_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"],
"require_pushed_authorization_requests": false,
"claims_parameter_supported": false,
"request_parameter_supported": false,
"request_uri_parameter_supported": false,
"tls_client_certificate_bound_access_tokens": false,
"authorization_response_iss_parameter_supported": true
}. stdout F
[13:16:37 INF] Request finished HTTP/1.1 GET https://auth.main.cargonerds.dev/.well-known/openid-configuration - 200 2716 application/json;charset=UTF-8 5.4354ms stdout F
[13:16:38 INF] Request starting HTTP/1.1 GET http://auth.main.cargonerds.dev/connect/authorize?... stdout F
[13:16:38 INF] The request URI matched a server endpoint: Authorization. stdout F
[13:16:38 INF] The authorization request was successfully extracted: {...}. stdout F
[13:16:38 INF] The authorization request was successfully validated. stdout F
[13:16:38 INF] Executing endpoint 'Volo.Abp.OpenIddict.Controllers.AuthorizeController.HandleAsync (Volo.Abp.OpenIddict.AspNetCore)' stdout F
[13:16:38 INF] Route matched... stdout F
[13:16:38 INF] Executed action method... stdout F
[13:16:39 INF] The authorization response was successfully returned... stdout F
[13:16:39 INF] Request finished HTTP/1.1 GET https://auth.main.cargonerds.dev/connect/authorize?... 302 0 null 283.5483ms stdout F
[13:16:39 INF] Start processing HTTP request GET https://auth.main.cargonerds.dev/health-status stdout F
[13:16:39 INF] Sending HTTP request GET https://auth.main.cargonerds.dev/health-status stdout F
[13:16:39 INF] Request starting HTTP/1.1 GET http://auth.main.cargonerds.dev/health-status - null null stdout F
[13:16:39 INF] Executing endpoint 'Health checks' stdout F
[13:16:39 INF] Executed endpoint 'Health checks' stdout F
[13:16:39 INF] Request finished HTTP/1.1 GET https://auth.main.cargonerds.dev/health-status - 200 null application/json 6.43ms stdout F
[13:16:40 INF] Request starting HTTP/1.1 POST http://auth.main.cargonerds.dev/connect/token - application/x-www-form-urlencoded 292 stdout F
[13:16:40 INF] The response was successfully returned as a JSON document: {...}. stdout F
[13:16:40 INF] Request finished HTTP/1.1 POST https://auth.main.cargonerds.dev/connect/token - 400 157 application/json;charset=UTF-8 17.8779ms stdout F
[13:16:41 INF] Request starting HTTP/1.1 GET http://auth.main.cargonerds.dev/connect/userinfo - null null stdout F
[13:16:41 INF] The response was successfully returned as a challenge response: {...}. stdout F
[13:16:41 INF] Request finished HTTP/1.1 GET https://auth.main.cargonerds.dev/connect/userinfo - 302 0 null 3.1915ms stdout F
[13:16:42 INF] Request starting HTTP/1.1 GET http://auth.main.cargonerds.dev/health-status - null null stdout F
[13:16:42 INF] Executed endpoint 'Health checks' stdout F
[13:16:42 INF] Request finished HTTP/1.1 GET https://auth.main.cargonerds.dev/health-status - 200 null application/json 5.0224ms stdout F
[13:16:47 INF] Request starting HTTP/1.1 GET http://auth.main.cargonerds.dev/connect/authorize?... stdout F
2 Answer(s)
-
0
In a distributed ABP Auth Server setup (such as Azure Aspire with multiple instances), you must ensure that both Data Protection keys and OpenIddict cryptographic keys are shared and synchronized across all instances. If not, you will see errors like "invalid_grant", "invalid_token", or "The signing key associated to the specified token was not found."
Using Azure Cache for Redis is a valid approach, but you must ensure:
- All instances use the same Redis connection string.
- Data Protection is configured to persist keys in Redis (not in-memory or local file).
- OpenIddict is configured to use distributed key storage (not ephemeral or per-instance).
To make your ABP Auth Server reliable in this environment:
- Configure Data Protection to use Redis for key storage:
context.Services.AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys");
See: [Distributed Data Protection](https://abp.io/docs/abp/latest/Distributed-Data-Protection)
- Configure OpenIddict to use distributed key storage (not ephemeral keys):
options.AddDevelopmentEncryptionCertificate() .AddDevelopmentSigningCertificate(); // For production, use a persisted certificate or key storage
See: [Distributed OpenIddict](https://abp.io/docs/abp/latest/Distributed-OpenIddict)
Ensure Redis is healthy and accessible from all instances. Any network or configuration issue can cause key desync.
If you want to remove Redis, you must use another distributed key store (e.g., Azure Blob Storage for Data Protection, or a shared certificate for OpenIddict). Do not use in-memory or local file storage in a multi-instance environment.
Restart all instances after changing key storage configuration to ensure all use the same keys.
If you see "The signing key associated to the specified token was not found," it means the instance that issued the token is not sharing its keys with others. This is almost always a misconfiguration of Data Protection or OpenIddict key storage.
For more details and code samples, see:
Summary:
You must use a distributed key store (like Redis or Azure Blob Storage) for both Data Protection and OpenIddict keys, and ensure all instances are configured identically. Do not use local or ephemeral keys in a multi-instance environment.Sources:
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
response links are not working.