Surface invalid OIDC state on auth error page

Missing, expired, or reused OIDC state left users on a JSON error
or a generic failure. Map those cases to invalid_state so they get
a clear prompt to restart sign-in.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
Bryan Frimin
2026-07-24 21:37:31 +00:00
committed by Cursor Agent
parent 318789ec38
commit b8e5d279a0
3 changed files with 18 additions and 1 deletions

View File

@@ -46,6 +46,13 @@ function useAuthErrorContent(code: string | null): AuthErrorContent {
"Your email address is not verified with the identity provider. Please verify it, then try signing in again.", "Your email address is not verified with the identity provider. Please verify it, then try signing in again.",
), ),
}; };
case "invalid_state":
return {
title: __("Sign-in session expired"),
description: __(
"This sign-in attempt is no longer valid. Please start again from the sign-in page.",
),
};
case "magic_link_expired": case "magic_link_expired":
return { return {
title: __("Link Expired"), title: __("Link Expired"),

View File

@@ -31,6 +31,7 @@ import (
const ( const (
authErrorPersonalAccountNotAllowed = "personal_account_not_allowed" authErrorPersonalAccountNotAllowed = "personal_account_not_allowed"
authErrorEmailNotVerified = "email_not_verified" authErrorEmailNotVerified = "email_not_verified"
authErrorInvalidState = "invalid_state"
authErrorAuthenticationFailed = "authentication_failed" authErrorAuthenticationFailed = "authentication_failed"
authErrorMagicLinkExpired = "magic_link_expired" authErrorMagicLinkExpired = "magic_link_expired"
authErrorMagicLinkAlreadyUsed = "magic_link_already_used" authErrorMagicLinkAlreadyUsed = "magic_link_already_used"

View File

@@ -125,7 +125,9 @@ func (h *OIDCHandler) CallbackHandler(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code") code := r.URL.Query().Get("code")
if stateParam == "" || code == "" { if stateParam == "" || code == "" {
httpserver.RenderError(w, http.StatusBadRequest, errors.New("missing state or code")) h.logger.WarnCtx(ctx, "OIDC callback missing state or code")
redirectAuthError(w, r, authErrorInvalidState)
return return
} }
@@ -145,6 +147,13 @@ func (h *OIDCHandler) CallbackHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
if _, ok := errors.AsType[*oidc.ErrInvalidState](err); ok {
h.logger.WarnCtx(ctx, "OIDC login rejected: invalid or expired state")
redirectAuthError(w, r, authErrorInvalidState)
return
}
h.logger.ErrorCtx(ctx, "cannot handle OIDC callback", log.Error(err)) h.logger.ErrorCtx(ctx, "cannot handle OIDC callback", log.Error(err))
redirectAuthError(w, r, authErrorAuthenticationFailed) redirectAuthError(w, r, authErrorAuthenticationFailed)