An invalid_client or unauthorized_client error means the authorisation server rejected the application itself, not the user or the token, and the usual reflex of generating a new secret often adds a credential without fixing the cause. The two errors are different: invalid_client says the client could not be authenticated, while unauthorized_client says the client was recognised but is not allowed to use the grant type it asked for. This guide works from each error to its likely causes by client authentication method (shared secret, signed JWT assertion, mutual TLS) and by grant type, shows where to look, and gives the fix and how to prevent recurrence. It is for developers, identity engineers and platform teams running confidential OAuth clients, service integrations and workloads. Provider behaviour was checked against IETF, Microsoft, Google, Okta and Auth0 documentation on 11 November 2026. Errors about the grant itself are covered in the OAuth invalid_grant Error Troubleshooting Guide.
Key takeaways
- Separate the two errors before anything else.
invalid_clientis an authentication failure (wrong, expired or badly sent credential, unknown client).unauthorized_clientis a permission failure (grant type not enabled, delegation not authorised). Rotating a secret never fixes the second. - Most invalid_client errors are transport mistakes. A secret not URL-encoded before Basic encoding, credentials sent in both header and body, the wrong method for the client's registration, or a request sent to the wrong tenant or endpoint.
- Assertion failures are claim failures. For
private_key_jwt, checkaud,iss,sub,exp,jtiand the key identifier against what the server expects, character for character. - Mutual TLS fails before OAuth sees the request. Check whether the certificate reached the authorisation server at all, through proxies and on the right endpoint alias.
- Do not fix the error by widening the client. Enabling every grant type, turning a public client into a confidential one, or adding a second long-lived secret creates exposure that outlives the incident.
What the two errors mean
| Error | RFC 6749 s5.2 meaning | HTTP status | Typical question to ask |
|---|---|---|---|
invalid_client | Client authentication failed (unknown client, no client authentication included, or unsupported authentication method) | 400 or 401; must be 401 with a WWW-Authenticate header if the client authenticated with the Authorization header | Did the server receive a credential it could verify for this client ID? |
unauthorized_client | The authenticated client is not authorised to use this authorisation grant type | 400 | Is this client allowed this grant, for this user or tenant? |
unsupported_grant_type | The server does not support the grant type at all | 400 | Is the grant_type value spelt and supported? |
invalid_grant | The grant or refresh token itself is invalid, expired or revoked | 400 | See the invalid_grant guide |
Providers add detail. Microsoft Entra places an AADSTS code in error_description; Google returns texts such as "Unauthorized client or scope in request."; Okta and Auth0 return descriptions and record the failure in their logs. Always log the full error body and the provider's request or trace ID, never the secret or assertion.
Triage in five checks
- Endpoint and tenant: is the request going to the right token endpoint for the right tenant, environment and authorisation server? Okta custom authorisation servers and the org server have different endpoints; Entra tenants are part of the URL. Entra AADSTS700016 says the application was not found in the directory, often because the request went to the wrong tenant.
- Client ID: exact match, no whitespace, correct environment. A staging client ID with a production secret fails as
invalid_client. - Registered authentication method: what does the registration say (
client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt,tls_client_auth,self_signed_tls_client_auth, ornonefor public clients)? Compare with what the client library actually sends. The server's metadata lists what it supports intoken_endpoint_auth_methods_supported. - Exactly one method per request: RFC 6749 says a client must not use more than one authentication method in a request. A library that sends Basic credentials and also
client_secretin the body can be rejected. - Grant type: is
grant_typeenabled for this client? If authentication passes and this check fails, the result isunauthorized_client.
invalid_client with shared secrets
| Cause | How to confirm | Fix | Prevent |
|---|---|---|---|
| Secret not encoded correctly for Basic | Fails only for secrets containing characters such as +, /, = or %; works with client_secret_post | RFC 6749 s2.3.1: form-urlencode the client ID and secret, then Base64-encode id:secret | Use a maintained OAuth library rather than hand-built headers |
| Wrong method for the registration | Registration says client_secret_basic, client posts the secret in the body, or the reverse | Align client and registration | Declare the method explicitly in client configuration |
| Secret expired | Entra AADSTS7000222 "The provided client secret keys are expired" | Issue a new secret through the normal rotation process | Alert on expiry ahead of time; prefer certificate or federated credentials |
| Secret wrong or stale | Entra AADSTS7000215 "Invalid client secret is provided" or AADSTS70002; a deployment still holds the old value after rotation | Check which version the running workload loaded; restart after secret updates where the app caches it | Overlap old and new secrets during rotation; read from a secrets manager at runtime |
| Secret identifier copied instead of value | The stored string looks like a GUID; the portal shows the value only once at creation | Create a new secret and store its value | Write the value straight into the secrets manager at creation |
| Credential missing | Entra AADSTS7000218 "The request body must contain the following parameter: 'client_assertion' or 'client_secret'." | Send the credential; check environment variable injection | Fail fast at start-up when credentials are absent |
| Public client sending a secret | Entra AADSTS700025 "Client is public so neither 'client_assertion' nor 'client_secret' should be presented." | Remove the credential, or register a confidential client for server-side use | Do not embed secrets in mobile, desktop or browser apps |
| Client disabled | Entra AADSTS7000112 "The application is disabled."; Google disabled_client "The OAuth client was disabled." | Find out who disabled it and why before re-enabling | Treat disablement as a possible security action |
Entra-specific service principal errors are covered in more depth in the Entra Service Principal Sign-In Errors Guide (AADSTS7000215, 7000222, 700016).
invalid_client with JWT assertions
With private_key_jwt (or client_secret_jwt), the client sends client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer and a signed JWT in client_assertion. RFC 7523 section 3.2 requires an invalid client JWT to produce invalid_client. Decode the assertion locally (it contains no secret, but treat it as sensitive until it expires) and check:
| Field | What the server expects | Common fault |
|---|---|---|
iss and sub | Both equal the client ID (RFC 7523; Okta and Entra state the same) | Service account email or object ID used instead of the client ID |
aud | The authorisation server; Entra documents https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token; Okta requires the full URL | Issuer URL instead of token endpoint, wrong tenant, trailing slash, v1 versus v2 endpoint |
exp, nbf, iat | Short lifetime: Okta rejects exp more than one hour ahead or in the past and iat after receipt; Microsoft recommends exp 5 to 10 minutes after nbf at most | Clock skew on the signing host; assertion cached and reused past expiry |
jti | Unique; Okta allows single use only when present | Library reuses a cached assertion across requests or retries |
Header kid, x5t#S256, alg | Identifies a key registered for the client; Entra documents PS256 and the certificate's SHA-256 thumbprint | Certificate rotated on the client but not uploaded, or the old one removed first; Entra AADSTS700027 "Client assertion failed signature validation" |
HMAC secret (client_secret_jwt) | Okta requires at least 32 characters | Okta returns "The client secret is too short to verify a JWT HMAC." |
Design guidance for this method is in the JWT Client Authentication (private_key_jwt) Guide.
invalid_client with mutual TLS
RFC 8705 (Standards Track, February 2020) defines two methods. With tls_client_auth the server validates the certificate chain and matches exactly one registered subject value: tls_client_auth_subject_dn or one of the SAN parameters (_san_dns, _san_uri, _san_ip, _san_email). With self_signed_tls_client_auth the server only checks the certificate against the client's registered jwks or jwks_uri. A missing or non-matching certificate returns invalid_client.
- Certificate never arrived: a load balancer or API gateway terminated TLS and did not forward the certificate. Check the authorisation server's logs for the presented certificate, not the client's.
- Wrong endpoint: if the server's metadata publishes
mtls_endpoint_aliases, mTLS clients must use those endpoints. Calling the conventional token endpoint gets no certificate request. - Subject mismatch: renewed certificate with a different DN ordering or SAN than registered. Compare the exact string.
- Chain not trusted: intermediate missing or issued by a CA the server does not trust for client authentication.
- Token later rejected at the API: certificate-bound tokens carry
cnfwithx5t#S256; a resource server seeing a different certificate returns 401invalid_token, notinvalid_client.
See the mTLS Guide for certificate design.
unauthorized_client: causes and fixes
| Cause | Evidence | Fix |
|---|---|---|
| Grant type not enabled on the client | Auth0 and Okta store allowed grants per application (Auth0 grant_types); the request uses one not in the list | Enable only the grant the use case needs |
| Public client asking for client credentials | Auth0: public applications (token_endpoint_auth_method none) cannot use client_credentials | Register a separate confidential client for the server workload; do not convert the public one |
| Google domain-wide delegation not authorised | "Unauthorized client or scope in request." | Authorise the service account's client ID and scopes in the Admin console; Google says allow up to 24 hours |
| Delegation registered the wrong way | "Client is unauthorized to retrieve access tokens using this method"; authorised by email address or via a group | Re-add using the numeric client ID |
| Third-party or restricted app type | Auth0 third-party applications only support authorisation code with PKCE, refresh token and client credentials (confidential only) | Use a supported grant |
| Client not present or not consented in the tenant | Entra AADSTS700016 (UnauthorizedClient_DoesNotMatchRequest) | Correct the tenant or have an administrator add and consent to the application |
Choosing the client authentication method
Some errors go away permanently with a better method. Use this when the fix is a redesign rather than a correction.
| Method | Typical errors | Best for | Trade-off |
|---|---|---|---|
client_secret_basic / _post | Encoding, expiry, stale secret | Small estates, low-risk integrations | Shared secret can leak and be replayed; needs rotation |
private_key_jwt | Claim and clock errors, key rollover | Most server workloads; recommended over secrets | Key management and clock discipline required |
tls_client_auth / self_signed_tls_client_auth | Proxy termination, subject mismatch | High-assurance and regulated APIs; certificate-bound tokens | Infrastructure must pass certificates end to end |
| Workload identity federation | Trust and claim mismatches | Cloud and CI workloads with a platform-issued identity | Depends on platform support; see the Multi-Cloud Workload Identity Troubleshooting Guide |
Small organisations can stay on well-managed secrets with expiry alerts; larger estates should move server workloads to asymmetric or federated credentials so the commonest invalid_client causes disappear.
Diagnosis workflow, testing and rollback
- Capture: endpoint, client ID,
grant_type, authentication method used, response status,WWW-Authenticateheader and full error body. Redact secrets and assertions. - Read the server log: Entra sign-in logs for service principals, Okta System Log, Auth0 tenant logs, Google Cloud audit logs. They usually state which check failed.
- Classify: authentication (invalid_client) or permission (unauthorized_client).
- Reproduce against a non-production client with the same settings using the same library version.
- Change one thing: for credential issues, add the new credential alongside the old, deploy, confirm, then remove the old. Rolling back is then just pointing at the previous credential.
- Clean up: delete any extra secrets or grant types added during troubleshooting.
Where identity fits
An OAuth client is a non-human identity, and every secret or certificate added to it is another way in. Credentials created in a hurry to clear an outage are often never removed, which is exactly the kind of extra cloud credential attackers add for persistence (MITRE ATT&CK T1098.001). The Microsoft Midnight Blizzard breach shows how a forgotten test tenant and an OAuth app opened executive mailboxes. Give each client an owner, keep credentials in a secrets manager, and alert on new credentials; see How to Detect new credentials added to an app registration and the Client ID and Client Secret Guide.
Detection and measurement
- invalid_client rate per client ID: a burst after a deployment or rotation, or a steady trickle from an unknown source IP (possible credential guessing or a leaked client ID).
- unauthorized_client events: each one is either a misconfiguration or a client attempting a grant it should not use; review all.
- Credentials per client: clients with more than two active secrets or certificates.
- Days to expiry: secrets and certificates expiring within 30 days, with owner notified.
- Share of clients on asymmetric or federated credentials: trending up across the estate.
Common failure patterns
- New secret for every error: credentials pile up and none are removed.
- Enabling every grant type: clears
unauthorized_clientand lets the client obtain tokens in ways nobody reviewed. - Converting a public client to confidential: to use client credentials from a browser or mobile app, which then ships the secret to every user.
- Removing the old certificate first: a rollover that breaks every instance still signing with the old key.
- Secrets in configuration files: copied between environments, so a staging secret meets a production client ID (and the file itself is a target, ATT&CK T1552.001).
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | IA-9 Service Identification and Authentication; IA-5 Authenticator Management; AU-6 Audit Record Review | Authenticating clients, managing their secrets and keys, reviewing failures |
| NIST CSF 2.0 | PR.AA-01; PR.AA-03 | Managing and authenticating service identities |
| ISO/IEC 27001:2022 | A.5.17 Authentication information; A.8.5 Secure authentication | Secret handling and authentication method strength |
| MITRE ATT&CK | T1098.001 Additional Cloud Credentials; T1552.001 Credentials In Files; T1078.004 Cloud Accounts | Abuse of extra, stored or stolen client credentials |
| IETF | RFC 6749 s2.3 and s5.2; RFC 7523 s3.2; RFC 8705 | Where the errors and methods are defined |
Practitioner checklist
- Distinguish authentication failure from grant permission failure before changing anything.
- Confirm endpoint, tenant and client ID match the environment.
- Send exactly one client authentication method, matching the registration.
- URL-encode client ID and secret before Base64 for Basic authentication.
- Decode and check
iss,sub,aud,exp,jtiand key ID on every assertion failure. - Confirm the client certificate reaches the authorisation server and the mTLS endpoint alias is used.
- Enable only the grant types each client needs.
- Rotate with overlap and remove any credentials added during troubleshooting.
- Alert on new client credentials and on expiry within 30 days.
- Move server workloads from shared secrets to private_key_jwt, mTLS or federation.
Standards and references
- IETF: RFC 6749 The OAuth 2.0 Authorization Framework
- IETF: RFC 7523 JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
- IETF: RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens
- Microsoft Learn: Microsoft Entra authentication and authorization error codes
- Microsoft Learn: Microsoft identity platform application authentication certificate credentials
- Google: Using OAuth 2.0 for Server to Server Applications
- Okta: Client authentication methods
- Auth0: Application Grant Types
Related NHI Mgmt Group resources: OAuth invalid_grant Error Troubleshooting Guide · Machine-to-Machine OAuth (Client Credentials) Guide · JWT Client Authentication (private_key_jwt) Guide · Azure Entra Client Secrets Security Guide · OAuth 2.0 and OpenID Connect Guide