Azure Error Guide: 'ExpiredAuthenticationToken' — Refresh the Access Token
Fix the Azure ExpiredAuthenticationToken / InvalidAuthenticationTokenTenant error in the CLI, SDK, and pipelines: refresh the bearer token, fix clock skew, and re-scope tokens to the right tenant so ARM calls stop returning 401.
- #azure
- #cloud
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
Azure Resource Manager (ARM) rejects a request when the bearer token you presented is no longer valid. The Azure CLI, PowerShell, SDKs, and REST callers all surface it as a 401 Unauthorized with this body:
{
"error": {
"code": "ExpiredAuthenticationToken",
"message": "The access token expiry UTC time '2026-07-09T09:14:03.000Z' is earlier than current UTC time '2026-07-09T10:22:41.000Z'."
}
}
Two closely related variants come from the same token-validation layer. A token issued for the wrong tenant returns:
{
"error": {
"code": "InvalidAuthenticationTokenTenant",
"message": "The access token is from the wrong issuer 'https://sts.windows.net/<tenantA>/'. It must match the tenant 'https://sts.windows.net/<tenantB>/' associated with this subscription."
}
}
And a malformed or truncated token returns InvalidAuthenticationToken (“The received access token is not valid”).
Symptoms
- A previously working
azcommand suddenly fails with401andExpiredAuthenticationTokenafter the session has been open for a while. - Long-running scripts, pipelines, or Terraform runs fail partway through with a token-expiry error even though they started fine.
- SDK apps throw
ClientAuthenticationError/AuthenticationFailedExceptionreferencing an expired or wrong-tenant token. - Switching between subscriptions in different tenants triggers
InvalidAuthenticationTokenTenant. - The error appears only intermittently and disappears after re-running the command (silent token refresh kicked in).
Common Root Causes
- Token past its lifetime. Azure AD access tokens are valid for roughly one hour. Any cached token used beyond that expires, and a call made exactly at the boundary can fail.
- Long-running operation outlives the token. A deployment, loop, or migration that runs longer than the token lifetime without refreshing hits expiry mid-run.
- Clock skew on the client. If the client’s clock is ahead of real time, a still-valid token looks expired; if behind, ARM sees the token as not-yet/already invalid. The message compares two UTC times — a large gap points at the clock.
- Wrong tenant context. The token was acquired for tenant A but the target subscription lives in tenant B, producing
InvalidAuthenticationTokenTenant. - Stale credential cache in CI. A cached token or
~/.azureprofile carried between pipeline steps or containers is already expired when reused. - Service principal secret/cert expired. The underlying credential (not just the token) has lapsed, so no fresh token can be minted.
Diagnostic Workflow
All commands below are read-only. Start by confirming who you are and which tenant you are pointed at:
# Who am I, which tenant and subscription is active?
az account show --output json
# List all subscriptions and their tenantId to spot a cross-tenant mismatch.
az account list --query "[].{name:name, subId:id, tenantId:tenantId, state:state}" --output table
Force a fresh token and inspect its expiry to confirm the lifetime issue:
# Acquire a fresh ARM token and read its expiresOn timestamp.
az account get-access-token --query "{expiresOn:expiresOn, tenant:tenant, subscription:subscription}" --output table
Check the client clock against real UTC, since skew is a frequent hidden cause:
# Compare local UTC to the message's timestamps.
date -u
timedatectl status | grep -i 'synchronized\|NTP'
For a service principal, confirm the credential itself has not expired:
# Check secret/cert expiry on the app registration used by the pipeline.
az ad app credential list --id <appId> --query "[].{keyId:keyId, endDate:endDateTime}" --output table
Example Root Cause Analysis
A nightly Terraform apply began failing about 70 minutes into its run with ExpiredAuthenticationToken. Short plans succeeded; the long apply did not.
az account get-access-token showed an expiresOn roughly one hour out. date -u on the runner matched real time, ruling out clock skew, and the service principal secret’s endDateTime was months away, ruling out a lapsed credential. That left the run duration itself: the apply provisioned dozens of resources and ran past the ~60-minute token lifetime, and the provider had cached the original token instead of refreshing.
The fix was twofold: upgrade the azurerm provider to a version that refreshes tokens automatically, and confirm the CLI/SDK credential chain re-acquires tokens on demand rather than caching a single one. Re-running the apply completed cleanly. Root cause: a long-running operation outliving a one-hour token, not a permissions or tenant problem.
Prevention Best Practices
- Let the SDK/CLI manage tokens. Use
DefaultAzureCredential/az loginsessions and current provider versions that auto-refresh, rather than capturing a token once and reusing it. - Keep client clocks in sync. Run NTP/
timesyncdon build agents and VMs so token time comparisons are accurate; clock drift is a top cause of spurious expiry. - Pin the correct tenant. Use
az login --tenant <id>or set the subscription explicitly so cross-tenant scripts acquire tokens for the right issuer. - Rotate service principal credentials before they expire. Track secret/cert
endDateTimeand rotate ahead of time; federated (OIDC) credentials avoid stored-secret expiry entirely. - Refresh mid-run in long jobs. For operations that can exceed an hour, ensure the credential is re-used (which triggers refresh) rather than a raw token string.
- Do not cache tokens across CI steps. Re-authenticate per job or use workload identity federation so each step gets a fresh token.
Quick Command Reference
# Re-authenticate interactively (clears expired session state).
az login --tenant <tenantId>
# Confirm active account, tenant, and subscription.
az account show --output json
# Acquire a fresh token and read its expiry.
az account get-access-token --query expiresOn --output tsv
# Set the correct subscription/tenant context.
az account set --subscription <subscriptionId>
# Verify service principal credential expiry.
az ad app credential list --id <appId> --output table
# Check client clock sync (skew causes false expiry).
timedatectl status
Conclusion
ExpiredAuthenticationToken is an authentication-freshness problem, not an authorization one: the token you presented is past its lifetime, from the wrong tenant, or malformed. Confirm the active tenant, mint a fresh token, and rule out clock skew and lapsed service principal credentials. For anything that can run longer than an hour, rely on the SDK/CLI credential chain to refresh automatically — and prefer workload identity federation over stored secrets to remove credential expiry as a class of failure.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.