Sign-in is refused when one ID token claim has an unexpected type, even a claim the console never reads #232

Open
opened 2026-10-16 02:59:53 +00:00 by cgalo5758 · 0 comments
Owner

What happens

After the identity provider signs a person in, the console reads seven claims from the ID token into one struct: sub, email, email_verified, name, preferred_username, given_name and family_name (internal/auth/auth.go:599-607). If any of them arrives with a JSON type other than the one the struct expects, the whole parse fails, and the console answers 500 with the page "Sign-in failed" (auth.go:608-613). "Sign in again" fails the same way, because the provider sends the same token every time.

Two of the seven are never used. given_name and family_name are parsed and then dropped; only the other five go on to provisioning (auth.go:615-621). A provider that sends given_name as a list refuses every person it applies to over a value the console throws away.

email_verified is the likeliest trigger. OpenID Connect defines it as a boolean, but some providers send the string "true". The OIDC library the console uses accepts the string form in its own user-info parsing for that reason (stringAsBool in github.com/coreos/go-oidc/v3). The console's parse does not.

What should happen

A claim the console does not read never affects sign-in: the parse reads only the five claims provisioning uses. email_verified sent as the string "true" or "false" reads as that boolean. A used claim with any other unexpected type counts as absent and is logged as a warning, the same as a provider that leaves it out. sub needs no change: token verification already requires it to be a string.

Where

internal/auth/auth.go:599-621 (CallbackHandler, the user-claims parse). The golden internal/auth/testdata/callback/email-verified-as-a-string.txt pins today's refusal, so the fix changes it on purpose.

Steps

  1. In Keycloak, set the Claim JSON Type of the client's email verified mapper to String.
  2. Sign in to the console.
  3. The page reads "Sign-in failed". The log reads could not parse the ID token's user claims with json: cannot unmarshal string into Go struct field .email_verified of type bool.

The email-verified-as-a-string row of TestCallbackEdges in internal/auth drives the same case without a provider.

Why it matters

The console is meant to run against any OpenID Connect provider. On a provider that sends one claim in an unexpected shape, nobody it applies to can sign in, retrying does not help, and the cause shows only in the server log.

## What happens After the identity provider signs a person in, the console reads seven claims from the ID token into one struct: `sub`, `email`, `email_verified`, `name`, `preferred_username`, `given_name` and `family_name` (`internal/auth/auth.go:599-607`). If any of them arrives with a JSON type other than the one the struct expects, the whole parse fails, and the console answers 500 with the page "Sign-in failed" (`auth.go:608-613`). "Sign in again" fails the same way, because the provider sends the same token every time. Two of the seven are never used. `given_name` and `family_name` are parsed and then dropped; only the other five go on to provisioning (`auth.go:615-621`). A provider that sends `given_name` as a list refuses every person it applies to over a value the console throws away. `email_verified` is the likeliest trigger. OpenID Connect defines it as a boolean, but some providers send the string `"true"`. The OIDC library the console uses accepts the string form in its own user-info parsing for that reason (`stringAsBool` in `github.com/coreos/go-oidc/v3`). The console's parse does not. ## What should happen A claim the console does not read never affects sign-in: the parse reads only the five claims provisioning uses. `email_verified` sent as the string `"true"` or `"false"` reads as that boolean. A used claim with any other unexpected type counts as absent and is logged as a warning, the same as a provider that leaves it out. `sub` needs no change: token verification already requires it to be a string. ## Where `internal/auth/auth.go:599-621` (`CallbackHandler`, the user-claims parse). The golden `internal/auth/testdata/callback/email-verified-as-a-string.txt` pins today's refusal, so the fix changes it on purpose. ## Steps 1. In Keycloak, set the Claim JSON Type of the client's `email verified` mapper to String. 2. Sign in to the console. 3. The page reads "Sign-in failed". The log reads `could not parse the ID token's user claims` with `json: cannot unmarshal string into Go struct field .email_verified of type bool`. The `email-verified-as-a-string` row of `TestCallbackEdges` in `internal/auth` drives the same case without a provider. ## Why it matters The console is meant to run against any OpenID Connect provider. On a provider that sends one claim in an unexpected shape, nobody it applies to can sign in, retrying does not help, and the cause shows only in the server log.
cgalo5758 added the
kind
bug
area/identity
priority
low
labels 2026-10-16 02:59:53 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wiki-cafe/member-console#232