Skip to content
ecdsa.com
Microsoft.IdentityModel.Tokens (.NET).NET

Error message

IDX10223: Lifetime validation failed. The token is expired.

Also printed as

  • Bearer error="invalid_token", error_description="The token expired at '10/20/2026 06:28:30'"
  • IDX10222: Lifetime validation failed. The token is not yet valid.

Text after "The token is expired." contains both timestamps in the server's culture format. The ASP.NET Core `WWW-Authenticate` description is produced by `JwtBearerHandler`.

Microsoft.IdentityModel.Tokens (.NET) — SecurityTokenExpiredException

What it means

The token's `exp` is earlier than the current time minus `ClockSkew` (five minutes by default). The signature and other claims may be valid; the token is simply past its lifetime. The message goes on with "ValidTo (UTC): …, Current time (UTC): …", and ASP.NET Core reports `error_description="The token expired at '…'"` to the client. IDX10222 is the opposite case: `nbf` lies in the future.

Why it happens

How to fix it

  1. 1.

    Keep a small clock skew

    Tolerate drift of seconds, not minutes, and fix the clocks. Never set `ValidateLifetime = false` in production.

    csharp
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = "https://login.example.com/",
        ValidAudience = "api://orders",
        ClockSkew = TimeSpan.FromSeconds(30), // default is 5 minutes; lower it, do not raise it to hide expiry
    };
  2. 2.

    Acquire tokens through the library on every call

    `AcquireTokenSilent` returns a valid token from cache or refreshes it, so expiry is handled before the request.

    csharp
    // Client side (MSAL): ask for a token every time; MSAL returns the cached one or refreshes it.
    var result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync();
    httpClient.DefaultRequestHeaders.Authorization = new("Bearer", result.AccessToken);

Related errors

Browse the full signature error database