AADSTS700024: Client assertion is not within its valid time range (fix)
Short answer: AADSTS700024 means an app signed in with a client assertion (a signed JWT, or a federated token from GitHub Actions or Azure DevOps) that was expired or not yet valid. Microsoft's error reference doesn't list it. The usual cause is a long CI/CD job that reuses its first token. Low attack value. Fetch a fresh token right before each use.
Fix: Developer: get a fresh token right before each use. GitHub Actions: request Azure tokens early, or run `az login` again before late steps. Azure DevOps past about 1 hour: use workload identity federation with a managed identity. Self-signed JWT: build a new assertion for each request. Full steps
| Code | AADSTS700024 |
|---|---|
| In Microsoft's error reference | No (checked ). Wording on this page is what Entra returned or what users report. |
| Category | App configuration |
| Type | Failure |
| Attack signal | Low: usually configuration or user error |
| Microsoft lookup | login.microsoftonline.com/error?code=700024 |
What does AADSTS700024 mean?
Section titled “What does AADSTS700024 mean?”Microsoft’s error code reference doesn’t list AADSTS700024 (checked 30 October 2026).
The lab reproduced it. A client-credentials request used a GitHub Actions OIDC token as the client assertion (through a federated credential), sent after that token had expired. Entra returned:
AADSTS700024: Client assertion is not within its valid time range. Current time: 2026-10-30T17:08:40.4130764Z, assertion valid from 2026-10-30T16:52:40.0000000Z
The lab’s record cuts the message at about 160 characters, so the rest of the line is missing here. A user on Microsoft Q&A posted a full message with the expiry time instead: “Current time: 2022-03-18T11:28:52.5620799Z, expiry time of assertion 2022-03-15T11:56:47.0000000Z.” A later reply in the same thread quotes both “assertion valid from” and “expiry”. The exact wording of the second half varies between reports. Either way, compare the times in it with the nbf and exp of the token you sent.
A client assertion is a signed token the app sends instead of a client secret. It is either a JWT the app signs with its own certificate, or a token from another identity provider that a federated credential trusts, such as GitHub Actions or Azure DevOps. Microsoft’s certificate credentials page describes its time claims. nbf is “the time before which the JWT MUST NOT be accepted” and exp is “the expiration time on or after which the JWT must not be accepted”. 700024 means the current time is outside that window.
Microsoft does document the code on another page. Its Azure DevOps workload identity troubleshooting page lists:
AADSTS700024: Client assertion is not within its valid time range
If the error happens after approximately 1 hour, use a service connection with Workload identity federation and a Managed Identity instead. […] If the error happens before 1 hour but after 10 minutes, move commands that (implicitly) request an access token to e.g. access Azure storage to the beginning of your script.
Reproduced in the lab
Section titled “Reproduced in the lab”On 30 October 2026 the lab ran a GitHub Actions job that signs in to my lab tenant through a federated credential. It sent a client-credentials token request with the job’s OIDC token as the client assertion, after the token had expired. Entra returned AADSTS700024, first line quoted above.
An earlier lab run the same day sent the same kind of request about 60 seconds after exp and got a token back (lab run issue); the run above waited about 11 minutes. Our reading: Entra allows some clock skew. Two runs don’t show where the limit is, and Microsoft doesn’t publish one.
The message gives the current time and the start of the token’s validity, 16 minutes apart. That makes it easy to see how old the assertion was. It lands in AADServicePrincipalSignInLogs: two rows (one per run), Failure. On the failed rows the client credential type is “none”, while the app’s successful sign-ins show “federatedIdentityCredential”, so don’t filter on that field to find these. Entra’s explanation on the row reads: “The app used an expired client assertion. It needs to be updated in the Azure Portal to generate a new client secret or certificate.” That advice doesn’t fit a federated credential, as far as we can tell; the fix is a fresh assertion, as below. The row’s failure reason is a template with placeholders ({curTime}, {validTime}, {expTime}); the real times are only in the error the app got. The KQL below was run on these rows (how). The first two queries found the rows. The unfamiliar-IP query returned nothing, which is correct here: both failing IPs also had successful sign-ins.
Is it an attack?
Section titled “Is it an attack?”Usually no. It is a timing problem in the app or pipeline.
| Pattern | Likely meaning |
|---|---|
| A GitHub Actions or Azure DevOps job fails partway through, after its first Azure commands worked | The job outlived its federated token. A reply in the Azure/login repository says the GitHub token’s “lifetime of it is 5 mins” (issue #441). Users report failures once Azure CLI runs past about 10 minutes (issue #372). |
| Azure DevOps fails after about 1 hour, or between 10 minutes and 1 hour | The two cases Microsoft’s DevOps page describes, quoted above. |
| An app with a self-signed certificate assertion fails on every request | In Q&A 778031 the expiry in the message stayed days in the past while the user’s code set a fresh one. Our reading: the app sends a cached or reused assertion, sets nbf/exp wrongly, or runs on a host whose clock or time zone is off. A reply in that thread suspects time zones. |
| 700024 from an IP that never signs in successfully for that app | Possibly an old assertion replayed from somewhere else (unconfirmed). Entra rejected it, but a leaked token means something leaked. Check where it came from. |
How do I find it in my logs (KQL)?
Section titled “How do I find it in my logs (KQL)?”Microsoft doesn’t document which sign-in table 700024 is logged in. An app signing in as itself should land in AADServicePrincipalSignInLogs (our lab rows did), but check where it lands in your workspace:
union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs| where TimeGenerated > ago(30d)| where ResultType == "700024"| summarize Rows = count(), LastSeen = max(TimeGenerated) by SourceTable, AppIdWhich apps fail, with which credential type and federated credential. FederatedCredentialId is documented as the ID of “an application’s federated identity credential if a federated identity credential was used to sign in”. Next to the failures, the query counts the same app’s successes, which shows whether a job got its first tokens before failing:
let fails = AADServicePrincipalSignInLogs | where TimeGenerated > ago(7d) | where ResultType == "700024" | summarize Failures = count(), LastFail = max(TimeGenerated), FailIPs = make_set(IPAddress, 10) by AppId, ServicePrincipalName, ClientCredentialType, FederatedCredentialId;let wins = AADServicePrincipalSignInLogs | where TimeGenerated > ago(7d) | where ResultType == "0" | summarize Successes = count() by AppId;fails| join kind=leftouter wins on AppId| project AppId, ServicePrincipalName, ClientCredentialType, FederatedCredentialId, Failures, Successes, LastFail, FailIPs| sort by Failures desc700024 from IP addresses that never succeed for that app may point to replay. Runners with changing IPs produce false positives, so treat hits as a starting point:
let goodIPs = AADServicePrincipalSignInLogs | where TimeGenerated > ago(30d) | where ResultType == "0" | distinct AppId, IPAddress;AADServicePrincipalSignInLogs| where TimeGenerated > ago(7d)| where ResultType == "700024"| join kind=leftanti goodIPs on AppId, IPAddress| summarize Attempts = count(), LastSeen = max(TimeGenerated) by AppId, ServicePrincipalName, IPAddress| sort by Attempts descHow do I fix AADSTS700024?
Section titled “How do I fix AADSTS700024?”For developers:
- GitHub Actions: request Azure tokens early, or log in again before late steps that need Azure. Users in issue #372 report that Azure CLI does not refresh the ID token on its own. A community workaround there re-runs
az loginwith a fresh federated token every 240 seconds. Splitting a long job into shorter jobs should also work, as each job gets its own token. We haven’t tested this. - Azure DevOps: follow Microsoft’s two cases quoted above. Past about 1 hour, use workload identity federation with a managed identity. Between 10 minutes and 1 hour, move the commands that need a token to the start of the script, so the cached access token covers the rest.
- Self-signed assertions: build a new assertion for each token request. Microsoft’s certificate credentials page says to use the current time for
nbfand to keepexp“short - 5-10 minutes afternbfat most”. - Clock: a host clock that runs ahead or behind likely gives the same error even with a fresh assertion (unconfirmed). Keep the clock in sync.
For admins:
- Find the app with the queries above and give its owner the time of the failures.
- Check whether the failures sit late in long pipeline runs. If so, it is the token lifetime, not the federated credential setup.
- If failures come from IPs your pipelines don’t use, find out how the assertion got there.
Related codes
- AADSTS7000215: Invalid client secret is provided
- AADSTS7000222: The provided client secret keys are expired
- AADSTS7000218: Client secret or assertion missing from the request
- AADSTS700016: Application not found in the directory
- AADSTS50013: invalid assertion in on-behalf-of flow
- AADSTS50027: JWT token is invalid or malformed
More: 90023
Sources
- Microsoft Entra authentication and authorization error codes
- Troubleshoot an Azure Resource Manager workload identity service connection (Azure DevOps)
- Microsoft identity platform application authentication certificate credentials
- Microsoft Q&A: Client assertion is not within its valid time range (778031)
- Azure/login issue #441 (GitHub)
- Azure/login issue #372 (GitHub)
- AADServicePrincipalSignInLogs table reference (Azure Monitor)