diff --git a/internal/testhelpers/http.go b/internal/testhelpers/http.go index 5523de9d5368..f46c1cc62968 100644 --- a/internal/testhelpers/http.go +++ b/internal/testhelpers/http.go @@ -20,26 +20,34 @@ func NewDebugClient(t *testing.T) *http.Client { return &http.Client{Transport: NewTransportWithLogger(http.DefaultTransport, t)} } -func NewClientWithCookieJar(t *testing.T, jar *cookiejar.Jar, debugRedirects bool) *http.Client { +func NewClientWithCookieJar(t *testing.T, jar *cookiejar.Jar, checkRedirect CheckRedirectFunc) *http.Client { if jar == nil { j, err := cookiejar.New(nil) jar = j require.NoError(t, err) } + if checkRedirect == nil { + checkRedirect = DebugRedirects(t) + } return &http.Client{ - Jar: jar, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - if debugRedirects { - t.Logf("Redirect: %s", req.URL.String()) - } - if len(via) >= 20 { - for k, v := range via { - t.Logf("Failed with redirect (%d): %s", k, v.URL.String()) - } - return errors.New("stopped after 20 redirects") + Jar: jar, + CheckRedirect: checkRedirect, + } +} + +type CheckRedirectFunc func(req *http.Request, via []*http.Request) error + +func DebugRedirects(t *testing.T) CheckRedirectFunc { + return func(req *http.Request, via []*http.Request) error { + t.Logf("Redirect: %s", req.URL.String()) + + if len(via) >= 20 { + for k, v := range via { + t.Logf("Failed with redirect (%d): %s", k, v.URL.String()) } - return nil - }, + return errors.New("stopped after 20 redirects") + } + return nil } } diff --git a/selfservice/strategy/code/strategy_login.go b/selfservice/strategy/code/strategy_login.go index cb734b1bf4a1..63a143ef9596 100644 --- a/selfservice/strategy/code/strategy_login.go +++ b/selfservice/strategy/code/strategy_login.go @@ -231,7 +231,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } return nil, nil case flow.StateEmailSent: - i, err := s.loginVerifyCode(ctx, r, f, &p, sess) + i, err := s.loginVerifyCode(ctx, f, &p, sess) if err != nil { return nil, s.HandleLoginError(r, f, &p, err) } @@ -437,7 +437,7 @@ func maybeNormalizeEmail(input string) string { return input } -func (s *Strategy) loginVerifyCode(ctx context.Context, r *http.Request, f *login.Flow, p *updateLoginFlowWithCodeMethod, sess *session.Session) (_ *identity.Identity, err error) { +func (s *Strategy) loginVerifyCode(ctx context.Context, f *login.Flow, p *updateLoginFlowWithCodeMethod, sess *session.Session) (_ *identity.Identity, err error) { ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.strategy.loginVerifyCode") defer otelx.End(span, &err) diff --git a/selfservice/strategy/oidc/provider_facebook.go b/selfservice/strategy/oidc/provider_facebook.go index abf9806cce05..8bbca9b24e83 100644 --- a/selfservice/strategy/oidc/provider_facebook.go +++ b/selfservice/strategy/oidc/provider_facebook.go @@ -41,9 +41,9 @@ func NewProviderFacebook( } } -func (g *ProviderFacebook) generateAppSecretProof(ctx context.Context, exchange *oauth2.Token) string { +func (g *ProviderFacebook) generateAppSecretProof(token *oauth2.Token) string { secret := g.config.ClientSecret - data := exchange.AccessToken + data := token.AccessToken h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(data)) @@ -62,19 +62,19 @@ func (g *ProviderFacebook) OAuth2(ctx context.Context) (*oauth2.Config, error) { return g.oauth2ConfigFromEndpoint(ctx, endpoint), nil } -func (g *ProviderFacebook) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) { +func (g *ProviderFacebook) Claims(ctx context.Context, token *oauth2.Token, query url.Values) (*Claims, error) { o, err := g.OAuth2(ctx) if err != nil { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err)) } - appSecretProof := g.generateAppSecretProof(ctx, exchange) + appSecretProof := g.generateAppSecretProof(token) u, err := url.Parse(fmt.Sprintf("https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender&appsecret_proof=%s", appSecretProof)) if err != nil { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err)) } - ctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, exchange) + ctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, token) req, err := retryablehttp.NewRequestWithContext(ctx, "GET", u.String(), nil) if err != nil { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err)) diff --git a/selfservice/strategy/oidc/provider_test.go b/selfservice/strategy/oidc/provider_test.go index a5733d2e95f8..208421ad2ab0 100644 --- a/selfservice/strategy/oidc/provider_test.go +++ b/selfservice/strategy/oidc/provider_test.go @@ -32,13 +32,13 @@ func NewTestProvider(c *Configuration, reg Dependencies) Provider { } } -func RegisterTestProvider(id string) func() { +func RegisterTestProvider(t *testing.T, id string) { supportedProviders[id] = func(c *Configuration, reg Dependencies) Provider { return NewTestProvider(c, reg) } - return func() { + t.Cleanup(func() { delete(supportedProviders, id) - } + }) } var _ IDTokenVerifier = new(TestProvider) diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index 14c2c26f8e50..f940c06d433b 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -66,6 +66,7 @@ const ( RouteAuth = RouteBase + "/auth/:flow" RouteCallback = RouteBase + "/callback/:provider" + RouteCallbackGeneric = RouteBase + "/callback" RouteOrganizationCallback = RouteBase + "/organization/:organization/callback/:provider" ) @@ -403,22 +404,22 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt req, cntnr, err := s.ValidateCallback(w, r) if err != nil { if req != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) } else { - s.d.SelfServiceErrorManager().Forward(ctx, w, r, s.handleError(ctx, w, r, nil, pid, nil, err)) + s.d.SelfServiceErrorManager().Forward(ctx, w, r, s.handleError(w, r, nil, pid, nil, err)) } return } if authenticated, err := s.alreadyAuthenticated(w, r, req); err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) } else if authenticated { return } provider, err := s.provider(r.Context(), pid) if err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) return } @@ -428,37 +429,37 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt case OAuth2Provider: token, err := s.ExchangeCode(r.Context(), provider, code) if err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) return } et, err = s.encryptOAuth2Tokens(r.Context(), token) if err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) return } claims, err = p.Claims(r.Context(), token, r.URL.Query()) if err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) return } case OAuth1Provider: token, err := p.ExchangeToken(r.Context(), r) if err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) return } claims, err = p.Claims(r.Context(), token) if err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) return } } if err = claims.Validate(); err != nil { - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, err)) + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, err)) return } @@ -482,7 +483,7 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt case *registration.Flow: a.Active = s.ID() a.TransientPayload = cntnr.TransientPayload - if ff, err := s.processRegistration(ctx, w, r, a, et, claims, provider, cntnr, ""); err != nil { + if ff, err := s.processRegistration(ctx, w, r, a, et, claims, provider, cntnr); err != nil { if ff != nil { s.forwardError(w, r, ff, err) return @@ -495,16 +496,16 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt a.TransientPayload = cntnr.TransientPayload sess, err := s.d.SessionManager().FetchFromRequest(r.Context(), r) if err != nil { - s.forwardError(w, r, a, s.handleError(ctx, w, r, a, pid, nil, err)) + s.forwardError(w, r, a, s.handleError(w, r, a, pid, nil, err)) return } if err := s.linkProvider(w, r, &settings.UpdateContext{Session: sess, Flow: a}, et, claims, provider); err != nil { - s.forwardError(w, r, a, s.handleError(ctx, w, r, a, pid, nil, err)) + s.forwardError(w, r, a, s.handleError(w, r, a, pid, nil, err)) return } return default: - s.forwardError(w, r, req, s.handleError(ctx, w, r, req, pid, nil, errors.WithStack(x.PseudoPanic. + s.forwardError(w, r, req, s.handleError(w, r, req, pid, nil, errors.WithStack(x.PseudoPanic. WithDetailf("cause", "Unexpected type in OpenID Connect flow: %T", a)))) return } @@ -588,7 +589,7 @@ func (s *Strategy) forwardError(w http.ResponseWriter, r *http.Request, f flow.F } } -func (s *Strategy) handleError(ctx context.Context, w http.ResponseWriter, r *http.Request, f flow.Flow, usedProviderID string, traits []byte, err error) error { +func (s *Strategy) handleError(w http.ResponseWriter, r *http.Request, f flow.Flow, usedProviderID string, traits []byte, err error) error { switch rf := f.(type) { case *login.Flow: return err @@ -608,7 +609,7 @@ func (s *Strategy) handleError(ctx context.Context, w http.ResponseWriter, r *ht rf.UI.Messages.Add(text.NewErrorValidationDuplicateCredentialsOnOIDCLink()) } - lf, err := s.registrationToLogin(w, r, rf, usedProviderID) + lf, err := s.registrationToLogin(w, r, rf) if err != nil { return err } @@ -741,7 +742,7 @@ func (s *Strategy) CompletedAuthenticationMethod(ctx context.Context) session.Au } } -func (s *Strategy) processIDToken(w http.ResponseWriter, r *http.Request, provider Provider, idToken, idTokenNonce string) (*Claims, error) { +func (s *Strategy) processIDToken(r *http.Request, provider Provider, idToken, idTokenNonce string) (*Claims, error) { verifier, ok := provider.(IDTokenVerifier) if !ok { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The provider %s does not support id_token verification", provider.Config().Provider)) diff --git a/selfservice/strategy/oidc/strategy_helper_test.go b/selfservice/strategy/oidc/strategy_helper_test.go index 7b39729cc6af..cee924ee5d91 100644 --- a/selfservice/strategy/oidc/strategy_helper_test.go +++ b/selfservice/strategy/oidc/strategy_helper_test.go @@ -76,19 +76,43 @@ func (token *idTokenClaims) MarshalJSON() ([]byte, error) { }) } -func createClient(t *testing.T, remote string, redir string) (id, secret string) { +func createClient(t *testing.T, remote string, redir []string) (id, secret string) { require.NoError(t, resilience.Retry(logrusx.New("", ""), time.Second*10, time.Minute*2, func() error { var b bytes.Buffer require.NoError(t, json.NewEncoder(&b).Encode(&struct { - Scope string `json:"scope"` - GrantTypes []string `json:"grant_types"` - ResponseTypes []string `json:"response_types"` - RedirectURIs []string `json:"redirect_uris"` + Scope string `json:"scope"` + GrantTypes []string `json:"grant_types"` + ResponseTypes []string `json:"response_types"` + RedirectURIs []string `json:"redirect_uris"` + TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"` }{ GrantTypes: []string{"authorization_code", "refresh_token"}, ResponseTypes: []string{"code"}, Scope: "offline offline_access openid", - RedirectURIs: []string{redir}, + RedirectURIs: redir, + + // This is a workaround to prevent golang.org/x/oauth2 from + // swallowing the actual error messages from failed token exchanges. + // + // The library first attempts to use the Authorization header to + // pass Client ID+secret during token exchange (client_secret_basic + // in Hydra terminology). If that fails (with any error), it tries + // again with the Client ID+secret passed in the HTTP POST body + // (client_secret_post in Hydra). If that also fails, this second + // error is returned. + // + // Now, if the the client was indeed configured to use + // client_secret_basic, but the token exchange fails for another + // reason, the error message will be swallowed and replaced with + // "invalid_client". + // + // Manually setting this to client_secret_post means that during + // tests, all token exchanges will first fail with `invalid_client` + // and then be retried with the correct method. This is the only way + // to get the actual error message from the server, however. + // + // https://github.com/golang/oauth2/blob/5fd42413edb3b1699004a31b72e485e0e4ba1b13/internal/token.go#L227-L242 + TokenEndpointAuthMethod: "client_secret_post", })) res, err := http.Post(remote+"/admin/clients", "application/json", &b) @@ -317,7 +341,7 @@ func newOIDCProvider( id string, opts ...func(*oidc.Configuration), ) oidc.Configuration { - clientID, secret := createClient(t, hydraAdmin, kratos.URL+oidc.RouteBase+"/callback/"+id) + clientID, secret := createClient(t, hydraAdmin, []string{kratos.URL + oidc.RouteBase + "/callback/" + id, kratos.URL + oidc.RouteCallbackGeneric}) cfg := oidc.Configuration{ Provider: "generic", diff --git a/selfservice/strategy/oidc/strategy_login.go b/selfservice/strategy/oidc/strategy_login.go index 8ebe1f1c8dfb..ffa643742ec3 100644 --- a/selfservice/strategy/oidc/strategy_login.go +++ b/selfservice/strategy/oidc/strategy_login.go @@ -132,12 +132,12 @@ func (s *Strategy) processLogin(ctx context.Context, w http.ResponseWriter, r *h registrationFlow, err := s.d.RegistrationHandler().NewRegistrationFlow(w, r, loginFlow.Type, opts...) if err != nil { - return nil, s.handleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, loginFlow, provider.Config().ID, nil, err) } err = s.d.SessionTokenExchangePersister().MoveToNewFlow(ctx, loginFlow.ID, registrationFlow.ID) if err != nil { - return nil, s.handleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, loginFlow, provider.Config().ID, nil, err) } registrationFlow.OrganizationID = loginFlow.OrganizationID @@ -148,22 +148,22 @@ func (s *Strategy) processLogin(ctx context.Context, w http.ResponseWriter, r *h registrationFlow.Active = s.ID() if err != nil { - return nil, s.handleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, loginFlow, provider.Config().ID, nil, err) } - if _, err := s.processRegistration(ctx, w, r, registrationFlow, token, claims, provider, container, loginFlow.IDToken); err != nil { + if _, err := s.processRegistration(ctx, w, r, registrationFlow, token, claims, provider, container); err != nil { return registrationFlow, err } return nil, nil } - return nil, s.handleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, loginFlow, provider.Config().ID, nil, err) } var oidcCredentials identity.CredentialsOIDC if err := json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&oidcCredentials); err != nil { - return nil, s.handleError(ctx, w, r, loginFlow, provider.Config().ID, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("The password credentials could not be decoded properly").WithDebug(err.Error()))) + return nil, s.handleError(w, r, loginFlow, provider.Config().ID, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("The password credentials could not be decoded properly").WithDebug(err.Error()))) } sess := session.NewInactiveSession() @@ -171,13 +171,13 @@ func (s *Strategy) processLogin(ctx context.Context, w http.ResponseWriter, r *h for _, c := range oidcCredentials.Providers { if c.Subject == claims.Subject && c.Provider == provider.Config().ID { if err = s.d.LoginHookExecutor().PostLoginHook(w, r, node.OpenIDConnectGroup, loginFlow, i, sess, provider.Config().ID); err != nil { - return nil, s.handleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, loginFlow, provider.Config().ID, nil, err) } return nil, nil } } - return nil, s.handleError(ctx, w, r, loginFlow, provider.Config().ID, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("Unable to find matching OpenID Connect Credentials.").WithDebugf(`Unable to find credentials that match the given provider "%s" and subject "%s".`, provider.Config().ID, claims.Subject))) + return nil, s.handleError(w, r, loginFlow, provider.Config().ID, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("Unable to find matching OpenID Connect Credentials.").WithDebugf(`Unable to find credentials that match the given provider "%s" and subject "%s".`, provider.Config().ID, claims.Subject))) } func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, _ *session.Session) (i *identity.Identity, err error) { @@ -190,7 +190,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, var p UpdateLoginFlowWithOidcMethod if err := s.newLinkDecoder(&p, r); err != nil { - return nil, s.handleError(ctx, w, r, f, "", nil, err) + return nil, s.handleError(w, r, f, "", nil, err) } f.IDToken = p.IDToken @@ -213,36 +213,36 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } provider, err := s.provider(ctx, pid) if err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } req, err := s.validateFlow(ctx, r, f.ID) if err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } if authenticated, err := s.alreadyAuthenticated(w, r, req); err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } else if authenticated { return i, nil } if p.IDToken != "" { - claims, err := s.processIDToken(w, r, provider, p.IDToken, p.IDTokenNonce) + claims, err := s.processIDToken(r, provider, p.IDToken, p.IDTokenNonce) if err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } _, err = s.processLogin(ctx, w, r, f, nil, claims, provider, &AuthCodeContainer{ FlowID: f.ID.String(), Traits: p.Traits, }) if err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } return nil, errors.WithStack(flow.ErrCompletedByStrategy) } @@ -259,12 +259,12 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, TransientPayload: f.TransientPayload, }), continuity.WithLifespan(time.Minute*30)); err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } f.Active = s.ID() if err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("Could not update flow").WithDebug(err.Error()))) + return nil, s.handleError(w, r, f, pid, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("Could not update flow").WithDebug(err.Error()))) } var up map[string]string @@ -274,7 +274,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, codeURL, err := getAuthRedirectURL(ctx, provider, f, state, up) if err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(w, r, f, pid, nil, err) } if x.IsJSONRequest(r) { diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index 982feeed7723..765baafbe902 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -154,7 +154,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat var p UpdateRegistrationFlowWithOidcMethod if err := s.newLinkDecoder(&p, r); err != nil { - return s.handleError(ctx, w, r, f, "", nil, err) + return s.handleError(w, r, f, "", nil, err) } pid := p.Provider // this can come from both url query and post body @@ -177,37 +177,37 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat } if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } provider, err := s.provider(ctx, pid) if err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } req, err := s.validateFlow(ctx, r, f.ID) if err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } if authenticated, err := s.alreadyAuthenticated(w, r, req); err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } else if authenticated { return errors.WithStack(registration.ErrAlreadyLoggedIn) } if p.IDToken != "" { - claims, err := s.processIDToken(w, r, provider, p.IDToken, p.IDTokenNonce) + claims, err := s.processIDToken(r, provider, p.IDToken, p.IDTokenNonce) if err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } _, err = s.processRegistration(ctx, w, r, f, nil, claims, provider, &AuthCodeContainer{ FlowID: f.ID.String(), Traits: p.Traits, TransientPayload: f.TransientPayload, - }, p.IDToken) + }) if err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } return errors.WithStack(flow.ErrCompletedByStrategy) } @@ -224,7 +224,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat TransientPayload: f.TransientPayload, }), continuity.WithLifespan(time.Minute*30)); err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } var up map[string]string @@ -234,7 +234,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat codeURL, err := getAuthRedirectURL(ctx, provider, f, state, up) if err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(w, r, f, pid, nil, err) } if x.IsJSONRequest(r) { s.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(codeURL)) @@ -245,7 +245,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat return errors.WithStack(flow.ErrCompletedByStrategy) } -func (s *Strategy) registrationToLogin(w http.ResponseWriter, r *http.Request, rf *registration.Flow, providerID string) (*login.Flow, error) { +func (s *Strategy) registrationToLogin(w http.ResponseWriter, r *http.Request, rf *registration.Flow) (*login.Flow, error) { // If return_to was set before, we need to preserve it. var opts []login.FlowOption if len(rf.ReturnTo) > 0 { @@ -278,7 +278,7 @@ func (s *Strategy) registrationToLogin(w http.ResponseWriter, r *http.Request, r return lf, nil } -func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWriter, r *http.Request, rf *registration.Flow, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider, container *AuthCodeContainer, idToken string) (_ *login.Flow, err error) { +func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWriter, r *http.Request, rf *registration.Flow, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider, container *AuthCodeContainer) (_ *login.Flow, err error) { ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.strategy.processRegistration") defer otelx.End(span, &err) @@ -296,13 +296,13 @@ func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWrite WithField("subject", claims.Subject). Debug("Received successful OpenID Connect callback but user is already registered. Re-initializing login flow now.") - lf, err := s.registrationToLogin(w, r, rf, provider.Config().ID) + lf, err := s.registrationToLogin(w, r, rf) if err != nil { - return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, rf, provider.Config().ID, nil, err) } if _, err := s.processLogin(ctx, w, r, lf, token, claims, provider, container); err != nil { - return lf, s.handleError(ctx, w, r, rf, provider.Config().ID, nil, err) + return lf, s.handleError(w, r, rf, provider.Config().ID, nil, err) } return nil, nil @@ -311,17 +311,17 @@ func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWrite fetch := fetcher.NewFetcher(fetcher.WithClient(s.d.HTTPClient(r.Context())), fetcher.WithCache(jsonnetCache, 60*time.Minute)) jsonnetMapperSnippet, err := fetch.FetchContext(r.Context(), provider.Config().Mapper) if err != nil { - return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, rf, provider.Config().ID, nil, err) } - i, va, err := s.createIdentity(ctx, w, r, rf, claims, provider, container, jsonnetMapperSnippet.Bytes()) + i, va, err := s.createIdentity(w, r, rf, claims, provider, container, jsonnetMapperSnippet.Bytes()) if err != nil { - return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, nil, err) + return nil, s.handleError(w, r, rf, provider.Config().ID, nil, err) } // Validate the identity itself if err := s.d.IdentityValidator().Validate(r.Context(), i); err != nil { - return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err) + return nil, s.handleError(w, r, rf, provider.Config().ID, i.Traits, err) } for n := range i.VerifiableAddresses { @@ -338,50 +338,50 @@ func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWrite creds, err := identity.NewCredentialsOIDC(token, provider.Config().ID, claims.Subject, provider.Config().OrganizationID) if err != nil { - return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err) + return nil, s.handleError(w, r, rf, provider.Config().ID, i.Traits, err) } i.SetCredentials(s.ID(), *creds) if err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, identity.CredentialsTypeOIDC, provider.Config().ID, rf, i); err != nil { - return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err) + return nil, s.handleError(w, r, rf, provider.Config().ID, i.Traits, err) } return nil, nil } -func (s *Strategy) createIdentity(ctx context.Context, w http.ResponseWriter, r *http.Request, a *registration.Flow, claims *Claims, provider Provider, container *AuthCodeContainer, jsonnetSnippet []byte) (*identity.Identity, []VerifiedAddress, error) { +func (s *Strategy) createIdentity(w http.ResponseWriter, r *http.Request, a *registration.Flow, claims *Claims, provider Provider, container *AuthCodeContainer, jsonnetSnippet []byte) (*identity.Identity, []VerifiedAddress, error) { var jsonClaims bytes.Buffer if err := json.NewEncoder(&jsonClaims).Encode(claims); err != nil { - return nil, nil, s.handleError(ctx, w, r, a, provider.Config().ID, nil, err) + return nil, nil, s.handleError(w, r, a, provider.Config().ID, nil, err) } vm, err := s.d.JsonnetVM(r.Context()) if err != nil { - return nil, nil, s.handleError(ctx, w, r, a, provider.Config().ID, nil, err) + return nil, nil, s.handleError(w, r, a, provider.Config().ID, nil, err) } vm.ExtCode("claims", jsonClaims.String()) evaluated, err := vm.EvaluateAnonymousSnippet(provider.Config().Mapper, string(jsonnetSnippet)) if err != nil { - return nil, nil, s.handleError(ctx, w, r, a, provider.Config().ID, nil, err) + return nil, nil, s.handleError(w, r, a, provider.Config().ID, nil, err) } i := identity.NewIdentity(s.d.Config().DefaultIdentityTraitsSchemaID(r.Context())) - if err := s.setTraits(ctx, w, r, a, claims, provider, container, evaluated, i); err != nil { - return nil, nil, s.handleError(ctx, w, r, a, provider.Config().ID, i.Traits, err) + if err := s.setTraits(w, r, a, provider, container, evaluated, i); err != nil { + return nil, nil, s.handleError(w, r, a, provider.Config().ID, i.Traits, err) } if err := s.setMetadata(evaluated, i, PublicMetadata); err != nil { - return nil, nil, s.handleError(ctx, w, r, a, provider.Config().ID, i.Traits, err) + return nil, nil, s.handleError(w, r, a, provider.Config().ID, i.Traits, err) } if err := s.setMetadata(evaluated, i, AdminMetadata); err != nil { - return nil, nil, s.handleError(ctx, w, r, a, provider.Config().ID, i.Traits, err) + return nil, nil, s.handleError(w, r, a, provider.Config().ID, i.Traits, err) } va, err := s.extractVerifiedAddresses(evaluated) if err != nil { - return nil, nil, s.handleError(ctx, w, r, a, provider.Config().ID, i.Traits, err) + return nil, nil, s.handleError(w, r, a, provider.Config().ID, i.Traits, err) } if orgID, err := uuid.FromString(provider.Config().OrganizationID); err == nil { @@ -398,7 +398,7 @@ func (s *Strategy) createIdentity(ctx context.Context, w http.ResponseWriter, r return i, va, nil } -func (s *Strategy) setTraits(ctx context.Context, w http.ResponseWriter, r *http.Request, a *registration.Flow, claims *Claims, provider Provider, container *AuthCodeContainer, evaluated string, i *identity.Identity) error { +func (s *Strategy) setTraits(w http.ResponseWriter, r *http.Request, a *registration.Flow, provider Provider, container *AuthCodeContainer, evaluated string, i *identity.Identity) error { jsonTraits := gjson.Get(evaluated, "identity.traits") if !jsonTraits.IsObject() { return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("OpenID Connect Jsonnet mapper did not return an object for key identity.traits. Please check your Jsonnet code!")) @@ -407,7 +407,7 @@ func (s *Strategy) setTraits(ctx context.Context, w http.ResponseWriter, r *http if container != nil { traits, err := merge(container.Traits, json.RawMessage(jsonTraits.Raw)) if err != nil { - return s.handleError(ctx, w, r, a, provider.Config().ID, nil, err) + return s.handleError(w, r, a, provider.Config().ID, nil, err) } i.Traits = traits diff --git a/selfservice/strategy/oidc/strategy_test.go b/selfservice/strategy/oidc/strategy_test.go index c3c8abd2ccce..1ac026350bce 100644 --- a/selfservice/strategy/oidc/strategy_test.go +++ b/selfservice/strategy/oidc/strategy_test.go @@ -150,9 +150,9 @@ func TestStrategy(t *testing.T) { return ts.URL + login.RouteSubmitFlow + "?flow=" + flowID.String() } - makeRequestWithCookieJar := func(t *testing.T, provider string, action string, fv url.Values, jar *cookiejar.Jar) (*http.Response, []byte) { + makeRequestWithCookieJar := func(t *testing.T, provider string, action string, fv url.Values, jar *cookiejar.Jar, checkRedirect testhelpers.CheckRedirectFunc) (*http.Response, []byte) { fv.Set("provider", provider) - res, err := testhelpers.NewClientWithCookieJar(t, jar, false).PostForm(action, fv) + res, err := testhelpers.NewClientWithCookieJar(t, jar, checkRedirect).PostForm(action, fv) require.NoError(t, err, action) body, err := io.ReadAll(res.Body) @@ -165,12 +165,12 @@ func TestStrategy(t *testing.T) { } makeRequest := func(t *testing.T, provider string, action string, fv url.Values) (*http.Response, []byte) { - return makeRequestWithCookieJar(t, provider, action, fv, nil) + return makeRequestWithCookieJar(t, provider, action, fv, nil, nil) } makeJSONRequest := func(t *testing.T, provider string, action string, fv url.Values) (*http.Response, []byte) { fv.Set("provider", provider) - client := testhelpers.NewClientWithCookieJar(t, nil, false) + client := testhelpers.NewClientWithCookieJar(t, nil, nil) req, err := http.NewRequest("POST", action, strings.NewReader(fv.Encode())) require.NoError(t, err) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") @@ -197,7 +197,7 @@ func TestStrategy(t *testing.T) { var changeLocation flow.BrowserLocationChangeRequiredError require.NoError(t, json.NewDecoder(res.Body).Decode(&changeLocation)) - res, err = testhelpers.NewClientWithCookieJar(t, nil, true).Get(changeLocation.RedirectBrowserTo) + res, err = testhelpers.NewClientWithCookieJar(t, nil, nil).Get(changeLocation.RedirectBrowserTo) require.NoError(t, err) returnToURL = res.Request.URL @@ -239,7 +239,7 @@ func TestStrategy(t *testing.T) { // assert ui error (redirect to login/registration ui endpoint) assertUIError := func(t *testing.T, res *http.Response, body []byte, reason string) { - require.Contains(t, res.Request.URL.String(), uiTS.URL, "status: %d, body: %s", res.StatusCode, body) + require.Contains(t, res.Request.URL.String(), uiTS.URL, "Redirect does not point to UI server. Status: %d, body: %s", res.StatusCode, body) assert.Contains(t, gjson.GetBytes(body, "ui.messages.0.text").String(), reason, "%s", prettyJSON(t, body)) } @@ -569,7 +569,7 @@ func TestStrategy(t *testing.T) { // We essentially run into this bit: // // if authenticated, err := s.alreadyAuthenticated(w, r, req); err != nil { - // s.forwardError(w, r, req, s.handleError(ctx, w, , r, req, pid, nil, err)) + // s.forwardError(w, r, req, s.handleError(w, , r, req, pid, nil, err)) // } else if authenticated { // return <-- we end up here on the second call // } @@ -605,10 +605,7 @@ func TestStrategy(t *testing.T) { require.NoError(t, err) require.NoError(t, res.Body.Close()) - // The reason for `invalid_client` here is that the code was already used and the session was already authenticated. The invalid_client - // happens because of the way Golang's OAuth2 library is trying out different auth methods when a token request fails, which obfuscates - // the underlying error. - assert.Contains(t, string(body), "invalid_client", "%s", body) + assert.Contains(t, string(body), "The authorization code has already been used", "%s", body) }) }) @@ -755,7 +752,7 @@ func TestStrategy(t *testing.T) { Mapper: "file://./stub/oidc.facebook.jsonnet", }, ) - t.Cleanup(oidc.RegisterTestProvider("test-provider")) + oidc.RegisterTestProvider(t, "test-provider") cl := http.Client{} @@ -982,7 +979,7 @@ func TestStrategy(t *testing.T) { action := assertFormValues(t, r.ID, "valid") fv := url.Values{} fv.Set("provider", "valid") - res, err := testhelpers.NewClientWithCookieJar(t, nil, false).PostForm(action, fv) + res, err := testhelpers.NewClientWithCookieJar(t, nil, nil).PostForm(action, fv) require.NoError(t, err) // Expect to be returned to the hydra instance, that instantiated the request assert.Equal(t, hydra.FakePostLoginURL, res.Request.URL.String()) @@ -1118,10 +1115,10 @@ func TestStrategy(t *testing.T) { fv := url.Values{"traits.name": {"valid-name"}} jar, _ := cookiejar.New(nil) r1 := newBrowserLoginFlow(t, returnTS.URL, time.Minute) - res1, body1 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r1.ID, "valid"), fv, jar) + res1, body1 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r1.ID, "valid"), fv, jar, nil) assertIdentity(t, res1, body1) r2 := newBrowserLoginFlow(t, returnTS.URL, time.Minute) - res2, body2 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r2.ID, "valid"), fv, jar) + res2, body2 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r2.ID, "valid"), fv, jar, nil) assertIdentity(t, res2, body2) assert.Equal(t, body1, body2) }) @@ -1133,11 +1130,11 @@ func TestStrategy(t *testing.T) { fv := url.Values{"traits.name": {"valid-name"}} jar, _ := cookiejar.New(nil) r1 := newBrowserLoginFlow(t, returnTS.URL, time.Minute) - res1, body1 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r1.ID, "valid"), fv, jar) + res1, body1 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r1.ID, "valid"), fv, jar, nil) assertIdentity(t, res1, body1) r2 := newBrowserLoginFlow(t, returnTS.URL, time.Minute) require.NoError(t, reg.LoginFlowPersister().ForceLoginFlow(context.Background(), r2.ID)) - res2, body2 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r2.ID, "valid"), fv, jar) + res2, body2 := makeRequestWithCookieJar(t, "valid", assertFormValues(t, r2.ID, "valid"), fv, jar, nil) assertIdentity(t, res2, body2) assert.NotEqual(t, gjson.GetBytes(body1, "id"), gjson.GetBytes(body2, "id")) authAt1, err := time.Parse(time.RFC3339, gjson.GetBytes(body1, "authenticated_at").String()) @@ -1338,7 +1335,7 @@ func TestStrategy(t *testing.T) { require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i2)) }) - client := testhelpers.NewClientWithCookieJar(t, nil, false) + client := testhelpers.NewClientWithCookieJar(t, nil, nil) loginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser) var linkingLoginFlow struct { @@ -1419,7 +1416,7 @@ func TestStrategy(t *testing.T) { }) subject = email1 - client := testhelpers.NewClientWithCookieJar(t, nil, false) + client := testhelpers.NewClientWithCookieJar(t, nil, nil) loginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser) var linkingLoginFlow struct{ ID string } t.Run("step=should fail login and start a new login", func(t *testing.T) { @@ -1551,7 +1548,7 @@ func TestCountActiveFirstFactorCredentials(t *testing.T) { for _, v := range tc.in { in[v.Type] = v } - actual, err := strategy.CountActiveFirstFactorCredentials(nil, in) + actual, err := strategy.CountActiveFirstFactorCredentials(context.Background(), in) require.NoError(t, err) assert.Equal(t, tc.expected, actual) }) diff --git a/selfservice/strategy/password/op_login_test.go b/selfservice/strategy/password/op_login_test.go index 6d8492a2ff3e..1fad1cce1c18 100644 --- a/selfservice/strategy/password/op_login_test.go +++ b/selfservice/strategy/password/op_login_test.go @@ -227,7 +227,7 @@ func TestOAuth2Provider(t *testing.T) { t.Cleanup(func() { conf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true) }) - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier, pwd := x.NewUUID().String(), "password" createIdentity(ctx, reg, t, identifier, pwd) @@ -298,7 +298,7 @@ func TestOAuth2Provider(t *testing.T) { // to SessionPersistentCookie=false, the user is not // remembered from the previous OAuth2 flow. // The user must then re-authenticate and re-consent. - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier, pwd := x.NewUUID().String(), "password" createIdentity(ctx, reg, t, identifier, pwd) @@ -398,7 +398,7 @@ func TestOAuth2Provider(t *testing.T) { // The user must then only re-consent. conf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true) - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier, pwd := x.NewUUID().String(), "password" @@ -488,7 +488,7 @@ func TestOAuth2Provider(t *testing.T) { }) t.Run("should prompt login even with session with OAuth flow", func(t *testing.T) { - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier, pwd := x.NewUUID().String(), "password" createIdentity(ctx, reg, t, identifier, pwd) @@ -559,7 +559,7 @@ func TestOAuth2Provider(t *testing.T) { }) t.Run("first party clients can skip consent", func(t *testing.T) { - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier, pwd := x.NewUUID().String(), "password" createIdentity(ctx, reg, t, identifier, pwd) @@ -628,7 +628,7 @@ func TestOAuth2Provider(t *testing.T) { }) t.Run("oauth flow with consent remember, skips consent", func(t *testing.T) { - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier, pwd := x.NewUUID().String(), "password" createIdentity(ctx, reg, t, identifier, pwd) @@ -719,7 +719,7 @@ func TestOAuth2Provider(t *testing.T) { }) t.Run("should fail when Hydra session subject doesn't match the subject authenticated by Kratos", func(t *testing.T) { - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier, pwd := x.NewUUID().String(), "password" createIdentity(ctx, reg, t, identifier, pwd) diff --git a/selfservice/strategy/password/op_registration_test.go b/selfservice/strategy/password/op_registration_test.go index 7a0f38270b9a..dc2df971b9f9 100644 --- a/selfservice/strategy/password/op_registration_test.go +++ b/selfservice/strategy/password/op_registration_test.go @@ -261,7 +261,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { require.Equal(t, http.StatusOK, res.StatusCode) } - registerNewAccount := func(t *testing.T, ctx context.Context, browserClient *http.Client, identifier, password string) { + registerNewAccount := func(t *testing.T, browserClient *http.Client, identifier, password string) { // we need to create a new session directly with kratos f := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, kratosPublicTS, false, false, false) require.NotNil(t, f) @@ -310,7 +310,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { Scopes: scopes, RedirectURL: clientAppTS.URL, } - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier := x.NewUUID().String() password := x.NewUUID().String() @@ -387,7 +387,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { RedirectURL: clientAppTS.URL, } - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier := x.NewUUID().String() password := x.NewUUID().String() @@ -418,7 +418,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { state: &clientAS, }) - registerNewAccount(t, ctx, browserClient, identifier, password) + registerNewAccount(t, browserClient, identifier, password) require.ElementsMatch(t, []callTrace{ RegistrationUI, @@ -472,7 +472,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { RedirectURL: clientAppTS.URL, } - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier := x.NewUUID().String() password := x.NewUUID().String() @@ -579,7 +579,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { RedirectURL: clientAppTS.URL, } - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier := x.NewUUID().String() password := x.NewUUID().String() @@ -659,7 +659,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { RedirectURL: clientAppTS.URL, } - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) identifier := x.NewUUID().String() password := x.NewUUID().String() @@ -776,7 +776,7 @@ func TestOAuth2ProviderRegistration(t *testing.T) { RedirectURL: clientAppTS.URL, } - browserClient := testhelpers.NewClientWithCookieJar(t, nil, false) + browserClient := testhelpers.NewClientWithCookieJar(t, nil, nil) ct := make([]callTrace, 0) diff --git a/selfservice/strategy/webauthn/login.go b/selfservice/strategy/webauthn/login.go index d3a46ddc5085..e2a0ef2a3145 100644 --- a/selfservice/strategy/webauthn/login.go +++ b/selfservice/strategy/webauthn/login.go @@ -184,7 +184,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, return s.loginPasswordless(ctx, w, r, f, &p) } - return s.loginMultiFactor(ctx, w, r, f, sess.IdentityID, &p) + return s.loginMultiFactor(ctx, r, f, sess.IdentityID, &p) } func (s *Strategy) loginPasswordless(ctx context.Context, w http.ResponseWriter, r *http.Request, f *login.Flow, p *updateLoginFlowWithWebAuthnMethod) (i *identity.Identity, err error) { @@ -300,7 +300,7 @@ func (s *Strategy) loginAuthenticate(ctx context.Context, r *http.Request, f *lo return i, nil } -func (s *Strategy) loginMultiFactor(ctx context.Context, w http.ResponseWriter, r *http.Request, f *login.Flow, identityID uuid.UUID, p *updateLoginFlowWithWebAuthnMethod) (*identity.Identity, error) { +func (s *Strategy) loginMultiFactor(ctx context.Context, r *http.Request, f *login.Flow, identityID uuid.UUID, p *updateLoginFlowWithWebAuthnMethod) (*identity.Identity, error) { if err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2); err != nil { return nil, err }