Skip to content
DevOps AI ToolKit
Newsletter
All guides
Azure with AI By James Joyner IV · · 9 min read

Azure Error Guide: 'ExpiredAuthenticationToken' — Refresh the Access Token

Quick answer

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
Free toolkit

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 az command suddenly fails with 401 and ExpiredAuthenticationToken after 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 / AuthenticationFailedException referencing 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 ~/.azure profile 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 login sessions and current provider versions that auto-refresh, rather than capturing a token once and reusing it.
  • Keep client clocks in sync. Run NTP/timesyncd on 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 endDateTime and 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.

Free download · 368-page PDF

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.