Synchronize JWT values into claims and cookie¶
| Field | Value |
|---|---|
| Operation ID | OP-05 |
| Trigger | JwtClaimMiddleware.InvokeAsync, every request |
| Inputs | HttpContext, current cookie principal |
| Outputs | Updated claims; re-issued cookie, or an exception |
| Primary components | JwtClaimMiddleware, JwtAuthorizationCookieClaimService.UpdateJwtTokenValues, ClaimsPrincipalExtensions.AddUpdateClaim |
| Entry point | src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Middleware/JwtClaimMiddleware.cs L38–51; src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/JwtAuthorizationCookieClaimService.cs L54–77 |
Steps¶
- If
context.User.Identity.IsAuthenticatedis false, go to step 6 (BR-OIDC-11). info = UpdateJwtTokenValues(context), which callsAuthenticateAsync("Cookies").- On failure: if
info.Failure != null, log Error and returninfo. - Inside the service:
id = idToken ?? GetTokenValue("id_token"),access = accessToken ?? GetTokenValue("access_token").principal.AddUpdateClaim("id_token", id)andAddUpdateClaim("access_token", access);AddUpdateClaimremoves the first matching claim and adds the new one. IfexpiresAthas a value,UpdateTokenValue("expires_at", expiresAt.ToString("o")). ThenUpdateTokenValueid_token/access_token (BR-OIDC-12). NoResult(Failure null, Principal null) is not handled by any guard in this operation: step 1 only checkscontext.User.Identity.IsAuthenticatedon the existing (pre-re-authentication) principal, andJwtAuthorizationCookieClaimService.UpdateJwtTokenValuesindependently re-authenticates the Cookies scheme and only checksinfo.Failure != nullbefore reading tokens. An authenticated incoming context does not guarantee the separateAuthenticateAsync("Cookies")call that follows returns a non-NoResultoutcome. The middleware always invokes the no-explicit-token overload, which forwardsidToken: null, accessToken: nullintoid = idToken ?? info.Properties.GetTokenValue(OpenIdConnectParameterNames.IdToken); onNoResult,info.Propertiesisnull, and ASP.NET Core 8'sAuthenticationTokenExtensions.GetTokenValuecallsArgumentNullException.ThrowIfNull(properties)before any token lookup. ANoResulthere therefore first fails with anArgumentNullExceptionon that properties read, not aNullReferenceExceptiononprincipal.AddUpdateClaim— the principal is never reached. An explicit-token overload call that supplies bothidTokenandaccessTokenwould instead skip both property reads and fail later, atprincipal.AddUpdateClaim, whenprincipalitself is null. Either way this is an unhandled case, not a scenario the code actively guards against*.- If
info.Succeeded,RewriteAuthCookie(principal, properties)→SignInAsync(context, CookieAuthenticationDefaults.AuthenticationScheme, principal, properties)(BR-OIDC-12). - Otherwise throw
HttpContextAuthenticateException(info.Failure)(BR-OIDC-11). - On failure: exception propagates to the error handler.
await next(context).
Note: the cookie is re-issued on every authenticated request*. (performance and Set-Cookie churn implication).
Process flow¶
flowchart TD
Start([Request arrives]) --> AuthCheck{User authenticated?}
AuthCheck -->|No| Next[next context]
AuthCheck -->|Yes| Reauth[AuthenticateAsync Cookies]
Reauth --> FailCheck{info.Failure != null?}
FailCheck -->|Yes| LogErr["Log Error, return info"]
FailCheck -->|No| UpdateClaims[Update id_token/access_token/expires_at claims]
LogErr --> Succeeded{info.Succeeded?}
UpdateClaims --> Succeeded
Succeeded -->|Yes| Resign[RewriteAuthCookie then SignInAsync Cookies]
Succeeded -->|No| Throw[Throw HttpContextAuthenticateException]
Resign --> Next
Next --> Done([Pipeline continues])
Failure and edge-case behavior¶
- A failed cookie re-authentication throws
HttpContextAuthenticateException, which propagates to whatever error handler the host application registers — there is no built-in recovery path. - The cookie is re-signed on every authenticated request, regardless of whether any claim value actually changed*, which is a potential performance and response-size concern under load.
NoResultfromAuthenticateAsync(bothFailureandPrincipalnull) is not handled: an authenticated incoming request (step 1) does not enforce a non-NoResultoutcome from the separate Cookies-scheme re-authentication inUpdateJwtTokenValues. Because the middleware always calls the no-explicit-token overload, the first failure onNoResultis anArgumentNullExceptionthrown byAuthenticationTokenExtensions.GetTokenValuewhen it checks the nullinfo.Properties, beforeprincipal.AddUpdateClaimis ever reached. This can arise, for example, when an authenticated principal was established independently of the registered Cookies scheme (such as a different authentication middleware populatingcontext.User) while that scheme itself returnsNoResult— a host-dependent scenario, not a missing or expired scheme registration, which does not itself produceNoResult.
Related documentation¶
Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.
Sources¶
src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Middleware/JwtClaimMiddleware.cssrc/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/JwtAuthorizationCookieClaimService.cshttps://raw.githubusercontent.com/dotnet/aspnetcore/v8.0.0/src/Http/Authentication.Abstractions/src/TokenExtensions.cs(independent reference forAuthenticationTokenExtensions.GetTokenValue's null-propertiescheck)