dotnet/aspnetcore · error · AntiforgeryValidationException
The antiforgery token could not be decrypted.
Error message
The antiforgery token could not be decrypted.
What it means
Thrown by the antiforgery token deserializer when the supplied token bytes cannot be deserialized for any reason (format, version, crypto, length). The serializer deliberately swallows and homogenizes the underlying exception into a single AntiforgeryValidationException to avoid leaking why decryption failed.
Solutions
- Persist and share DataProtection keys across all instances (configure AddDataProtection().PersistKeysToFileSystem/Redis with the same key ring and application discriminator).
- Verify all nodes in the farm share the same antiforgery application name (AntiforgeryOptions or ApplicationDiscriminator).
- Clear the stale antiforgery cookie on the client and obtain a fresh token from the server.
- If crossing app versions/migrations, force a token refresh by rotating the data-protection keys.
Example fix
// before: ephemeral keys per instance
builder.Services.AddDataProtection();
// after: shared persistent key ring across the farm
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("/shared/keys"))
.SetApplicationName("MyApp"); Defensive patterns
Strategy: try-catch
Try / catch
try { await antiforgery.ValidateRequestAsync(httpContext); }
catch (AntiforgeryValidationException ex) when (ex.InnerException != null) { logger.LogWarning("Token decryption failed; possible key ring mismatch"); return Results.BadRequest(); } Prevention
- Persist DataProtection keys to shared storage across all instances.
- Set a stable ApplicationName/SetApplicationName so the key ring is consistent.
- Log decryption failures distinctly to detect farm key drift quickly.
- Rotate keys deliberately during migrations and document the rollout.
When it happens
Trigger: GetRequestTokensAsync returns a request or cookie token string whose base64-decoded bytes do not deserialize into a valid AntiforgeryToken — e.g. tampered token, stale token from a previous app version, machine key mismatch across farm nodes, or a token issued under different data-protection keys.
Common situations: Deploying to a web farm without shared DataProtection keys; an app restart that lost ephemeral keys (no persistent key ring); token issued by an older incompatible version; client-side manipulation of the cookie; multiple apps sharing the same cookie domain with different keys.
Related errors
- The antiforgery system has the configuration value
- The provided identity of type
- The required antiforgery cookie
- The required antiforgery form field
- The required antiforgery header value
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/3fe2a5e679a970a4.
Report an issue: GitHub.
Appendix: source
Thrown at src/Antiforgery/src/Internal/DefaultAntiforgeryTokenSerializer.cs:86
return token;
}
}
}
catch (Exception ex)
{
// swallow all exceptions - homogenize error if something went wrong
innerException = ex;
}
finally
{
if (tokenBytesRent is not null)
{
ArrayPool<byte>.Shared.Return(tokenBytesRent);
}
}
// if we reached this point, something went wrong deserializing
throw new AntiforgeryValidationException(Resources.AntiforgeryToken_DeserializationFailed, innerException);
}
/* The serialized format of the anti-XSRF token is as follows:
* Version: 1 byte integer
* SecurityToken: 16 byte binary blob
* IsCookieToken: 1 byte Boolean
* [if IsCookieToken != true]
* +- IsClaimsBased: 1 byte Boolean
* | [if IsClaimsBased = true]
* | `- ClaimUid: 32 byte binary blob
* | [if IsClaimsBased = false]
* | `- Username: UTF-8 string with 7-bit integer length prefix
* `- AdditionalData: UTF-8 string with 7-bit integer length prefix
*/
private static AntiforgeryToken? Deserialize(ReadOnlySpan<byte> tokenBytes)
{
// Minimum lengths:
// - Cookie token: 1 (version) + 16 (securityToken) + 1 (isCookieToken) = 18 bytesView on GitHub (pinned to 3600ca084e)