OAuth 2.0 Token does not expire as expected

Eckhart, Johannes 0 Zuverlässigkeitspunkte
2026-05-05T11:25:42.6+00:00

Dear Microsoft Support Team,

I need your help with an issue regarding OAuth 2.0 token expiration in our Azure-based setup.

Our system architecture (simplified):

  • Service B needs to fetch an access token to communicate with external services inside our Microsoft environment.

Tenant ID → belongs to Service A, against which the token is requested.

  • Client ID & Client Secret → come from a separate App Registration, from Service B

Goal: Register external services and enable communication using token-based authentication.

What I did (following the documentation):

  1. Requested credentials from Servie A and B:
  • AUTH_MICROSOFT_CLIENT_ID
    • AUTH_MICROSOFT_CLIENT_SECRET
    • AUTH_MICROSOFT_TENANT_ID
    • baseUrl (our API endpoint)
  1. Acquired token via OAuth 2.0 client credentials flow:
curl --location 'https://login.microsoftonline.com/{TENANT_ID}/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id={CLIENT_ID}' \
--data-urlencode 'client_secret={CLIENT_SECRET}' \
--data-urlencode 'resource=https://management.azure.com/'
  1. Response example:
{
    "token_type": "Bearer",
    "expires_in": "3599",
    "ext_expires_in": "3599",
    "expires_on": "1776936897",
    "not_before": "1776932997",
    "resource": "https://management.azure.com/",
    "access_token": "eyJ0eXAiOiJKV1QiLCJ..."
}

The problem:
The response clearly states expires_in: 3599 (1 hour).
However, the token I received does not expire as expected.
I tested a token generated one week ago – it is still valid today.

This should not happen with the default OAuth 2.0 client credentials flow.

Questions:

Are there any Azure settings (App Registration, Enterprise Application, Token lifetime policies, or Conditional Access) that could cause a token to ignore expires_in?

Could the resource (https://management.azure.com/) or a specific API policy override token expiration?

Is it possible that token validation is not checking expiration on the API side (our baseUrl service)?

Where else should I look for misconfiguration?

We changed nothing regarding token expiration – we use Azure defaults.

Thank you for your help.

Best regards,
Johannes

Azure API Management
Azure API Management

Ein Azure-Dienst, der eine hybride Multi-Cloud-Verwaltungsplattform für APIs bereitstellt


2 Antworten

Sortieren nach: Älteste
  1. Julian Arndt 100 Zuverlässigkeitspunkte
    2026-05-05T11:39:59.7066667+00:00

    Hi Johannes,

    This is definitely strange behavior for the standard OAuth 2.0 Client Credentials flow. Since Microsoft Entra ID (Azure AD) is quite rigid with its default 1-hour expiration for access tokens, the issue usually isn't that the token isn't expiring—it's usually that the resource (the API) isn't checking the expiration date.

    Here is a breakdown of where things might be going sideways and what you should check:

    1. The "Old Token" Test (The Smoke Test)

    Take that 1-week-old token and paste it into jwt.ms.

    Look for the exp (expiration) claim.

    If the date in the exp claim is in the past (which it should be), then Microsoft Entra ID has done its job correctly.

    The Conclusion: If the token is expired according to the payload but your baseUrl service still accepts it, the issue lies entirely within your API’s token validation logic, not Azure’s settings.

    1. Validation Logic at baseUrl

    Most APIs that receive a Bearer token need to perform several checks. It sounds like your service might be validating the Signature (confirming the token was signed by Microsoft) but skipping the Lifetime Validation.

    Check your code: If you are using middleware (like Microsoft.Identity.Web for .NET or passport-azure-ad for Node.js), ensure that ValidateLifetime is set to true.

    Clock Skew: Most libraries allow for a 5-minute "clock skew" to account for time differences between servers. However, a week is way beyond any drift settings.

    1. The Audience (aud) Claim Mismatch

    You are requesting a token for resource=https://management.azure.com/.

    The Risk: If you are sending this token to your own custom API (Service B / baseUrl), your API should technically reject it because the aud (Audience) claim in the token is for Azure Management, not your specific service.

    If your API accepts a token meant for a different resource and doesn't check the expiration date, it indicates the validation middleware is likely set to "permissive" or is incorrectly configured.

    1. Are you using a Proxy or WAF?

    Sometimes, an Intermediate layer (like an Azure Application Gateway, an F5, or a specialized WAF) might be caching authorization headers or performing its own validation. If the backend never sees the "fresh" request and simply relies on a cached "200 OK" from a previous session, it might appear as if the token is still valid.

    Summary Checklist for your Team:

    Decode the token: Verify exp via jwt.ms.

    Verify API Middleware: Ensure the service at baseUrl is explicitly configured to validate Lifetime and Audience.

    Check for Caching: Ensure there is no local cache (Redis, In-Memory) on the API side that stores the "Validated" status of a token indefinitely.

    If the exp claim in the JWT actually shows a date in the future (one week away), please let us know, as that would imply a Token Lifetime Policy has been applied to your Service Principal via PowerShell/Graph API, though even then, a 1-week duration for an access token is highly non-standard.

    I hope this helps you narrow it down!

    Best regards,

    War diese Antwort hilfreich?

    0 Kommentare Keine Kommentare

  2. VEMULA SRISAI 14,065 Zuverlässigkeitspunkte Externe Microsoft-Mitarbeiter Moderator
    2026-05-05T12:47:52.1466667+00:00

    Hello Eckhart, Johannes,

    Thank you for the detailed explanation. Based on the information shared, this behavior is not expected from Microsoft Entra ID for a client credentials–based access token.

    An access token issued via the OAuth 2.0 client credentials flow always has a fixed lifetime (typically ~1 hour by default). The values returned in the token response (expires_in, expires_on, and the exp claim in the JWT) reflect the actual token validity and are enforced by Microsoft Entra ID. There is no supported configuration that would cause a token with expires_in: 3599 to remain valid for a week.

    Given this, the most likely causes are on the resource/API validation side, not on the token issuance side:

    1. Token expiration is not being validated by the API The API receiving the token (baseUrl) must explicitly validate the token’s exp and nbf claims. If lifetime validation is missing or incomplete, an expired token can still be accepted. This is the most common cause of the behavior you’re seeing.
    2. Audience / resource mismatch The token is requested for resource=https://management.azure.com/, which means the token’s aud is Azure Resource Manager. If the same token is being accepted by a custom API, that indicates the API is not strictly validating the aud claim, which is a security misconfiguration.
    3. Token lifetime policies / Conditional Access Custom token lifetime policies can change access token lifetime, but only up to 1 day maximum, and Conditional Access / CAE does not allow week‑long validity for app‑only tokens. These settings do not explain a token remaining valid for a week. What to check next
    • Decode both the old and new tokens (for example using jwt.ms) and verify:
      • exp is already in the past for the one‑week‑old token
      • aud is https://management.azure.com/
    • Review token validation logic on the API side (or API Management policies, if used) and ensure:
      • Signature, issuer, audience, and expiration (exp) are enforced

    Confirm the token is actually being presented directly to Azure Resource Manager and not to a custom endpoint that bypasses lifetime validation

    Conclusion

    Microsoft Entra ID is issuing the token correctly, and the token does expire as stated. If an old token is still accepted, the issue is almost certainly due to missing or incorrect token validation on the resource/API side, not an Entra ID configuration.

    Please let us know if you want help reviewing your API token validation setup.

    War diese Antwort hilfreich?


Ihre Antwort

Antworten können von Fragestellenden als „Angenommen“ und von Moderierenden als „Empfohlen“ gekennzeichnet werden, wodurch Benutzende wissen, dass diese Antwort das Problem des Fragestellenden gelöst hat.