Preserve continue URL on auth error re-login
Failed OIDC, magic-link, and SAML sign-ins sent users to /auth/error without the post-login destination, so Sign in dropped OAuth flows and deep links. Propagate a validated continue query through auth error redirects, recover it from OIDC state when the IdP denies or cancels login, and forward it from AuthErrorPage to /auth/login. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
committed by
Bryan Frimin
parent
428d28fade
commit
9abea50507
@@ -381,6 +381,53 @@ func (s *Service) InitiateLogin(
|
||||
return authURL, nil
|
||||
}
|
||||
|
||||
// ContinueURLFromCallbackState loads and removes a pending login state and returns
|
||||
// its continue URL. Used when the IdP callback fails before an authorization code
|
||||
// is exchanged (user denied consent, cancelled login, etc.).
|
||||
func (s *Service) ContinueURLFromCallbackState(
|
||||
ctx context.Context,
|
||||
provider coredata.OIDCProvider,
|
||||
stateParam string,
|
||||
) (string, error) {
|
||||
if stateParam == "" {
|
||||
return "", NewInvalidStateError()
|
||||
}
|
||||
|
||||
var oidcState coredata.OIDCState
|
||||
|
||||
err := s.pg.WithTx(
|
||||
ctx,
|
||||
func(ctx context.Context, tx pg.Tx) error {
|
||||
if err := oidcState.LoadByIDForUpdate(ctx, tx, stateParam); err != nil {
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return NewInvalidStateError()
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load oidc state: %w", err)
|
||||
}
|
||||
|
||||
if err := oidcState.Delete(ctx, tx); err != nil {
|
||||
return fmt.Errorf("cannot delete oidc state: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if time.Now().After(oidcState.ExpiresAt) {
|
||||
return "", NewInvalidStateError()
|
||||
}
|
||||
|
||||
if oidcState.Provider != provider {
|
||||
return "", NewInvalidStateError()
|
||||
}
|
||||
|
||||
return oidcState.ContinueURL, nil
|
||||
}
|
||||
|
||||
func (s *Service) HandleCallback(
|
||||
ctx context.Context,
|
||||
provider coredata.OIDCProvider,
|
||||
@@ -417,11 +464,11 @@ func (s *Service) HandleCallback(
|
||||
}
|
||||
|
||||
if time.Now().After(oidcState.ExpiresAt) {
|
||||
return nil, "", nil, NewInvalidStateError()
|
||||
return nil, oidcState.ContinueURL, nil, NewInvalidStateError()
|
||||
}
|
||||
|
||||
if oidcState.Provider != provider {
|
||||
return nil, "", nil, NewInvalidStateError()
|
||||
return nil, oidcState.ContinueURL, nil, NewInvalidStateError()
|
||||
}
|
||||
|
||||
token, err := info.oauth2Config.Exchange(
|
||||
@@ -430,26 +477,26 @@ func (s *Service) HandleCallback(
|
||||
oauth2.SetAuthURLParam("code_verifier", oidcState.CodeVerifier),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, "", nil, NewCodeExchangeError(err)
|
||||
return nil, oidcState.ContinueURL, nil, NewCodeExchangeError(err)
|
||||
}
|
||||
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return nil, "", nil, NewIDTokenMissingError()
|
||||
return nil, oidcState.ContinueURL, nil, NewIDTokenMissingError()
|
||||
}
|
||||
|
||||
claims, err := s.verifyAndParseIDToken(ctx, info, rawIDToken, oidcState.Nonce)
|
||||
if err != nil {
|
||||
return nil, "", nil, fmt.Errorf("cannot verify id token: %w", err)
|
||||
return nil, oidcState.ContinueURL, nil, fmt.Errorf("cannot verify id token: %w", err)
|
||||
}
|
||||
|
||||
if err := validateIDTokenClaims(info, claims); err != nil {
|
||||
return nil, "", nil, err
|
||||
return nil, oidcState.ContinueURL, nil, err
|
||||
}
|
||||
|
||||
email, err := mail.ParseAddr(claims.Email)
|
||||
if err != nil {
|
||||
return nil, "", nil, fmt.Errorf("cannot parse email from id token: %w", err)
|
||||
return nil, oidcState.ContinueURL, nil, fmt.Errorf("cannot parse email from id token: %w", err)
|
||||
}
|
||||
|
||||
var identity *coredata.Identity
|
||||
@@ -496,7 +543,7 @@ func (s *Service) HandleCallback(
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
return nil, oidcState.ContinueURL, nil, err
|
||||
}
|
||||
|
||||
return identity, oidcState.ContinueURL, oidcState.OrganizationID, nil
|
||||
|
||||
Reference in New Issue
Block a user