# AADSTS70002 fix: error validating client credentials

Canonical: https://brambrinkmeier.com/entra/aadsts/70002/
By Bram Brinkmeier. JSON: https://brambrinkmeier.com/api/aadsts/70002.json

**Short answer:** AADSTS70002 means Microsoft Entra ID couldn't validate the credentials an app sent, typically a client secret that doesn't match. A wrong secret today often comes back as 7000215 instead. 70002 is also seen as an outer code in front of a more specific one, so read the whole message. Usually a setup mistake; repeated failures from IPs you don't own deserve a look.

**Fix:** Developer: read the second AADSTS code in the message, then send the secret's Value (not its ID) for the right app and tenant, or create a new secret. Admin: after a rotation, update every system that still fails. If failures come from IPs you don't own, replace the app's credentials.

| Field | Value |
|---|---|
| Code | AADSTS70002 |
| Microsoft name | InvalidClient |
| Category | App configuration |
| Type | Failure |
| Attack signal | Medium: worth a look in context |
| MITRE ATT&CK | [T1078.004](https://attack.mitre.org/techniques/T1078/004/) Valid Accounts: Cloud Accounts |
| In Microsoft's error reference | Yes |
| Verification | documentation |
| Last updated | 2026-11-01 |

## What does AADSTS70002 mean?

Microsoft's description:

> **InvalidClient**: Error validating the credentials. The specified client_secret does not match the expected value for this client. Correct the client_secret and try again. For more info, see [Use the authorization code to request an access token](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow#redeem-a-code-for-an-access-token).

A confidential client, such as a web app, a daemon or a connector, proves who it is with a client secret or a certificate. Microsoft's authorization code flow reference says the `client_secret` is *"the application secret that you created in the app registration portal for your app"* and that it *"must be URL-encoded before being sent"*. The SDK does that for you; a hand-built request may not. When Entra can't validate what was sent, the token request fails.

### 70002 or 7000215?
Both are about a bad client secret, and they are easy to confuse.

- [7000215](https://brambrinkmeier.com/entra/aadsts/7000215/) is *"Invalid client secret is provided"*. Its page records a token request with a wrong secret that came back as 7000215, with Microsoft's current hint to send *"the client secret value, not the client secret ID"*. A Microsoft Q&A thread that people find when searching for 70002, a Postman call with the wrong secret, also shows 7000215.
- 70002 still appears in user reports, often with a second code after it. Quest's knowledge base shows *"AADSTS70002: Error validating credentials. AADSTS50126: Invalid username or password"*. Microsoft's own external MFA provider reference shows *"AADSTS70002: Error validating credentials. AADSTS50012: External ID token from issuer ... failed signature verification"* (Microsoft prints the prefix there as `ENTRA IDSTS`).

Our reading: 70002 works as a general "credential check failed" code, and the code that follows it says what actually failed. If your message has a second AADSTS code, look that one up: [50126](https://brambrinkmeier.com/entra/aadsts/50126/) is a wrong user password, and 50012 is a token whose signature didn't verify. If 70002 stands alone, treat it as a wrong client secret.

Other neighbours:

- [7000222](https://brambrinkmeier.com/entra/aadsts/7000222/): the secret was right but has expired.
- [7000218](https://brambrinkmeier.com/entra/aadsts/7000218/): no secret or assertion was sent at all.
- [700016](https://brambrinkmeier.com/entra/aadsts/700016/): the client ID isn't found in the tenant. Check this first if the secret is definitely right.

## Is it an attack?

Usually not. The causes users report are setup mistakes: the secret ID pasted instead of the value, an old secret left in a config file after a rotation, or the right secret for the wrong app or tenant. UiPath's documentation, for example, tells users to check the tenant ID and application ID and to *"copy the Secret Value, not the Secret ID"*.

| Pattern | Likely meaning |
|---|---|
| Failures for one app from your own servers, starting at a rotation date | A system still has the old secret. Update it. |
| Failures since day one of a new integration | Secret ID instead of value, or a secret for another app registration. |
| 70002 followed by 50126 | A user's password was wrong. See [50126](https://brambrinkmeier.com/entra/aadsts/50126/), not this page. |
| Repeated failures for one app from IPs you don't own | Someone has an old or leaked secret, or is guessing. Treat it like [7000215](https://brambrinkmeier.com/entra/aadsts/7000215/) from a foreign IP (T1078.004 if it ever succeeds). |

## How do I find it in my logs (KQL)?

Microsoft doesn't document which sign-in table 70002 lands in. App-only requests would be expected in `AADServicePrincipalSignInLogs`, and requests on behalf of a user in the user tables; that is an assumption. Check, and compare with 7000215 so you see which code your tenant actually returns. One caveat, as on the [7000215 page](https://brambrinkmeier.com/entra/aadsts/7000215/): the reference for `AADServicePrincipalSignInLogs` describes `ResultType` as *"Success or Failure"*, so confirm failed rows carry the code; if they don't, filter on `ResultSignature` instead:

```kusto
union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs
| where TimeGenerated > ago(30d)
| where ResultType in ("70002", "7000215")
| summarize Rows = count() by SourceTable, ResultType
```

Which apps fail, from where, and with which key. Microsoft documents `ClientCredentialType` as the type of client credential used (for example client assertion or client secret) and `ServicePrincipalCredentialKeyId` as the key ID of the credential:

```kusto
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType in ("70002", "7000215")
| summarize Attempts = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated),
            CredTypes = make_set(ClientCredentialType, 5), KeyIds = make_set(ServicePrincipalCredentialKeyId, 10)
          by AppId, ServicePrincipalName, IPAddress, ResultType
| sort by Attempts desc
```

Failures from IPs that have never signed in successfully for that app in the last 30 days. Worth an alert; the 30-day window and a threshold of one are starting points:

```kusto
let knownAppIPs =
    AADServicePrincipalSignInLogs
    | where TimeGenerated > ago(30d)
    | where ResultType == "0"
    | distinct AppId, IPAddress;
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(1d)
| where ResultType in ("70002", "7000215")
| join kind=leftanti knownAppIPs on AppId, IPAddress
| summarize Attempts = count(), LastSeen = max(TimeGenerated) by AppId, ServicePrincipalName, IPAddress, ResultType
| sort by Attempts desc
```

If the first query shows 70002 only in the user tables, run the third one against those with `UserPrincipalName` added. `ResultType` is a **string** column, so compare it with `"70002"`, not `70002`.

## How do I fix AADSTS70002?

**For developers:**
1. Read the full message. If a second AADSTS code follows, fix that one.
2. Send the secret's **Value**, not its ID, for the same app registration as the `client_id`, against the tenant that app lives in.
3. If you build the request by hand, URL-encode the secret, as Microsoft's reference requires.
4. If you can't find the value any more, create a new client secret and use that.

**For admins:**
1. After a rotation, use the second query to find every system still failing, and update it.
2. If failures come from IPs you don't own, treat the app's secrets as exposed: create a new credential, remove the old ones from the app registration, and update your own systems.
3. Move the app to a certificate where it supports one. Microsoft *"recommends that you use a certificate instead of a client secret before moving the application to a production environment."*

## Evidence

Not reproduced in the lab yet; the page is based on Microsoft's documentation.

Lab-run rows naming this code (2; full list at https://brambrinkmeier.com/lab-runs.json):

| Date | Scenario | Expected | Got | Message (first line) |
|---|---|---|---|---|
| 2026-10-31 | Device code redeemed with a different client ID | 70016 / 7000215 / 70002 | 90023 | AADSTS90023: ClientId passed in the request doesn't match the one in cache |
| 2026-10-31 | Device code redeemed with a different client ID | 70016 / 7000215 / 70002 | 90023 | AADSTS90023: ClientId passed in the request doesn't match the one in cache |

## Sources

1. [Microsoft Entra authentication and authorization error codes](https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes)
2. [Microsoft identity platform and OAuth 2.0 authorization code flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow)
3. [Microsoft Entra multifactor authentication external method provider reference](https://learn.microsoft.com/entra/identity/authentication/concept-authentication-external-method-provider)
4. [Add credentials to your application](https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-credentials)
5. [AADServicePrincipalSignInLogs table reference (Azure Monitor)](https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/aadserviceprincipalsigninlogs)
6. [Microsoft Q&A: Problem using Postman to call REST APIs](https://learn.microsoft.com/en-us/answers/questions/323082/problem-using-postman-to-call-rest-apis-error-desc)
7. [UiPath documentation: AADSTS70002 Error validating credentials](https://docs.uipath.com/activities/other/latest/productivity/aadsts70002-error-validating-credentials.md)
8. [Quest knowledge base: AADSTS70002 Error validating credentials. AADSTS50126](https://support.quest.com/kb/4258624/aadsts70002-error-validating-credentials-aadsts50126-invalid-username-or-password)

## Related codes

[AADSTS7000215](https://brambrinkmeier.com/entra/aadsts/7000215/), [AADSTS7000222](https://brambrinkmeier.com/entra/aadsts/7000222/), [AADSTS7000218](https://brambrinkmeier.com/entra/aadsts/7000218/), [AADSTS50126](https://brambrinkmeier.com/entra/aadsts/50126/), [AADSTS700016](https://brambrinkmeier.com/entra/aadsts/700016/)

## License and citation

CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). 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.

Cite as: Bram Brinkmeier, "AADSTS70002 fix: error validating client credentials", https://brambrinkmeier.com/entra/aadsts/70002/ (CC BY 4.0), last updated 2026-11-01.
