{
  "schemaVersion": "1.0",
  "code": "50126",
  "name": "InvalidUserNameOrPassword",
  "title": "AADSTS50126: Invalid username or password",
  "url": "https://brambrinkmeier.com/entra/aadsts/50126/",
  "alternates": {
    "markdown": "https://brambrinkmeier.com/entra/aadsts/50126.md",
    "json": "https://brambrinkmeier.com/api/aadsts/50126.json",
    "html": "https://brambrinkmeier.com/entra/aadsts/50126/"
  },
  "description": "AADSTS50126: Entra ID rejected the username or password. Many accounts from one IP is password spraying, one account is brute force. With KQL.",
  "summary": "AADSTS50126 means Microsoft Entra ID rejected the username or password. A few per day are normal typos. The security signal is the shape: many accounts failing from one IP is password spraying, many failures on one account is brute force. Hunt it in SigninLogs with ResultType == \"50126\".",
  "inMicrosoftReference": true,
  "unlistedChecked": null,
  "meaningText": "Microsoft's description:\n\n> **InvalidUserNameOrPassword**: Error validating credentials due to invalid username or password. The user didn't enter the right credentials. Expect to see some number of these errors in your logs due to users making mistakes.\n\nSource: [Microsoft Entra authentication and authorization error codes](https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes).\n\nThe request reached the password check and failed. Nothing after that ran: no MFA prompt and no Conditional Access evaluation.",
  "sections": [
    {
      "heading": "What does AADSTS50126 mean?",
      "text": "Microsoft's description:\n\n> **InvalidUserNameOrPassword**: Error validating credentials due to invalid username or password. The user didn't enter the right credentials. Expect to see some number of these errors in your logs due to users making mistakes.\n\nSource: [Microsoft Entra authentication and authorization error codes](https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes).\n\nThe request reached the password check and failed. Nothing after that ran: no MFA prompt and no Conditional Access evaluation."
    },
    {
      "heading": "Reproduced in the lab",
      "text": "On 25 October 2026 an automated lab run sent a token request for an existing test user with the wrong password. Entra returned `AADSTS50126` with this first line:\n\n> AADSTS50126: Error validating credentials due to invalid username or password.\n\nThat confirms the code and its wording at the token endpoint.\n\nThe same attempt in the lab tenant's sign-in log (Entra admin center, interactive user sign-ins), with identifiers left out:\n\n| Field | Value in the lab row |\n|---|---|\n| Status | Failure |\n| Sign-in error code | 50126 |\n| Failure reason | Error validating credentials due to invalid username or password. |\n| Additional details | The user didn't enter the right credentials. It's expected to see some number of these errors in your logs due to users making mistakes. |\n| Authentication details | Password (password in the cloud), succeeded: false |\n| Authentication requirement | Single-factor authentication |\n| Conditional Access | Not Applied |\n| Resource | Microsoft Graph |\n| User type | Member |\n\nIn the real row Conditional Access shows **Not Applied**: a wrong password stops the sign-in before any policy is evaluated, which is why a spray that never guesses right leaves no Conditional Access trace. The KQL below was run on the lab rows ([how](https://brambrinkmeier.com/methodology/)). Both queries returned nothing as written, as expected: the lab sends one wrong password per run, far below the spray threshold of 10 users. With the threshold lowered to 1, the spray query found both source IPs and the follow-up query found the next sign-ins from those IPs."
    },
    {
      "heading": "Is it an attack?",
      "text": "[Microsoft itself says](https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes) a baseline of 50126 is expected. **The signal is the distribution, not the presence.** The [password spray guide](https://brambrinkmeier.com/guides/detect-password-spray-entra-id/) turns the patterns below into a step-by-step investigation.\n\n| Pattern | Likely meaning |\n|---|---|\n| 1–3 failures, then a success from the same IP and device | A user typo. Benign. |\n| Many **different users**, few attempts each, from one IP or a small IP range | **Password spraying** (T1110.003) |\n| Many attempts against **one user** | Brute force / password guessing (T1110.001) |\n| Failures via `ClientAppUsed` = IMAP, POP, SMTP, Exchange ActiveSync, other legacy clients | Spray tooling using legacy protocols, which can't do MFA |\n| 50126 followed by **50053** for the same accounts | The spray tripped smart lockout |\n| 50126 followed by **50076/50074** or **53003** from the same IP | **The password was correct** on a later try. MFA or Conditional Access is now the only barrier. Treat it as a compromised password. |\n\nTwo details from Microsoft's documentation matter when you read these rows:\n\n- **Conditional Access never ran.** CA [only evaluates after the first factor succeeds](https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview), so 50126 rows show `ConditionalAccessStatus` = `notApplied`. That doesn't mean your policies are missing.\n- **`AuthenticationRequirement` shows `singleFactorAuthentication` even in an MFA-enforced tenant**, because MFA was never reached ([sign-in log activity details](https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-in-log-activity-details)). Don't use it as evidence that MFA is off."
    },
    {
      "heading": "How do I find it in my logs (KQL)?",
      "text": "Spray pattern: one IP, many distinct users failing, over the last day.\n\n```kusto\nSigninLogs\n| where TimeGenerated > ago(1d)\n| where ResultType == \"50126\"\n| summarize FailedUsers = dcount(UserPrincipalName),\n            Attempts = count(),\n            Apps = make_set(AppDisplayName, 5),\n            Clients = make_set(ClientAppUsed, 5)\n          by IPAddress\n| where FailedUsers >= 10\n| sort by FailedUsers desc\n```\n\nDid any of those IPs **also get a correct password**? That is the row that matters:\n\n```kusto\nlet sprayIPs =\n    SigninLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"50126\"\n    | summarize FailedUsers = dcount(UserPrincipalName) by IPAddress\n    | where FailedUsers >= 10\n    | project IPAddress;\nSigninLogs\n| where TimeGenerated > ago(1d)\n| where IPAddress in (sprayIPs)\n| where ResultType in (\"0\", \"50076\", \"50074\", \"50079\", \"53003\", \"50055\")\n| project TimeGenerated, UserPrincipalName, IPAddress, ResultType, ResultDescription,\n          ConditionalAccessStatus, AppDisplayName, ClientAppUsed\n| sort by TimeGenerated asc\n```\n\n`ResultType` is a **string** column, so compare it with `\"50126\"`, not `50126`. Legacy-protocol sign-ins that replay a stored password can also show up in `AADNonInteractiveUserSignInLogs`; run the same query there if you allow legacy auth."
    },
    {
      "heading": "How do Conditional Access and MFA affect it?",
      "text": "- CA can't stop a 50126. It happens before CA runs. What CA controls is what happens **after** a correct password (MFA, compliant device, block).\n- **Blocking legacy authentication** removes the protocols most spray tools use, and those protocols can't do MFA.\n- **[Smart lockout](https://learn.microsoft.com/en-us/entra/identity/authentication/howto-password-smart-lockout)** is always on. By default it locks after 10 failed attempts (Azure public cloud), with separate counters for familiar and unfamiliar locations, which then turns the errors into 50053."
    },
    {
      "heading": "How do I fix AADSTS50126?",
      "text": "**For the user:** check the account name and keyboard layout, then reset the password with self-service password reset if needed.\n\n**For admins:**\n1. If it's a spray, check whether any targeted account **succeeded** or reached MFA (second query above). Reset those passwords and revoke sessions.\n2. Block legacy authentication with Conditional Access.\n3. Make sure every account has a registered phishing-resistant MFA method, so a correct password alone isn't enough.\n4. Consider custom banned passwords. Spray lists use seasonal and company-name passwords.\n5. If the failures come from the IP address of one of your own servers, check that server for compromise. A host that was breached may be trying credentials it found on it. See [hunting credential reuse after a web-server breach](https://brambrinkmeier.com/guides/hunt-credential-reuse-after-web-server-breach/)."
    }
  ],
  "microsoftWording": {
    "text": "**InvalidUserNameOrPassword**: Error validating credentials due to invalid username or password. The user didn't enter the right credentials. Expect to see some number of these errors in your logs due to users making mistakes.",
    "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": "credentials",
      "label": "Credentials and lockout"
    },
    "kind": {
      "id": "failure",
      "label": "Failure"
    },
    "attackSignal": {
      "level": "high",
      "label": "High: often seen in attacks"
    },
    "mitre": [
      {
        "id": "T1110.003",
        "name": "Brute Force: Password Spraying",
        "url": "https://attack.mitre.org/techniques/T1110/003/"
      },
      {
        "id": "T1110.001",
        "name": "Brute Force: Password Guessing",
        "url": "https://attack.mitre.org/techniques/T1110/001/"
      }
    ],
    "tables": [
      "SigninLogs",
      "AADNonInteractiveUserSignInLogs"
    ],
    "isItAnAttack": "[Microsoft itself says](https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes) a baseline of 50126 is expected. **The signal is the distribution, not the presence.** The [password spray guide](https://brambrinkmeier.com/guides/detect-password-spray-entra-id/) turns the patterns below into a step-by-step investigation.\n\n| Pattern | Likely meaning |\n|---|---|\n| 1–3 failures, then a success from the same IP and device | A user typo. Benign. |\n| Many **different users**, few attempts each, from one IP or a small IP range | **Password spraying** (T1110.003) |\n| Many attempts against **one user** | Brute force / password guessing (T1110.001) |\n| Failures via `ClientAppUsed` = IMAP, POP, SMTP, Exchange ActiveSync, other legacy clients | Spray tooling using legacy protocols, which can't do MFA |\n| 50126 followed by **50053** for the same accounts | The spray tripped smart lockout |\n| 50126 followed by **50076/50074** or **53003** from the same IP | **The password was correct** on a later try. MFA or Conditional Access is now the only barrier. Treat it as a compromised password. |\n\nTwo details from Microsoft's documentation matter when you read these rows:\n\n- **Conditional Access never ran.** CA [only evaluates after the first factor succeeds](https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview), so 50126 rows show `ConditionalAccessStatus` = `notApplied`. That doesn't mean your policies are missing.\n- **`AuthenticationRequirement` shows `singleFactorAuthentication` even in an MFA-enforced tenant**, because MFA was never reached ([sign-in log activity details](https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-in-log-activity-details)). Don't use it as evidence that MFA is off.",
    "conditionalAccess": {
      "heading": "How do Conditional Access and MFA affect it?",
      "text": "- CA can't stop a 50126. It happens before CA runs. What CA controls is what happens **after** a correct password (MFA, compliant device, block).\n- **Blocking legacy authentication** removes the protocols most spray tools use, and those protocols can't do MFA.\n- **[Smart lockout](https://learn.microsoft.com/en-us/entra/identity/authentication/howto-password-smart-lockout)** is always on. By default it locks after 10 failed attempts (Azure public cloud), with separate counters for familiar and unfamiliar locations, which then turns the errors into 50053."
    }
  },
  "kql": [
    {
      "title": null,
      "leadIn": "Spray pattern: one IP, many distinct users failing, over the last day.",
      "query": "SigninLogs\n| where TimeGenerated > ago(1d)\n| where ResultType == \"50126\"\n| summarize FailedUsers = dcount(UserPrincipalName),\n            Attempts = count(),\n            Apps = make_set(AppDisplayName, 5),\n            Clients = make_set(ClientAppUsed, 5)\n          by IPAddress\n| where FailedUsers >= 10\n| sort by FailedUsers desc"
    },
    {
      "title": null,
      "leadIn": "Did any of those IPs **also get a correct password**? That is the row that matters:",
      "query": "let sprayIPs =\n    SigninLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"50126\"\n    | summarize FailedUsers = dcount(UserPrincipalName) by IPAddress\n    | where FailedUsers >= 10\n    | project IPAddress;\nSigninLogs\n| where TimeGenerated > ago(1d)\n| where IPAddress in (sprayIPs)\n| where ResultType in (\"0\", \"50076\", \"50074\", \"50079\", \"53003\", \"50055\")\n| project TimeGenerated, UserPrincipalName, IPAddress, ResultType, ResultDescription,\n          ConditionalAccessStatus, AppDisplayName, ClientAppUsed\n| sort by TimeGenerated asc"
    }
  ],
  "kqlSectionText": "Spray pattern: one IP, many distinct users failing, over the last day.\n\n```kusto\nSigninLogs\n| where TimeGenerated > ago(1d)\n| where ResultType == \"50126\"\n| summarize FailedUsers = dcount(UserPrincipalName),\n            Attempts = count(),\n            Apps = make_set(AppDisplayName, 5),\n            Clients = make_set(ClientAppUsed, 5)\n          by IPAddress\n| where FailedUsers >= 10\n| sort by FailedUsers desc\n```\n\nDid any of those IPs **also get a correct password**? That is the row that matters:\n\n```kusto\nlet sprayIPs =\n    SigninLogs\n    | where TimeGenerated > ago(1d)\n    | where ResultType == \"50126\"\n    | summarize FailedUsers = dcount(UserPrincipalName) by IPAddress\n    | where FailedUsers >= 10\n    | project IPAddress;\nSigninLogs\n| where TimeGenerated > ago(1d)\n| where IPAddress in (sprayIPs)\n| where ResultType in (\"0\", \"50076\", \"50074\", \"50079\", \"53003\", \"50055\")\n| project TimeGenerated, UserPrincipalName, IPAddress, ResultType, ResultDescription,\n          ConditionalAccessStatus, AppDisplayName, ClientAppUsed\n| sort by TimeGenerated asc\n```\n\n`ResultType` is a **string** column, so compare it with `\"50126\"`, not `50126`. Legacy-protocol sign-ins that replay a stored password can also show up in `AADNonInteractiveUserSignInLogs`; run the same query there if you allow legacy auth.",
  "fix": {
    "short": "User: check the account name and keyboard layout, then reset your password with self-service reset if needed. Admin: if it's a spray, check whether any targeted account succeeded or reached MFA, then reset those passwords and revoke sessions. Block legacy authentication.",
    "steps": "**For the user:** check the account name and keyboard layout, then reset the password with self-service password reset if needed.\n\n**For admins:**\n1. If it's a spray, check whether any targeted account **succeeded** or reached MFA (second query above). Reset those passwords and revoke sessions.\n2. Block legacy authentication with Conditional Access.\n3. Make sure every account has a registered phishing-resistant MFA method, so a correct password alone isn't enough.\n4. Consider custom banned passwords. Spray lists use seasonal and company-name passwords.\n5. If the failures come from the IP address of one of your own servers, check that server for compromise. A host that was breached may be trying credentials it found on it. See [hunting credential reuse after a web-server breach](https://brambrinkmeier.com/guides/hunt-credential-reuse-after-web-server-breach/)."
  },
  "labReproduced": {
    "date": "2026-10-25",
    "method": "ROPC token request against a lab tenant",
    "scenario": "Wrong password",
    "message": "AADSTS50126: Error validating credentials due to invalid username or password.",
    "logCaptured": "2026-10-30",
    "kqlTested": "2026-10-30"
  },
  "labSectionText": "On 25 October 2026 an automated lab run sent a token request for an existing test user with the wrong password. Entra returned `AADSTS50126` with this first line:\n\n> AADSTS50126: Error validating credentials due to invalid username or password.\n\nThat confirms the code and its wording at the token endpoint.\n\nThe same attempt in the lab tenant's sign-in log (Entra admin center, interactive user sign-ins), with identifiers left out:\n\n| Field | Value in the lab row |\n|---|---|\n| Status | Failure |\n| Sign-in error code | 50126 |\n| Failure reason | Error validating credentials due to invalid username or password. |\n| Additional details | The user didn't enter the right credentials. It's expected to see some number of these errors in your logs due to users making mistakes. |\n| Authentication details | Password (password in the cloud), succeeded: false |\n| Authentication requirement | Single-factor authentication |\n| Conditional Access | Not Applied |\n| Resource | Microsoft Graph |\n| User type | Member |\n\nIn the real row Conditional Access shows **Not Applied**: a wrong password stops the sign-in before any policy is evaluated, which is why a spray that never guesses right leaves no Conditional Access trace. The KQL below was run on the lab rows ([how](https://brambrinkmeier.com/methodology/)). Both queries returned nothing as written, as expected: the lab sends one wrong password per run, far below the spray threshold of 10 users. With the threshold lowered to 1, the spray query found both source IPs and the follow-up query found the next sign-ins from those IPs.",
  "labRuns": [
    {
      "date": "2026-10-25",
      "run": null,
      "method": "ROPC token request against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/38",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-30",
      "run": "round 4",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/67",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-30",
      "run": "round 4",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/67",
      "scenario": "Old password after an admin reset",
      "expected": "50054 / 50126",
      "got": "error",
      "matchedExpectation": false,
      "message": "Graph PATCH /users/{object-id}: HTTP 403 {\"error\":{\"code\":\"Authorization_RequestDenied\",\"message\":\"Insufficient privileges to complete "
    },
    {
      "date": "2026-10-30",
      "run": "round 5",
      "method": "token-endpoint requests against a lab tenant, using lab-created test apps",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/69",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-30",
      "run": "round 5",
      "method": "token-endpoint requests against a lab tenant, using lab-created test apps",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/69",
      "scenario": "Repeated wrong passwords (smart lockout)",
      "expected": "50053",
      "got": "50126",
      "matchedExpectation": false,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-30",
      "run": "round 5 (first attempt)",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/68",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-30",
      "run": "round 6",
      "method": "token-endpoint requests against a lab tenant, using lab-created test apps",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/70",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-30",
      "run": "round 7",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/71",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-30",
      "run": null,
      "method": "ROPC and other token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/66",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-31",
      "run": "round 8",
      "method": "token-endpoint and device-code requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/72",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    },
    {
      "date": "2026-10-31",
      "run": "round 9",
      "method": "token-endpoint and device-code requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/73",
      "scenario": "Wrong password",
      "expected": "50126",
      "got": "50126",
      "matchedExpectation": true,
      "message": "AADSTS50126: Error validating credentials due to invalid username or password."
    }
  ],
  "verification": "lab-reproduced",
  "related": [
    "50053",
    "50057",
    "50055",
    "50034",
    "53003",
    "50076",
    "50052",
    "50054",
    "50074",
    "50079",
    "50125",
    "50133",
    "50140",
    "50158",
    "53000",
    "70002",
    "500121",
    "530032",
    "900144"
  ],
  "coveredBy": [
    {
      "title": "How to detect MFA fatigue (push bombing) in Microsoft Entra ID",
      "url": "https://brambrinkmeier.com/guides/detect-mfa-fatigue-entra-id/"
    },
    {
      "title": "How to detect password spraying in Microsoft Entra ID (with KQL)",
      "url": "https://brambrinkmeier.com/guides/detect-password-spray-entra-id/"
    },
    {
      "title": "SMTP AUTH basic authentication is being disabled in Exchange Online (end of December 2026): find and fix the apps and scanners that still use it",
      "url": "https://brambrinkmeier.com/guides/smtp-auth-basic-auth-retirement-exchange-online/"
    }
  ],
  "sources": [
    {
      "title": "Microsoft Entra authentication and authorization error codes",
      "url": "https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes"
    },
    {
      "title": "Password spray investigation (Microsoft incident response playbook)",
      "url": "https://learn.microsoft.com/en-us/security/operations/incident-response-playbook-password-spray"
    },
    {
      "title": "Protect user accounts from attacks with Microsoft Entra smart lockout",
      "url": "https://learn.microsoft.com/en-us/entra/identity/authentication/howto-password-smart-lockout"
    },
    {
      "title": "Sign-in log activity details",
      "url": "https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-in-log-activity-details"
    },
    {
      "title": "SigninLogs table reference (Azure Monitor)",
      "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/signinlogs"
    },
    {
      "title": "What is Conditional Access? (Microsoft Entra)",
      "url": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview"
    }
  ],
  "published": null,
  "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, \"AADSTS50126: Invalid username or password\", https://brambrinkmeier.com/entra/aadsts/50126/ (CC BY 4.0)",
  "microsoftErrorLookup": "https://login.microsoftonline.com/error?code=50126"
}
