Skip to content

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

At a glance
CodeAADSTS50027
Microsoft nameInvalidJwtToken
CategoryCredentials and lockout
TypeFailure
Attack signalMedium: worth a look in context
Where to lookSigninLogs, AADServicePrincipalSignInLogs
Microsoft lookuplogin.microsoftonline.com/error?code=50027

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_assertion instead 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, sub and duplicate-claim bullets, which name idToken explicitly.

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.

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.

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.

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 SourceTable

The 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 desc

ClientCredentialType 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 asc

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

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.

For developers sending a client assertion:

  1. Decode what you are sending. If it isn’t three base64url segments separated by dots, it is not a JWT and nothing else matters.
  2. Build the header with alg PS256, typ JWT and x5t#S256 set to the certificate’s SHA-256 thumbprint, and the claims aud, iss, sub, jti, nbf, exp and iat as Microsoft’s certificate credentials page specifies. iss and sub are both the client ID.
  3. Keep the lifetime short, 5 to 10 minutes after nbf. If the window has passed you get 700024 rather than 50027.
  4. Sign with the private key of a certificate registered on the app registration, using PSS padding.
  5. 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.
  6. 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:

  1. 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.
  2. 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”.
  3. Check clock skew. “Not within its valid time range” is a time problem on the provider, not in Entra.

For admins generally:

  1. Use the queries above to identify the app and its owner, and whether any source that failed also succeeded.
  2. 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.
  • 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

  1. Microsoft Entra authentication and authorization error codes
  2. Microsoft identity platform application authentication certificate credentials
  3. Microsoft identity platform and the OAuth 2.0 client credentials flow
  4. AADServicePrincipalSignInLogs table reference (Azure Monitor)
  5. SigninLogs table reference (Azure Monitor)