{"record":{"id":"3fe2a5e679a970a4","repo":"dotnet/aspnetcore","slug":"the-antiforgery-token-could-not-be-decrypted","errorCode":null,"errorMessage":"The antiforgery token could not be decrypted.","messagePattern":"The antiforgery token could not be decrypted\\.","errorType":"exception","errorClass":"AntiforgeryValidationException","httpStatus":null,"severity":"error","filePath":"src/Antiforgery/src/Internal/DefaultAntiforgeryTokenSerializer.cs","lineNumber":86,"sourceCode":"                    return token;\n                }\n            }\n        }\n        catch (Exception ex)\n        {\n            // swallow all exceptions - homogenize error if something went wrong\n            innerException = ex;\n        }\n        finally\n        {\n            if (tokenBytesRent is not null)\n            {\n                ArrayPool<byte>.Shared.Return(tokenBytesRent);\n            }\n        }\n\n        // if we reached this point, something went wrong deserializing\n        throw new AntiforgeryValidationException(Resources.AntiforgeryToken_DeserializationFailed, innerException);\n    }\n\n    /* The serialized format of the anti-XSRF token is as follows:\n     * Version: 1 byte integer\n     * SecurityToken: 16 byte binary blob\n     * IsCookieToken: 1 byte Boolean\n     * [if IsCookieToken != true]\n     *   +- IsClaimsBased: 1 byte Boolean\n     *   |  [if IsClaimsBased = true]\n     *   |    `- ClaimUid: 32 byte binary blob\n     *   |  [if IsClaimsBased = false]\n     *   |    `- Username: UTF-8 string with 7-bit integer length prefix\n     *   `- AdditionalData: UTF-8 string with 7-bit integer length prefix\n     */\n    private static AntiforgeryToken? Deserialize(ReadOnlySpan<byte> tokenBytes)\n    {\n        // Minimum lengths:\n        // - Cookie token: 1 (version) + 16 (securityToken) + 1 (isCookieToken) = 18 bytes","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenSerializer.cs#L68-L104","documentation":"Thrown by DefaultAntiforgeryTokenSerializer.Deserialize (line 86) when the serialized token cannot be decrypted or parsed. The method catches all exceptions during base64-decoding, data-protection Unprotect, and binary deserialization, then throws a homogenized AntiforgeryValidationException with the original error as innerException. Common underlying causes include data-protection key loss, corrupted tokens, or format-version mismatches.","triggerScenarios":"Deserialize is called with a token string that fails any step in: WebEncoders.Base64UrlDecode, _defaultCryptoSystem.Unprotect (data protection), or the private Deserialize(ReadOnlySpan<byte>) format parser (which returns null for wrong version/length/trailing bytes).","commonSituations":"Data protection keys were not persisted and the app restarted (in-memory keys lost); the app moved to a different machine/container without persisting the key ring; multiple apps sharing the same cookie name but different data-protection purposes; the token string was truncated or URL-mangled; a version upgrade changed the token format (TokenVersion mismatch).","solutions":["Persist data protection keys to a durable store (Redis, SQL Server, Azure Blob, filesystem) so they survive restarts and are shared across instances: builder.Services.AddDataProtection().PersistKeysToRedis(...).","Ensure all application instances use the same key ring and application discriminator (SetApplicationName).","If keys were genuinely lost, clear the old antiforgery cookie in the browser and call GetAndStoreTokens to mint fresh tokens under the new key ring.","Check that the token isn't being URL-encoded/decoded inconsistently between generation and consumption."],"exampleFix":"// before — ephemeral in-memory keys (lost on restart)\nbuilder.Services.AddDataProtection();\n\n// after — persist keys to Redis for multi-instance durability\nvar redis = ConnectionMultiplexer.Connect(redisConnStr);\nbuilder.Services.AddDataProtection()\n    .PersistKeysToStackExchangeRedis(redis, \"DataProtection-Keys\")\n    .SetApplicationName(\"my-app\");","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    await _antiforgery.ValidateRequestAsync(HttpContext);\n}\ncatch (AntiforgeryValidationException ex) when (ex.Message.Contains(\"could not be decrypted\"))\n{\n    // Clear the stale cookie and redirect to get fresh tokens\n    Response.Cookies.Delete(\".AspNetCore.Antiforgery\");\n    return Redirect(Request.Path);\n}","preventionTips":["Persist data-protection keys to a durable store (Redis/SQL/Blob) in all environments.","Share the same SetApplicationName across all instances that must validate each other's tokens.","Don't share cookie names between apps with different data-protection purposes."],"tags":["antiforgery","security","data-protection","encryption","serialization"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}