Azure AD JWT Token Not Accepted by ServiceNow MCP Server – "Key ID not found in JWKS"

ubhimani
Tera Expert

Hi everyone,

I’m currently working on integrating authentication between Microsoft Azure Active Directory and the ServiceNow MCP server, and I’m running into an issue with JWT token validation.

 

We are implementing a token-based authentication flow where:

  • Users are authenticated via Azure AD
  • A JWT access token is generated using OAuth 2.0 / OpenID Connect
  • This token is then used to authenticate requests to the ServiceNow MCP server

The goal is to enable secure, token-based authentication using Azure AD–issued tokens.


Current Setup

  • Identity Provider: Microsoft Azure Active Directory
  • Authentication Protocol: OAuth 2.0 / OpenID Connect
  • Token Type: JWT Access Token
  • API Testing Tool: Insomnia
  • Target System: ServiceNow MCP Server

Observed Behavior

  • Azure AD authentication is working as expected
    • Successfully generating JWT access tokens via Insomnia
    • Token appears structurally valid and contains expected claims
  • However, when using the same token to authenticate with the ServiceNow MCP server:
    • The request fails during authentication

Error Message

 

Streamable HTTP error: Error POSTing to endpoint:
{
"error": "Authentication failed",
"message": "Key ID not found in JWKS",
"details": "Please provide a valid JWT token"
}
 

What We’re Trying to Understand

  • How does ServiceNow MCP validate JWT tokens against JWKS?
  • Does MCP require explicit configuration of Azure AD’s JWKS endpoint?
  • Are there any additional steps required to trust external IdPs like Azure AD?
  • Could this be related to key rotation, kid mismatch, or issuer/audience validation?

Additional Context

  • Token includes a kid in the header
  • Azure AD JWKS endpoint is accessible and returns signing keys
  • No custom token manipulation is being performed

 

Has anyone successfully configured Azure AD JWT authentication with the ServiceNow MCP server?

Any pointers on:

  • Required MCP configuration for external JWKS validation
  • Common pitfalls with Azure AD tokens
  • Debugging "Key ID not found in JWKS" errors

would be really helpful.


Thanks in advance for your help!

1 REPLY 1

Naveen20
ServiceNow Employee

This is a classic Azure AD + JWKS integration headache, and the error message is very specific — "Key ID not found in JWKS" tells you exactly where the validation pipeline is breaking. The MCP server is performing JWKS lookup, it's just not finding a key matching your token's kid. Here's what's almost certainly going on and how to fix it.

The Root Cause: Token Version vs. JWKS Endpoint Mismatch

Azure AD has two token issuance versions (v1.0 and v2.0), and each uses a different JWKS endpoint with different signing keys. This is by far the most common cause of kid not found errors.

First, decode your token (use jwt.ms or jwt.io) and check two things in the header and payload:

Token header — look at the kid value:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "nOo3ZDrODXEK1jKWhXslHR_KXEg"  // <-- this is what MCP tries to match
}

Token payload — check the iss (issuer) claim:

v1.0 → "iss": "https://sts.windows.net/{tenant-id}/"
v2.0 → "iss": "https://login.microsoftonline.com/{tenant-id}/v2.0"

The issuer tells you which version your token actually is. Then verify the MCP server is fetching keys from the matching JWKS endpoint:

Token Version Correct JWKS URI
v1.0 https://login.microsoftonline.com/{tenant-id}/discovery/keys
v2.0 https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys

If your token is v1.0 but the MCP server is hitting the v2.0 keys endpoint (or vice versa), the kid simply won't exist in that key set.

Debugging Steps

Step 1 — Confirm the kid exists in the right JWKS endpoint. Hit the JWKS URI directly and grep for your token's kid:

# Get your token's kid
echo '<your-jwt>' | cut -d'.' -f1 | base64 -d 2>/dev/null | jq '.kid'

# Then fetch the JWKS and check if that kid exists
curl -s "https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys" | jq '.keys[].kid'

If your kid isn't in that response, try the other version's endpoint. That confirms the version mismatch.

Step 2 — Check accessTokenAcceptedVersion in your Azure AD App Registration. In the Azure Portal, go to your App Registration → Manifest and look for:

"accessTokenAcceptedVersion": null  // null defaults to v1.0!

Set this explicitly to 2 if you want v2.0 tokens:

"accessTokenAcceptedVersion": 2

This is a very common gotcha — null silently defaults to v1.0, so people assume they're getting v2.0 tokens when they aren't.

Step 3 — Verify the MCP server's JWKS configuration. The ServiceNow MCP server needs to know where to fetch signing keys. Look for configuration like:

JWKS_URI=https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys

or an OpenID Connect discovery URL:

OIDC_DISCOVERY_URL=https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration

The discovery document contains a jwks_uri field that the MCP server should follow automatically. Make sure this points to the correct tenant and version.

Other Things to Check

Audience (aud) claim. Azure AD issues different token formats depending on the audience. If aud is set to https://graph.microsoft.com or another Microsoft first-party resource, Azure issues an opaque or specially-signed token that may use different keys than your tenant's JWKS. Your aud should be your own App Registration's Application ID URI (e.g., api://{client-id}).

Multi-tenant vs. single-tenant. If your app is multi-tenant, you might need the "common" JWKS endpoint (/common/discovery/v2.0/keys), but this returns keys across all tenants and can be unreliable for strict validation. Pin to your specific tenant ID whenever possible.

Key rotation. Azure AD rotates signing keys periodically (roughly every 6 weeks). If the MCP server caches JWKS keys aggressively without a refresh mechanism, a rotation event will cause this exact error. The server should either respect HTTP cache headers on the JWKS response or re-fetch keys when a kid isn't found locally.

Quick Fix Checklist

  1. Decode the JWT and note the kid and iss values
  2. Determine if it's a v1.0 or v2.0 token from the iss format
  3. Confirm the kid exists in the corresponding JWKS endpoint
  4. Ensure accessTokenAcceptedVersion is set explicitly (not null) in the App Manifest
  5. Ensure aud points to your own app, not a Microsoft resource
  6. Configure the MCP server's JWKS URI or OIDC discovery URL to match the token version
  7. Verify the MCP server refreshes JWKS keys on kid miss (not just at startup)