AADSTS50027: JWT token is invalid or malformed (fix)
Short answer: AADSTS50027 means Microsoft Entra ID received a JWT it would not accept: wrong format, missing or duplicated claims, unexpected issuer or audience, outside its valid time range, or a failed signature check on an external ID token. It is usually a developer error. The two common sources are a hand-built client assertion and a federated identity provider's ID token.
Fix: Developer sending a client assertion: build a real JWT with the header and claims Microsoft documents, signed with the certificate registered on the app. Federation admin: fix the issuer, audience or signing key on the identity provider. Full steps
| Code | AADSTS50027 |
|---|---|
| Microsoft name | InvalidJwtToken |
| Category | Credentials and lockout |
| Type | Failure |
| Attack signal | Medium: worth a look in context |
| Where to look | SigninLogs, AADServicePrincipalSignInLogs |
| Microsoft lookup | login.microsoftonline.com/error?code=50027 |
What does AADSTS50027 mean?
Section titled “What does AADSTS50027 mean?”Microsoft’s description:
InvalidJwtToken: Invalid JWT token because of the following reasons:
- doesn’t contain nonce claim, sub claim
- subject identifier mismatch
- duplicate claim in idToken claims
- unexpected issuer
- unexpected audience
- not within its valid time range
- token format isn’t proper
- External ID token from issuer failed signature verification.
That is eight different conditions behind one code, which is why the fix depends entirely on which JWT Entra was looking at. Two kinds of request put a JWT in front of Entra:
- A client assertion. An app authenticating with a certificate or a federated credential sends
client_assertioninstead of a secret. Our lab hit 50027 this way. - An external ID token. A federated identity provider returns an ID token to Entra, which validates it. The last bullet in Microsoft’s list, “External ID token from issuer failed signature verification”, is this case, and so are the
nonce,suband duplicate-claim bullets, which nameidTokenexplicitly.
For the client assertion case, Microsoft’s certificate credentials page documents what the JWT must contain. The header:
| Parameter | Microsoft’s remark |
|---|---|
alg |
Should be PS256 |
typ |
Should be JWT |
x5t#S256 |
Base64url-encoded SHA-256 thumbprint of the X.509 certificate’s DER encoding. |
And the claims: aud (https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token), iss and sub (both the client ID), jti, nbf, exp and iat. On the lifetime, Microsoft says to “keep it short - 5-10 minutes after nbf at most”. The signature “is computed by applying the certificate as described in the JSON Web Token RFC7519 specification”, using PSS padding.
Several of Microsoft’s eight bullets map onto that table: aud wrong is “unexpected audience”, iss wrong is “unexpected issuer”, and an exp in the past is “not within its valid time range”.
Which assertion error you get, and in what order
Section titled “Which assertion error you get, and in what order”Our lab separated three of the assertion failures. Entra checks them in this order:
| What is wrong | Code |
|---|---|
client_assertion_type is not the JWT bearer URN |
90023 “Unexpected client assertion type.” |
| The assertion is not a well-formed JWT | 50027 |
| The assertion is a JWT but its validity window has passed or not begun | 700024 |
Nothing was sent at all on a client_credentials request |
7000216 |
Microsoft’s reference also lists AADSTS700027, “Client assertion failed signature validation”, for a well-formed JWT with a bad signature. So 50027 sits between “wrong kind of parameter” and “right shape, wrong signature”: the string reached the JWT parser and the parser rejected it.
Reproduced in the lab
Section titled “Reproduced in the lab”On 30 October 2026 an automated lab run sent a client-credentials token request with the correct client_assertion_type and a client_assertion that was not a JWT. Entra returned:
AADSTS50027: JWT token is invalid or malformed.
Both rounds of the run returned the same code and the same one-line message, short enough that the lab’s 160-character cut does not truncate it. The run had expected 9002313, 50027 or 700027, and 50027 is the one it got. In the same run, the same request with an unaccepted client_assertion_type returned 90023, and a syntactically valid assertion whose validity window had passed returned 700024 with the current time and the assertion’s nbf in the message. So the lab confirms the format case specifically, and not the other seven conditions in Microsoft’s list.
No sign-in log row was captured. See how pages are checked.
Is it an attack?
Section titled “Is it an attack?”Usually not, and this is the one code on this page’s family where the answer deserves a second look. A malformed JWT is what a developer produces; it is also what someone replaying or editing a captured token produces.
| Pattern | Likely meaning |
|---|---|
| Repeated 50027 for one app from your own build agents or servers | A client assertion implementation under development, or a certificate rotation that went wrong. Benign. |
| 50027 on an app that has worked for months, with no deployment | Look at the certificate. An expired or replaced certificate usually gives a different code, but a half-finished rotation can produce assertions nobody can parse. |
| 50027 for one app from an address you don’t own | Someone is sending assertions for your app ID. They do not have a working credential, or you would see a success instead. Check for successes from the same address. |
| 50027 on user sign-ins through a federated identity provider | The identity provider’s ID token is being rejected. Check the issuer, the audience and the signing key on the provider, and whether a certificate was rolled. |
| A burst across several apps from one address | Someone is probing with crafted tokens. Nothing is disclosed by this error, but the source is worth a look. |
Our reading on the signal: 50027 means a token was refused, so it is evidence of a failure rather than of a compromise. What makes it worth watching is a success from the same source shortly after.
How do I find it in my logs (KQL)?
Section titled “How do I find it in my logs (KQL)?”Which table 50027 lands in depends on which JWT failed, and Microsoft doesn’t document it for either case. Our reading: a client assertion is an app signing in, so AADServicePrincipalSignInLogs; a federated user’s ID token is a user sign-in, so SigninLogs or AADNonInteractiveUserSignInLogs. Start with the union:
union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs| where TimeGenerated > ago(30d)| where ResultType == "50027" or ResultSignature == "50027"| summarize Rows = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), Apps = make_set(AppId, 10) by SourceTableThe filter covers both columns because the tables describe them differently: in SigninLogs, ResultType “provides the 5-6 digit error code that’s generated during a sign-in event”, while AADServicePrincipalSignInLogs describes ResultType as “Success or Failure” and ResultSignature as “the error code, if any, for the sign-in operation”.
For the client assertion case, which app and which source:
AADServicePrincipalSignInLogs| where TimeGenerated > ago(7d)| where ResultType == "50027" or ResultSignature == "50027"| summarize Failures = count(), LastSeen = max(TimeGenerated), CredentialTypes = make_set(ClientCredentialType, 5), Thumbprints = make_set(ServicePrincipalCredentialThumbprint, 5), Descriptions = make_set(ResultDescription, 5) by AppId, ServicePrincipalName, IPAddress| sort by Failures descClientCredentialType is documented as “the type of client credential used. Examples include client assertion, client secret, etc.” Whether it and the thumbprint column are filled in when the JWT could not be parsed is not documented: a token Entra cannot read may not identify a credential at all. Check a row before you rely on either column.
The pattern worth alerting on is a refusal followed by a success from the same place:
let failedFrom = AADServicePrincipalSignInLogs | where TimeGenerated > ago(7d) | where ResultType == "50027" or ResultSignature == "50027" | summarize FirstFail = min(TimeGenerated) by AppId, IPAddress;AADServicePrincipalSignInLogs| where TimeGenerated > ago(7d)| where ResultType == "0" or ResultType =~ "Success"| join kind=inner failedFrom on AppId, IPAddress| where TimeGenerated > FirstFail| project TimeGenerated, AppId, ServicePrincipalName, IPAddress, ClientCredentialType, FirstFail| sort by TimeGenerated ascThresholds are a starting point only: for a developer’s own address, dozens an hour mean nothing; for an address with no history for that app, one row is enough to look at.
How does Conditional Access affect it?
Section titled “How does Conditional Access affect it?”Not relevant to the client assertion case: there is no user and no policy evaluation, and the request fails on the credential. For the federated user case, 50027 happens while Entra is validating the incoming token, which is before the point where Conditional Access decides anything. Our reading, by analogy with other first-factor failures; Microsoft doesn’t state it for this code.
How do I fix AADSTS50027?
Section titled “How do I fix AADSTS50027?”For developers sending a client assertion:
- Decode what you are sending. If it isn’t three base64url segments separated by dots, it is not a JWT and nothing else matters.
- Build the header with
algPS256,typJWT andx5t#S256set to the certificate’s SHA-256 thumbprint, and the claimsaud,iss,sub,jti,nbf,expandiatas Microsoft’s certificate credentials page specifies.issandsubare both the client ID. - Keep the lifetime short, 5 to 10 minutes after
nbf. If the window has passed you get 700024 rather than 50027. - Sign with the private key of a certificate registered on the app registration, using PSS padding.
- Let a library do it. MSAL builds the assertion from a certificate for you, and most 50027 in this case comes from hand-rolled code.
- Send it with
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer, or you get 90023 first.
For admins of a federated identity provider:
- Work out which of Microsoft’s eight conditions applies before changing anything. The
nonce,sub, duplicate-claim and signature-verification bullets are all about an external ID token. - Check the issuer and audience values configured on both sides, and that the provider’s current signing key is the one Entra is reading. A key rollover on the provider gives “External ID token from issuer failed signature verification”.
- Check clock skew. “Not within its valid time range” is a time problem on the provider, not in Entra.
For admins generally:
- Use the queries above to identify the app and its owner, and whether any source that failed also succeeded.
- If an address with no history for the app is sending assertions, treat the app ID as known to someone outside and review the app’s permissions.
Related codes
- AADSTS90023: Entra rejected the request as invalid
- AADSTS700024: Client assertion is not within its valid time range
- AADSTS7000216: No client credential in a client_credentials request
- AADSTS7000218: Client secret or assertion missing from the request
Sources
- Microsoft Entra authentication and authorization error codes
- Microsoft identity platform application authentication certificate credentials
- Microsoft identity platform and the OAuth 2.0 client credentials flow
- AADServicePrincipalSignInLogs table reference (Azure Monitor)
- SigninLogs table reference (Azure Monitor)