Skip to content

JWT controller claim helpers

This operation provides two claim-derivation helpers on JwtControllerBase: deriving a display username from the nameidentifier-type claim, and parsing the authentication time from the auth_time claim.

Field Value
Operation ID OP-22
Trigger A controller derived from JwtControllerBase calls GetUsernameFromClaim() or GetAuthTimeFromClaim().
Inputs The current user's ClaimsPrincipal (via HttpContext.User).
Outputs A derived username string, or a parsed DateTime authentication time.
Primary components JwtControllerBase.GetUsernameFromClaim, .GetAuthTimeFromClaim
Entry point src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/JwtControllerBase.cs (L14–40)

Steps

  1. GetUsernameFromClaim: find the first claim whose type contains "nameidentifier", split its value on @, take index [0], upper-case the result (BR-SE-27)
  2. On exception during parsing: returns "Unknown".
  3. If the claim is missing: returns null.
  4. GetAuthTimeFromClaim: find the auth_time claim and, only if long.TryParse succeeds on its value, parse it as Unix seconds since epoch via DateTimeOffset.FromUnixTimeSeconds(unixTimestamp).UtcDateTime (BR-SE-27)
  5. If the claim is missing or its value is not a parseable long: returns DateTime.MinValue without throwing.
  6. If the value parses as a long but is outside the range DateTimeOffset.FromUnixTimeSeconds supports: the method has no try/catch, so ArgumentOutOfRangeException propagates uncaught to the caller — this is a distinct third outcome from the two above.

Process flow

This diagram shows the per-call branching inside each helper on a single invocation of GetUsernameFromClaim/GetAuthTimeFromClaim, not a combined or sequential call to both.

flowchart TD
    Start([GetUsernameFromClaim / GetAuthTimeFromClaim called]) --> S1{Which helper?}
    S1 -->|Username| S2{nameidentifier claim present?}
    S2 -->|No| EndNull([Return null])
    S2 -->|Yes| S3{Parsing succeeds?}
    S3 -->|Yes| EndName([Return upper-cased local part])
    S3 -->|No| EndUnknown(["Return 'Unknown'"])
    S1 -->|Auth time| S4{auth_time claim present and long.TryParse succeeds?}
    S4 -->|No| EndMin([Return DateTime.MinValue])
    S4 -->|Yes| S5{"Value in FromUnixTimeSeconds range?"}
    S5 -->|Yes| EndTime([Return parsed DateTime])
    S5 -->|No| EndThrow(["ArgumentOutOfRangeException propagates uncaught"])

Failure and edge-case behavior

  • Any exception while parsing the username claim yields "Unknown"; a missing claim yields null (these are distinct outcomes).
  • A missing or non-numeric auth_time claim yields DateTime.MinValue rather than throwing.
  • A numeric auth_time claim value outside the range DateTimeOffset.FromUnixTimeSeconds supports throws ArgumentOutOfRangeException, uncaught, from GetAuthTimeFromClaim — this method has no try/catch around the conversion, unlike GetUsernameFromClaim.

Sources

  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/JwtControllerBase.cs