Skip to content

AADSTS70002 fix: error validating client credentials

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. Full steps

At a glance
CodeAADSTS70002
Microsoft nameInvalidClient
CategoryApp configuration
TypeFailure
Attack signalMedium: worth a look in context
MITRE ATT&CKT1078.004 Valid Accounts: Cloud Accounts
Microsoft lookuplogin.microsoftonline.com/error?code=70002

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.

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.

Both are about a bad client secret, and they are easy to confuse.

  • 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 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: the secret was right but has expired.
  • 7000218: no secret or assertion was sent at all.
  • 700016: the client ID isn’t found in the tenant. Check this first if the secret is definitely right.

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, 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 from a foreign IP (T1078.004 if it ever succeeds).

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: 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:

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:

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:

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.

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.”

Sources

  1. Microsoft Entra authentication and authorization error codes
  2. Microsoft identity platform and OAuth 2.0 authorization code flow
  3. Microsoft Entra multifactor authentication external method provider reference
  4. Add credentials to your application
  5. AADServicePrincipalSignInLogs table reference (Azure Monitor)
  6. Microsoft Q&A: Problem using Postman to call REST APIs
  7. UiPath documentation: AADSTS70002 Error validating credentials
  8. Quest knowledge base: AADSTS70002 Error validating credentials. AADSTS50126