diff --git a/client/internal/auth/pkce_flow.go b/client/internal/auth/pkce_flow.go index 1c8a6d376..ebb0d4f1f 100644 --- a/client/internal/auth/pkce_flow.go +++ b/client/internal/auth/pkce_flow.go @@ -154,9 +154,12 @@ func (p *PKCEAuthorizationFlow) RequestAuthInfo(ctx context.Context) (AuthFlowIn oauth2.SetAuthURLParam("code_challenge", codeChallenge), oauth2.SetAuthURLParam("audience", p.providerConfig.Audience), } + forceAccountPrompt := p.forceAccountPrompt + p.forceAccountPrompt = false + if !p.providerConfig.DisablePromptLogin { switch { - case p.forceAccountPrompt: + case forceAccountPrompt: params = append(params, oauth2.SetAuthURLParam("prompt", "login")) case p.providerConfig.LoginFlag == common.LoginFlagPromptLogin: params = append(params, oauth2.SetAuthURLParam("prompt", "login")) @@ -185,6 +188,9 @@ func (p *PKCEAuthorizationFlow) SetLoginHint(hint string) { // re-authenticate instead of answering from the session it already holds. Used // to retry a login that came back for an account other than the one hinted. // +// The next RequestAuthInfo consumes the flag, so a flow that outlives its retry +// goes back to the configured behaviour instead of re-authenticating forever. +// // DisablePromptLogin still wins: it is set for IdPs that break on prompt=login, // where retrying with it would replace a wrong-account login with one that // cannot complete at all. diff --git a/client/internal/auth/pkce_flow_test.go b/client/internal/auth/pkce_flow_test.go index c487c13df..ccbca10e9 100644 --- a/client/internal/auth/pkce_flow_test.go +++ b/client/internal/auth/pkce_flow_test.go @@ -76,6 +76,32 @@ func TestPromptLogin(t *testing.T) { } } +func TestForceAccountPromptAppliesOnlyToTheRetry(t *testing.T) { + config := PKCEAuthProviderConfig{ + ClientID: "test-client-id", + Audience: "test-audience", + TokenEndpoint: "https://test-token-endpoint.com/token", + Scope: "openid email profile", + AuthorizationEndpoint: "https://test-auth-endpoint.com/authorize", + RedirectURLs: []string{"http://127.0.0.1:33992/"}, + UseIDToken: true, + LoginFlag: mgm.LoginFlagNone, + } + pkce, err := NewPKCEAuthorizationFlow(config) + require.NoError(t, err) + + pkce.ForceAccountPrompt() + + retry, err := pkce.RequestAuthInfo(context.Background()) + require.NoError(t, err) + require.Contains(t, retry.VerificationURIComplete, "prompt=login") + + next, err := pkce.RequestAuthInfo(context.Background()) + require.NoError(t, err) + require.NotContains(t, next.VerificationURIComplete, "prompt=login", + "the forced prompt outlived the retry it was armed for") +} + func TestIsPortInExcludedRange(t *testing.T) { tests := []struct { name string