{
  "schemaVersion": "1.0",
  "code": "50089",
  "name": null,
  "title": "AADSTS50089: Flow token expired, sign in again (fix)",
  "url": "https://brambrinkmeier.com/entra/aadsts/50089/",
  "alternates": {
    "markdown": "https://brambrinkmeier.com/entra/aadsts/50089.md",
    "json": "https://brambrinkmeier.com/api/aadsts/50089.json",
    "html": "https://brambrinkmeier.com/entra/aadsts/50089/"
  },
  "description": "AADSTS50089: a flow token expired during sign-in. Microsoft calls it expected; the app asks the user to sign in again. Low attack value. KQL.",
  "summary": "AADSTS50089 means a sign-in failed because a token that was part of the sign-in had expired. Microsoft calls it expected: auth codes, refresh tokens and sessions expire or get revoked, and the app asks the user to sign in again. It has low attack value. Filter it out of failure dashboards and look only at users who can't get past it.",
  "inMicrosoftReference": true,
  "unlistedChecked": null,
  "meaningText": "Microsoft's description:\n\n> Authentication failed due to flow token expired. Expected - auth codes, refresh tokens, and sessions expire over time or are revoked by the user or an admin. The app will request a new login from the user.\n\nThe reference table gives this code no PascalCase name.\n\nMicrosoft doesn't define \"flow token\" anywhere we could find. Our reading is that it refers to the state Entra keeps for a sign-in in progress, so a sign-in page left open too long before the user finishes would produce 50089. The description itself names three things that expire: auth codes, refresh tokens and sessions. Some documented lifetimes:\n\n- **Authorization codes** are short lived. The authorization code flow reference: *\"Typically, they expire after about 1 minute.\"*\n- **Refresh tokens** have a default Max Inactive Time of 90 days.\n- **Session tokens**: *\"Non-persistent session tokens have a Max Inactive Time of 24 hours; persistent session tokens have a Max Inactive Time of 90 days.\"*\n\nSeveral close relatives cover specific cases of \"your old token or session no longer counts\":\n\n- **[50133](https://brambrinkmeier.com/entra/aadsts/50133/)**: the session is invalid after a password change or expiry.\n- **[50173](https://brambrinkmeier.com/entra/aadsts/50173/)**: the grant was revoked, for example after a password reset.\n- **[70043](https://brambrinkmeier.com/entra/aadsts/70043/)**: a Conditional Access sign-in frequency policy refused the refresh token.\n\nFor a comparison of the codes behind repeated sign-in prompts, see [why a user is asked to sign in again](https://brambrinkmeier.com/guides/why-user-asked-to-sign-in-again-entra-id/).",
  "sections": [
    {
      "heading": "What does AADSTS50089 mean?",
      "text": "Microsoft's description:\n\n> Authentication failed due to flow token expired. Expected - auth codes, refresh tokens, and sessions expire over time or are revoked by the user or an admin. The app will request a new login from the user.\n\nThe reference table gives this code no PascalCase name.\n\nMicrosoft doesn't define \"flow token\" anywhere we could find. Our reading is that it refers to the state Entra keeps for a sign-in in progress, so a sign-in page left open too long before the user finishes would produce 50089. The description itself names three things that expire: auth codes, refresh tokens and sessions. Some documented lifetimes:\n\n- **Authorization codes** are short lived. The authorization code flow reference: *\"Typically, they expire after about 1 minute.\"*\n- **Refresh tokens** have a default Max Inactive Time of 90 days.\n- **Session tokens**: *\"Non-persistent session tokens have a Max Inactive Time of 24 hours; persistent session tokens have a Max Inactive Time of 90 days.\"*\n\nSeveral close relatives cover specific cases of \"your old token or session no longer counts\":\n\n- **[50133](https://brambrinkmeier.com/entra/aadsts/50133/)**: the session is invalid after a password change or expiry.\n- **[50173](https://brambrinkmeier.com/entra/aadsts/50173/)**: the grant was revoked, for example after a password reset.\n- **[70043](https://brambrinkmeier.com/entra/aadsts/70043/)**: a Conditional Access sign-in frequency policy refused the refresh token.\n\nFor a comparison of the codes behind repeated sign-in prompts, see [why a user is asked to sign in again](https://brambrinkmeier.com/guides/why-user-asked-to-sign-in-again-entra-id/)."
    },
    {
      "heading": "Is it an attack?",
      "text": "No. Microsoft's description starts with *\"Expected\"*. The sign-in fails, nothing is issued, and the app starts over.\n\n| Pattern | Likely meaning |\n|---|---|\n| A few 50089 rows per user, each followed by a success | A token or sign-in step expired and the user signed in again. Benign. |\n| 50089 for many users of one app, all at the same time of day | Our interpretation: users who leave the sign-in page or app open overnight and continue the next morning. Benign. |\n| 50089 for one user, repeating several times a day, often with no success after | Something on that device keeps presenting an expired token or session. Clear the app's cached sign-in or browser state. |\n| 50089 together with 50133 or 50173 for the same user | A password change or revocation is working its way through the user's sessions. Benign if you know about the change. |\n\nThere is no documented attack pattern for 50089. An expired token gets the sender nothing. Keep it out of password spray detections and failure-rate thresholds."
    },
    {
      "heading": "How do I find it in my logs (KQL)?",
      "text": "Microsoft doesn't document which sign-in table 50089 is logged in. Start by checking where it lands in your workspace:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"50089\"\n| summarize Rows = count(), Users = dcount(UserPrincipalName) by SourceTable\n```\n\nWhich apps and client types produce it:\n\n```kusto\nunion SigninLogs, AADNonInteractiveUserSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"50089\"\n| summarize Rows = count(), Users = dcount(UserPrincipalName) by AppDisplayName, ClientAppUsed\n| sort by Rows desc\n```\n\nUsers who hit 50089 again and again without a success afterwards on the same app the same day. These are the ones worth a support ticket:\n\n```kusto\nlet expired =\n    union SigninLogs, AADNonInteractiveUserSignInLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"50089\"\n    | summarize FirstFail = min(TimeGenerated), Fails = count() by UserPrincipalName, AppId, AppDisplayName;\nlet successes =\n    union SigninLogs, AADNonInteractiveUserSignInLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"0\"\n    | summarize LastSuccess = max(TimeGenerated) by UserPrincipalName, AppId;\nexpired\n| join kind=leftouter successes on UserPrincipalName, AppId\n| where isnull(LastSuccess) or LastSuccess < FirstFail\n| where Fails >= 5\n| project UserPrincipalName, AppDisplayName, Fails, FirstFail\n| sort by Fails desc\n```\n\nThe threshold of 5 is a starting point; tune it to your tenant. Non-interactive rows are aggregated when application, user, IP address, status and resource match, so `Fails` counts rows there, not requests. `ResultType` is a **string** column, so compare it with `\"50089\"`, not `50089`."
    },
    {
      "heading": "How does Conditional Access affect it?",
      "text": "- 50089 is not a Conditional Access block. No policy causes it directly.\n- Session lifetime is now controlled with Conditional Access. Microsoft: refresh and session token lifetimes *\"are no longer configurable through token lifetime policies\"* and *\"To control how frequently users are required to sign in, use Conditional Access sign-in frequency instead.\"*\n- A sign-in frequency policy that refuses a refresh token shows up as [70043](https://brambrinkmeier.com/entra/aadsts/70043/), not 50089. If users complain about frequent prompts, check for 70043 first.\n- Microsoft warns that combining **Remember MFA on trusted devices** with sign-in frequency *\"might prompt users unexpectedly.\"*"
    },
    {
      "heading": "How do I fix AADSTS50089?",
      "text": "**For the user:** sign in again. If it keeps happening, close the app or browser tab completely and start a fresh sign-in instead of continuing an old one.\n\n**For developers:** treat 50089 as \"start a new sign-in\", not as a fatal error. Microsoft says *\"The app will request a new login from the user.\"* Redeem authorization codes immediately; they typically expire after about a minute. Don't build logic on the code number itself. Microsoft notes that *\"Apps that take a dependency on text or error code numbers will be broken over time.\"* React to the OAuth `error` value instead.\n\n**For admins:**\n1. Nothing is broken. Exclude `\"50089\"` from failure counts and spray detections.\n2. Use the third query for users stuck in a loop. Clear the app's cached sign-in on their device or browser.\n3. If many users get unexpected sign-in prompts, review your Conditional Access session controls (sign-in frequency, persistent browser session) and turn off **Remember MFA on trusted devices** if you use sign-in frequency."
    }
  ],
  "microsoftWording": {
    "text": "Authentication failed due to flow token expired. Expected - auth codes, refresh tokens, and sessions expire over time or are revoked by the user or an admin. The app will request a new login from the user.",
    "source": "Microsoft's error code reference, as quoted on the page",
    "sourceUrl": "https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes"
  },
  "securityReading": {
    "category": {
      "id": "token",
      "label": "Tokens and sessions"
    },
    "kind": {
      "id": "interrupt",
      "label": "Interrupt (an expected extra step, not a failure)"
    },
    "attackSignal": {
      "level": "low",
      "label": "Low: usually configuration or user error"
    },
    "mitre": [],
    "tables": [],
    "isItAnAttack": "No. Microsoft's description starts with *\"Expected\"*. The sign-in fails, nothing is issued, and the app starts over.\n\n| Pattern | Likely meaning |\n|---|---|\n| A few 50089 rows per user, each followed by a success | A token or sign-in step expired and the user signed in again. Benign. |\n| 50089 for many users of one app, all at the same time of day | Our interpretation: users who leave the sign-in page or app open overnight and continue the next morning. Benign. |\n| 50089 for one user, repeating several times a day, often with no success after | Something on that device keeps presenting an expired token or session. Clear the app's cached sign-in or browser state. |\n| 50089 together with 50133 or 50173 for the same user | A password change or revocation is working its way through the user's sessions. Benign if you know about the change. |\n\nThere is no documented attack pattern for 50089. An expired token gets the sender nothing. Keep it out of password spray detections and failure-rate thresholds.",
    "conditionalAccess": {
      "heading": "How does Conditional Access affect it?",
      "text": "- 50089 is not a Conditional Access block. No policy causes it directly.\n- Session lifetime is now controlled with Conditional Access. Microsoft: refresh and session token lifetimes *\"are no longer configurable through token lifetime policies\"* and *\"To control how frequently users are required to sign in, use Conditional Access sign-in frequency instead.\"*\n- A sign-in frequency policy that refuses a refresh token shows up as [70043](https://brambrinkmeier.com/entra/aadsts/70043/), not 50089. If users complain about frequent prompts, check for 70043 first.\n- Microsoft warns that combining **Remember MFA on trusted devices** with sign-in frequency *\"might prompt users unexpectedly.\"*"
    }
  },
  "kql": [
    {
      "title": null,
      "leadIn": "Microsoft doesn't document which sign-in table 50089 is logged in. Start by checking where it lands in your workspace:",
      "query": "union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"50089\"\n| summarize Rows = count(), Users = dcount(UserPrincipalName) by SourceTable"
    },
    {
      "title": null,
      "leadIn": "Which apps and client types produce it:",
      "query": "union SigninLogs, AADNonInteractiveUserSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"50089\"\n| summarize Rows = count(), Users = dcount(UserPrincipalName) by AppDisplayName, ClientAppUsed\n| sort by Rows desc"
    },
    {
      "title": null,
      "leadIn": "Users who hit 50089 again and again without a success afterwards on the same app the same day. These are the ones worth a support ticket:",
      "query": "let expired =\n    union SigninLogs, AADNonInteractiveUserSignInLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"50089\"\n    | summarize FirstFail = min(TimeGenerated), Fails = count() by UserPrincipalName, AppId, AppDisplayName;\nlet successes =\n    union SigninLogs, AADNonInteractiveUserSignInLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"0\"\n    | summarize LastSuccess = max(TimeGenerated) by UserPrincipalName, AppId;\nexpired\n| join kind=leftouter successes on UserPrincipalName, AppId\n| where isnull(LastSuccess) or LastSuccess < FirstFail\n| where Fails >= 5\n| project UserPrincipalName, AppDisplayName, Fails, FirstFail\n| sort by Fails desc"
    }
  ],
  "kqlSectionText": "Microsoft doesn't document which sign-in table 50089 is logged in. Start by checking where it lands in your workspace:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"50089\"\n| summarize Rows = count(), Users = dcount(UserPrincipalName) by SourceTable\n```\n\nWhich apps and client types produce it:\n\n```kusto\nunion SigninLogs, AADNonInteractiveUserSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"50089\"\n| summarize Rows = count(), Users = dcount(UserPrincipalName) by AppDisplayName, ClientAppUsed\n| sort by Rows desc\n```\n\nUsers who hit 50089 again and again without a success afterwards on the same app the same day. These are the ones worth a support ticket:\n\n```kusto\nlet expired =\n    union SigninLogs, AADNonInteractiveUserSignInLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"50089\"\n    | summarize FirstFail = min(TimeGenerated), Fails = count() by UserPrincipalName, AppId, AppDisplayName;\nlet successes =\n    union SigninLogs, AADNonInteractiveUserSignInLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"0\"\n    | summarize LastSuccess = max(TimeGenerated) by UserPrincipalName, AppId;\nexpired\n| join kind=leftouter successes on UserPrincipalName, AppId\n| where isnull(LastSuccess) or LastSuccess < FirstFail\n| where Fails >= 5\n| project UserPrincipalName, AppDisplayName, Fails, FirstFail\n| sort by Fails desc\n```\n\nThe threshold of 5 is a starting point; tune it to your tenant. Non-interactive rows are aggregated when application, user, IP address, status and resource match, so `Fails` counts rows there, not requests. `ResultType` is a **string** column, so compare it with `\"50089\"`, not `50089`.",
  "fix": {
    "short": "Nothing to fix: the token expired as designed. User: sign in again. If it keeps happening, close the app or tab and start a fresh sign-in. Developer: treat 50089 as 'start a new sign-in' and redeem auth codes immediately. Admin: exclude 50089 from failure counts and spray rules.",
    "steps": "**For the user:** sign in again. If it keeps happening, close the app or browser tab completely and start a fresh sign-in instead of continuing an old one.\n\n**For developers:** treat 50089 as \"start a new sign-in\", not as a fatal error. Microsoft says *\"The app will request a new login from the user.\"* Redeem authorization codes immediately; they typically expire after about a minute. Don't build logic on the code number itself. Microsoft notes that *\"Apps that take a dependency on text or error code numbers will be broken over time.\"* React to the OAuth `error` value instead.\n\n**For admins:**\n1. Nothing is broken. Exclude `\"50089\"` from failure counts and spray detections.\n2. Use the third query for users stuck in a loop. Clear the app's cached sign-in on their device or browser.\n3. If many users get unexpected sign-in prompts, review your Conditional Access session controls (sign-in frequency, persistent browser session) and turn off **Remember MFA on trusted devices** if you use sign-in frequency."
  },
  "labReproduced": null,
  "labSectionText": null,
  "labRuns": [],
  "verification": "documentation",
  "related": [
    "50133",
    "50173",
    "70043",
    "700082",
    "50058"
  ],
  "coveredBy": [],
  "sources": [
    {
      "title": "Microsoft Entra authentication and authorization error codes",
      "url": "https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes"
    },
    {
      "title": "Configurable token lifetimes in the Microsoft identity platform",
      "url": "https://learn.microsoft.com/en-us/entra/identity-platform/configurable-token-lifetimes"
    },
    {
      "title": "Configure adaptive session lifetime policies",
      "url": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/howto-conditional-access-session-lifetime"
    },
    {
      "title": "Microsoft identity platform and OAuth 2.0 authorization code flow",
      "url": "https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow"
    },
    {
      "title": "Non-interactive user sign-ins",
      "url": "https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-noninteractive-sign-ins"
    }
  ],
  "published": "2026-10-30",
  "lastUpdated": "2026-10-30",
  "lastVerified": null,
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "licenseNote": "CC BY 4.0 covers the author's own text and classification fields. Quoted Microsoft error wording belongs to Microsoft, is quoted with its source and can change.",
  "attribution": "Bram Brinkmeier, \"AADSTS50089: Flow token expired, sign in again (fix)\", https://brambrinkmeier.com/entra/aadsts/50089/ (CC BY 4.0)",
  "microsoftErrorLookup": "https://login.microsoftonline.com/error?code=50089"
}
