Key takeaways
- ASP.NET Identity token expiration is controlled in three separate places: the 30-minute security stamp validation interval, the 1-day DataProtection token lifespan, and your own JwtBearerOptions.
- SecurityStampValidator.OnValidateIdentity re-checks the security stamp on a configurable validationInterval, so credential changes invalidate sessions within that window rather than instantly.
- DataProtectionTokenProviderOptions.TokenLifespan defaults to one day, and the deadline is embedded in each token at generation time, so changes only affect tokens minted afterward.
- ASP.NET Identity has no built-in refresh token; you must add your own store, endpoint, and rotation logic, typically pairing short-lived access tokens with long-lived refresh tokens.
- Set JwtBearerOptions.ClockSkew (default five minutes) deliberately and test expiration with 60-second token lifetimes and zero ClockSkew so expiry is assertable in integration tests.
ASP.NET Identity token expiration is controlled in three separate places: the security stamp validation interval (30 minutes by default), the DataProtection token provider lifespan (one day), and JwtBearerOptions with a five-minute ClockSkew. This article walks each code path with runnable configuration so you can shorten or extend token lifetimes without logging users out mid-session.
What is the default token expiration in ASP.NET Identity?
ASP.NET Identity has three separate expiration clocks, and they are not synchronized. Knowing where each default lives is the difference between a controlled rotation and a surprise logout.
Security stamp validation: 30 minutes by default
SecurityStampValidator.OnValidateIdentity re-checks the user's security stamp on a validationInterval of 30 minutes. When the stamp changes (password reset, email change, 2FA change), all sessions invalidate within that window. The Microsoft Learn documentation for ASP.NET Core Identity covers the validator configuration path.
DataProtection token lifespan: 1 day by default
Email confirmation, password reset, and 2FA tokens use DataProtectionTokenProviderOptions, which defaults to a 1-day TokenLifespan.
JWT bearer tokens: no Identity default at all
If you issue JWTs, the lifetime comes from your JwtBearerOptions or token-issuing code, not Identity. Typical setups use 15-60 minutes for access tokens plus a refresh token.
| Token type | Default | Where it's configured |
|---|---|---|
| Security stamp validation | 30 min | SecurityStampValidator.OnValidateIdentity |
| DataProtection tokens | 1 day | DataProtectionTokenProviderOptions |
| JWT bearer access token | None | Your JwtBearerOptions / issuer |
The same expiry-tracking problem applies beyond your app's tokens: API keys and secrets in AWS Secrets Manager carry deadlines nobody owns. KeyGuardRail's guide to credential expiration reminder tools covers monitoring that metadata without holding the secret values.
How does SecurityStampValidator control session invalidation?
SecurityStampValidator is the mechanism that turns a stored security stamp into forced sign-outs. Identity stamps every user record with a security stamp value and bakes it into the auth cookie. On an interval, the validator re-reads the stamp from the database and compares it to the cookie. If they differ, the cookie is rejected and the session dies.
What triggers revalidation
The cookie itself doesn't carry a "revalidate me" flag; the OnValidatePrincipal callback wired by SecurityStampValidatorOptions runs on every request that falls inside the validation window. A stamp change comes from UpdateSecurityStampAsync, which Identity calls internally on password change, email change, 2FA changes, and SignOutAsync everywhere.
Changing the validationInterval tradeoff
validationInterval (default 30 minutes) controls how often the database is actually hit:
options.ValidationInterval = TimeSpan.FromMinutes(15);
Shorter intervals invalidate sessions faster but add a user query per window. Longer intervals reduce DB load but let a compromised session live up to that long after a stamp change.
Invalidating sessions on password change or 2FA reset
You don't configure invalidation per event; you configure the interval and let stamp updates do the work. Calling UpdateSecurityStampAsync after any credential change (password reset, 2FA reset, API key revocation) invalidates every session within one validationInterval. Treat that interval like any expiring credential: pick it deliberately, document it in your rotation policy alongside your 30/60/90 day schedules, and review it the same way you review an API key expiration policy.
How do you change access token lifetime in ASP.NET Identity?
Three settings govern token lifetime, one per token type: the auth cookie, DataProtection tokens, and JWTs.
CookieAuthenticationOptions ExpireTimeSpan and SlidingExpiration
In Program.cs, set ExpireTimeSpan on AddCookie to extend or shorten the session cookie. SlidingExpiration = true (the default) renews the cookie on every request past half the lifetime, so active users never hit expiry; set it to false for a hard cap. If you use SecurityStampValidator, keep ValidationInterval in mind: the cookie can outlive its ExpireTimeSpan only until the next validation pass.
DataProtectionTokenProviderOptions.TokenLifespan
Password reset, email confirmation, and 2FA codes use DataProtection tokens with a default lifespan of one day (Microsoft Learn). Override it in AddIdentity:
services.Configure<DataProtectionTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromHours(2));
Shorten it for reset links; extending it past a day invites stale tokens lingering in inboxes and Postman collections. This is the same sprawl problem you face with AWS and Stripe keys: someone has to own the expiry. KeyGuardRail tracks credential lifecycles like these without holding the secret value, and our October integration brief covers wiring expiry metadata into existing vaults. Where it falls short: it monitors lifecycles, it does not rotate secrets for you.
JwtBearerOptions and TokenValidationParameters
For JWTs, set TokenValidationParameters.ClockSkew and validate exp server-side; the token's own lifetime comes from your SecurityTokenDescriptor.Expires. Shortening ClockSkew from the default five minutes tightens real expiry without touching issuance.
Why do DataProtection tokens expire differently than JWTs?
ASP.NET Identity runs two independent token systems, and each has its own clock and validator. A JWT access token lives in your bearer options (default 1 hour in most setups), while DataProtection-generated tokens like password-reset links, email confirmation, and 2FA codes use DataProtectionTokenProviderOptions.TokenLifespan, which defaults to 1 day. That is why a reset link can outlive an access token by 23 hours: they are not the same pipeline.
Purpose-scoped protectors and embedded timestamps
DataProtection tokens are created with a purpose string (e.g. UserSecurityStamp or ResetPassword), so a token minted for one flow fails validation in another. Expiry is embedded in the payload at generation time and checked at validation time, not read from config afterward. Changing TokenLifespan only affects tokens minted after the change; outstanding links keep their original deadline.
Clock skew and server time drift
Both validators compare embedded timestamps against server time, so drift between app servers (or between the app and your database-backed security stamp) causes intermittent "expired" errors even when config looks right. Sync NTP on every instance and keep SecurityStampValidator intervals in mind when coordinating expiring secrets across systems. For the rotation side of that coordination, see https://keyguardrail.com/blog/alternatives-manual-credential-rotation-tracking.
How do refresh tokens interact with Identity's expiration settings?
ASP.NET Identity has no built-in refresh token. Access tokens come from your JWT bearer setup, and Identity's own tokens (auth, 2FA, stamp validation) are separate. If you want refresh tokens, you add them yourself: a store, an endpoint, and rotation logic.
Rolling your own refresh token store
Keep refresh tokens out of the vault. They are app-issued values, so a table keyed by user ID with hashed token, expiry, and device info works. On refresh, validate the incoming token, revoke it, and issue a new pair. Reuse of a revoked token should kill the family, since that is your replay signal.
Short-lived access plus long-lived refresh: worked example
Set access token lifetime to 10 minutes, refresh token to 14 days with a 30-day absolute cap, and rotate on every use. Keep the sliding behavior of your cookie pipeline in mind: ExpireTimeSpan and SlidingExpiration govern the cookie independently of your JWTs. Track the API keys your refresh flow ultimately calls, since expiry there breaks auth silently; see https://keyguardrail.com/blog/how-to-track-api-key-expiry for a metadata-only approach.
How do you handle expired tokens without logging users out mid-session?
SlidingExpiration tradeoffs
SlidingExpiration = true with a 20-minute cookie lifetime resets the timer on every request, so active users stay logged in while idle sessions expire. The tradeoff: a stolen cookie stays valid indefinitely as long as the attacker keeps using it. A middle path is sliding expiration with a hard ExpirationTimeSpan cap via SecurityStampValidator intervals, so even active sessions revalidate periodically.
Silent refresh from the client
For SPAs and mobile clients, refresh before expiry rather than after a 401. If you issue JWTs with a short AccessTokenExpiration, pair them with a refresh token and refresh on a schedule slightly shorter than the access token lifetime, or on 401 with a single retry. Coordinating this is the same ownership problem as any expiring credential: someone has to know when each token path expires and who fixes it when it breaks.
Grace periods and ClockSkew
JwtBearerOptions.ClockSkew defaults to 5 minutes, meaning tokens are honored up to 5 minutes past stated expiry. Reduce it deliberately, and use OnAuthenticationFailed to distinguish expired tokens from invalid ones so clients can refresh instead of dying mid-session.
How do you test token expiration behavior in ASP.NET Identity?
Shorten lifespans in integration tests
Set access token lifetime to 60 seconds and ClockSkew to zero in your test host so expiry happens on a timescale you can actually assert against. TokenValidationParameters.ClockSkew defaults to five minutes, which masks short lifetimes unless you zero it out (Microsoft Learn). Override the JWT bearer options and SecurityStampValidator intervals per environment through IOptions configuration, not hardcoded values.
Asserting on 401 vs redirect behavior
Use WebApplicationFactory to spin up the app in memory, mint a token with a two-second lifetime, sleep past expiry, then hit a protected endpoint. For API routes, assert a 401 with the right WWW-Authenticate header; for MVC routes, assert a 302 to the login path. This distinction catches the common failure where expired tokens return 401 to browsers that expected a redirect, or vice versa.
How does token expiration fit into a broader credential lifecycle policy?
Identity tokens are one credential type among many. Your AWS access keys, Stripe API keys, and GitHub PATs all expire or need rotation on their own schedules, and each one needs a named owner. Map Identity's token lifetimes onto the same 30/60/90-day rotation policy you apply to long-lived secrets: short-lived access tokens align with the short end, refresh tokens and security stamp intervals with the longer tiers.
Inventory what expires and who owns it
List every credential with three fields: owner (team, not person), expiry date, and rotation deadline. Tokens configured in code, like SecurityStampValidator intervals, belong in the same inventory as Secrets Manager entries and Postman collections, because humans created all of them and humans forget them.
Where vaults stop and lifecycle monitoring starts
Vaults like HashiCorp Vault, Infisical, and Doppler store and gate secret values well. What they generally do not give you is a cross-provider view of who owns each credential and when it lapses. That is the gap KeyGuardRail targets: credential lifecycle monitoring without secret custody, tracking ownership, expiry, and rotation deadlines across AWS and SaaS providers. It does not rotate secrets for you; it tells you what expires and who owns what happens next.
FAQ
How long does a password reset token last in ASP.NET Identity?
One day by default, governed by DataProtectionTokenProviderOptions.TokenLifespan. Override it with services.Configure<DataProtectionTokenProviderOptions> in AddIdentity, for example to two hours. The deadline is embedded in the token payload at generation time, so shortening the lifespan only affects tokens minted after the change; outstanding reset links keep their original expiry.
Can I set different token lifetimes per client or per user?
Not through the default DataProtection provider, which applies one global TokenLifespan. For per-client lifetimes, issue JWTs yourself and set Expires on the SecurityTokenDescriptor per request, since Identity imposes no default there. For per-user behavior, branch on user claims or roles when minting tokens, or configure ValidationInterval differently per authentication scheme.
Does changing the security stamp interval log everyone out?
No. Changing ValidationInterval only affects how quickly future stamp changes propagate, not the stamps themselves. A logout wave happens when stamps actually change, for example after a password reset or 2FA change, and even then sessions die within one interval, not instantly. Lowering the interval just makes existing sessions revalidate against the database more often.
What is the difference between ExpireTimeSpan and TokenLifespan?
ExpireTimeSpan lives on CookieAuthenticationOptions and controls the auth cookie lifetime, with SlidingExpiration renewing it for active users. TokenLifespan lives on DataProtectionTokenProviderOptions and controls one-shot tokens like password reset and email confirmation. They run on separate clocks: a reset link can outlive a session cookie, and changing one does nothing to the other.
How do I revoke a JWT access token before it expires?
You cannot revoke a stateless JWT directly; clients keep it until exp passes. Practical options: keep the token lifetime short (10-15 minutes), maintain a server-side denylist checked per request, or embed the security stamp as a claim and validate it in OnTokenValidated so UpdateSecurityStampAsync kills outstanding tokens. Reduce ClockSkew so revoked tokens die sooner.