Name the actual OIDC token verification failure on the login page

Every ID token verification failure rendered the issuer-mismatch
advice, sending users with audience or clock problems down the wrong
path and telling users with a genuine issuer mismatch nothing they had
not already checked (#1533). Map the distinct verification failures
(issuer, audience, expiry) to their own error codes and give each
accurate login-page copy pointing at the server log's got/want detail.
This commit is contained in:
rcourtman
2026-07-16 10:15:42 +01:00
parent 506d2e5b7e
commit 4dcba3a1d8
2 changed files with 20 additions and 2 deletions
+7 -1
View File
@@ -84,7 +84,13 @@ export const Login: Component<LoginProps> = (props) => {
case 'session_failed':
return 'Login succeeded but we could not create a session. Try again.';
case 'invalid_id_token':
return 'ID token verification failed. Check that OIDC_ISSUER_URL matches the issuer claim in your provider tokens (check server logs for details).';
return 'ID token verification failed. The server log records the exact reason for this login attempt.';
case 'issuer_mismatch':
return 'The token was issued by a different issuer than the one configured. The server log shows the expected and received issuer values for this login attempt.';
case 'audience_mismatch':
return 'The token was issued for a different client ID than the one configured in Pulse. Check the client ID against your provider application.';
case 'token_expired':
return 'The identity provider returned an already-expired token. Check that the clocks on Pulse and the provider are in sync.';
case 'invalid_signature_alg':
return "The identity provider is issuing HS256 tokens. Configure it to sign ID tokens with RS256 (see your IdP's OIDC settings).";
case 'invalid_nonce':
+13 -1
View File
@@ -581,9 +581,21 @@ func (r *Router) handleSSOOIDCCallback(w http.ResponseWriter, req *http.Request)
idToken, err := service.verifier.Verify(ctx, rawIDToken)
if err != nil {
// Distinguish the verification failure modes so the login page can
// name the actual problem. The generic code used to render issuer
// advice for every failure, sending users with audience or clock
// problems down the wrong path (#1533).
errorCode := "invalid_id_token"
if strings.Contains(err.Error(), "unexpected signature algorithm") {
errMsg := err.Error()
switch {
case strings.Contains(errMsg, "unexpected signature algorithm"):
errorCode = "invalid_signature_alg"
case strings.Contains(errMsg, "issued by a different provider"):
errorCode = "issuer_mismatch"
case strings.Contains(errMsg, "expected audience"):
errorCode = "audience_mismatch"
case strings.Contains(errMsg, "token is expired"):
errorCode = "token_expired"
}
log.Error().Err(err).Str("provider_id", providerID).Msg("Failed to verify ID token")
LogAuditEventForTenant(GetOrgID(req.Context()), "sso_oidc_login", "", GetClientIP(req), req.URL.Path, false, "ID token verification failed: "+err.Error())