medusajs/medusa · error · MedusaError

The identity provider's ID token is missing the '${entityIdC

Error message

The identity provider's ID token is missing the '${entityIdClaim}' claim used to identify the user

What it means

mapClaims extracts the user identifier from a configurable claim (mappings.entity_id, defaulting typically to 'sub'). If that claim is absent, null, or empty string in the ID token, the engine cannot identify the user and throws INVALID_DATA naming the missing claim.

Source

Thrown at packages/modules/providers/auth-oidc/src/engine/engine.ts:220

   *
   * `entity_id` defaults to the `sub` claim (never the email, which is mutable
   * and reassignable).
   */
  mapClaims(claims: Record<string, unknown>): OidcMappedClaims {
    const mappings: OidcClaimMappings = {
      ...DEFAULT_CLAIM_MAPPINGS,
      ...this.options_.claim_mappings,
    }

    const entityIdClaim = mappings.entity_id ?? "sub"
    const entityIdValue = claims[entityIdClaim]

    if (
      entityIdValue === undefined ||
      entityIdValue === null ||
      entityIdValue === ""
    ) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        `The identity provider's ID token is missing the '${entityIdClaim}' claim used to identify the user`
      )
    }

    const emailClaim = mappings.email ?? "email"
    const email = claims[emailClaim]

    const requireVerifiedEmail = this.options_.require_verified_email ?? true
    if (requireVerifiedEmail && claims.email_verified !== true) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        "The identity provider did not confirm a verified email address"
      )
    }

    if (this.options_.allowed_email_domains?.length) {
      const allowedDomains = this.options_.allowed_email_domains.map((domain) =>

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Decode the ID token (e.g. jwt.io or JSON.parse(Buffer.from(token.split('.')[1], 'base64'))) and check which claims are actually present.
  2. Either set mappings.entity_id to a claim that exists (commonly 'sub' or 'email'), or configure the IdP to include the desired claim in the ID token.
  3. Request the scopes (e.g. 'email', 'profile') that make the IdP emit the claim.

Example fix

// before
options: { ..., mappings: { entity_id: "preferred_username" } }
// after
options: { ..., mappings: { entity_id: "sub" } }
Defensive patterns

Strategy: validation

Validate before calling

function decodeJwtClaims(idToken: string): Record<string, unknown> {
  const payload = idToken.split(".")[1]
  return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"))
}
const claims = decodeJwtClaims(idToken)
if (claims[mappings.entity_id ?? "sub"] == null) throw new Error("entity_id claim missing in ID token")

Type guard

const hasEntityIdClaim = (claims: Record<string, unknown>, claim: string): boolean =>
  claims[claim] !== undefined && claims[claim] !== null && claims[claim] !== ""

Try / catch

try { engine.mapClaims(claims) } catch (e) { if (e instanceof MedusaError && /missing the '.*' claim/.test(e.message)) { /* adjust mappings or IdP claim config */ } throw e }

Prevention

When it happens

Trigger: mappings.entity_id is set to a claim the IdP does not emit (e.g. 'preferred_username' on a provider that doesn't include it in the ID token); the claim only appears in the userinfo endpoint, not the ID token; scopes needed to include the claim were not requested so the claim is empty.

Common situations: Copied claim mappings from a different IdP (Azure AD vs Keycloak vs Google emit different claims); custom claim configured in Keycloak but not added to the ID token mapper; requested scopes don't cover the claim (e.g. email claim without 'email' scope).

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/32939e0fe6052623. Report an issue: GitHub.