SAML assertion not yet valid, or already expired
The sign-in fails on the way back to the application with Assertion is not yet valid, NotBefore condition not met, Assertion has expired, Response is too old or a generic SSO failure, and it often comes and goes. Nearly always, two computers disagree about what time it is.
The timestamps that matter
| Attribute | Where | What the SP checks |
|---|---|---|
IssueInstant | Response and Assertion | When the IdP created it. Some SPs reject responses older than a few minutes. |
NotBefore | Conditions | The assertion may not be used before this time. IdPs often set it to the issue time, so a server whose clock is even a second behind rejects it. |
NotOnOrAfter | Conditions | The assertion may not be used at or after this time. Typically a few minutes to an hour after issue. |
NotOnOrAfter | SubjectConfirmationData | The deadline for delivering it to the ACS URL. Usually short, around five minutes. |
SessionNotOnOrAfter | AuthnStatement | How long the app's own session may last. Not a validity check on the assertion itself. |
All SAML times are UTC and end in Z, for example 2026-11-03T14:05:12Z.
Read the gap, then pick the cause
Work out how far the SP's clock was from the window when it rejected the response. The size of the gap usually tells you the cause.
- A few seconds to a few minutes, "not yet valid": the SP's clock is behind the IdP's. Check time sync on the SP servers.
- A few minutes, "expired": either the SP's clock is ahead, or the response really arrived late (see replays below).
- Exactly one or more whole hours: not drift - a time-zone bug. The SP is parsing the UTC timestamp as local time, or a server is set to local time with the wrong zone. It typically starts or stops when daylight saving time changes.
- Hours or days, "expired": an old response was replayed - the user pressed Back or Refresh on the post-sign-in page and the browser re-sent the old form, or a bookmark points at the ACS URL.
Fix the clocks first
Make sure every SP server (and the IdP, if you run it) syncs time automatically. Virtual machines resumed from a snapshot and containers on a host with a stopped time service are the usual offenders.
# Linux (chrony / systemd) chronyc tracking timedatectl status # Windows w32tm /query /status w32tm /resync
Then allow a small skew
Even with good time sync, servers differ by a second or two, so SPs should accept a little drift. One to three minutes is a common allowance; five is a sensible upper bound. Most SAML libraries call it clock skew or allowed clock drift. If you control the IdP rather than the SP:
- ADFS: set
NotBeforeSkew(in minutes) on the relying party trust, which movesNotBeforeback by that much:Set-AdfsRelyingPartyTrust -TargetName "App" -NotBeforeSkew 2. - Other IdPs: look for a token lifetime or skew setting on the app. Longer lifetimes help with "expired", but not with "not yet valid".
Don't paper over a large or whole-hour gap with a huge skew allowance. It weakens replay protection and leaves the real bug in place.
How AuthTrace shows it
Open the AuthTrace panel in DevTools before signing in. The diagnosis says Assertion not valid yet (clock skew) or Assertion expired when it arrived, with the size of the gap and whose clock it was measured against - the SP's own Date header when there is one. The skew allowance is a setting, so you can match what your SP tolerates. AuthTrace also compares the server's HTTP Date header with your computer's clock, so Server clock differs from your computer points straight at a drifting server.
More guides
Signature validation failed
Usually a rolled-over signing certificate. How to compare fingerprints, and the four other reasons a valid-looking signature is refused.
ENTRAFix AADSTS50011 / redirect_uri mismatch
The redirect URI or reply URL the app sent is not registered. Find the exact value, spot the difference, and register it in the right place.
OAUTHFix invalid_grant
The code-for-token swap fails. Usually the code was redeemed twice or the PKCE verifier doesn't match - how to tell which.