You get AADSTS50027 when Azure AD rejects a JWT token because it's malformed, has the wrong audience, expired, or fails signature validation. This typically happens during app authentication or API calls using OAuth/OpenID Connect.
The Fix
Start by decoding the token to see what's actually wrong. Copy the JWT from your logs or network trace (the long string after "Bearer"), then decode it:
# Save token to variable
$token = "YOUR_JWT_TOKEN_HERE"
Decode header and payload (PowerShell)
$parts = $token.Split('.')
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($parts[0]))
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($parts[1]))Or use https://jwt.ms to decode it instantly. Look at the aud (audience), iss (issuer), exp (expiration), and kid (key ID) claims.
Fix the audience mismatch - This is the most common cause. Your token's aud claim must match the resource you're calling. If calling Microsoft Graph, request the token with the correct scope:
# Example: Get token for Graph API
$body = @{
client_id = "YOUR_CLIENT_ID"
scope = "https://graph.microsoft.com/.default"
client_secret = "YOUR_CLIENT_SECRET"
grant_type = "client_credentials"
}
$response = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token" -Body $body
$response.access_tokenCheck your app registration's Application ID URI and ensure your code requests tokens for that exact URI.
If That Doesn't Work:
Fix the issuer validation. Your app might be rejecting tokens from the correct tenant but wrong endpoint version. Get your tenant's metadata:
$tenant = "YOUR_TENANT_ID"
$metadata = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenant/v2.0/.well-known/openid-configuration"
$metadata.issuerCompare this issuer URL to the iss claim in your token. If they don't match, update your authentication endpoint (v1.0 vs v2.0) or configure your app to accept both.
If That Doesn't Work:
Sync your system clock. Token expiration uses strict time validation:
# Force Windows time sync
w32tm /resync /nowait
Or restart time service
Restart-Service w32time
w32tm /resyncOn Linux:
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncdIf That Doesn't Work:
Fix signature validation by refreshing signing keys. Azure rotates keys regularly, and cached keys might be stale:
# Get current signing keys
$tenant = "YOUR_TENANT_ID"
$metadata = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenant/v2.0/.well-known/openid-configuration"
$keys = Invoke-RestMethod -Uri $metadata.jwks_uri
$keys.keys | Select-Object kid, kty, useCheck if your token's kid in the header matches any key in this list. If not, your app is caching old keys. Clear your application's key cache or implement automatic key refresh using MSAL libraries.
If That Doesn't Work:
Fix missing nonce claims in ID tokens. This happens with custom OIDC implementations. Switch to MSAL libraries which handle this automatically:
Install-Package Microsoft.Identity.ClientOr add nonce parameter to your authorization request if you're building the URL manually: &nonce=RANDOM_VALUE&response_type=id_token
If That Doesn't Work:
For client certificate authentication, verify your uploaded certificate matches your assertion JWT:
# List certificates in app registration via Graph API
$appId = "YOUR_APP_ID"
Invoke-RestMethod -Headers @{Authorization="Bearer $token"} -Uri "https://graph.microsoft.com/v1.0/applications?$filter=appId eq '$appId'&$select=keyCredentials"The kid in your client assertion JWT header must reference an uploaded certificate's key ID.
Verify
Request a new token with your fixes and decode it again. Confirm the aud matches your resource, iss matches your tenant's metadata issuer, exp is in the future, and kid exists in the signing keys endpoint. Test your API call - it should succeed without AADSTS50027.