Skip to content

Commit

Permalink
fix: pass on correct context during verification (#4151)
Browse files Browse the repository at this point in the history
  • Loading branch information
zepatrik authored Oct 9, 2024
1 parent 18056a0 commit 7e0b500
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 41 deletions.
1 change: 1 addition & 0 deletions internal/client-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
72 changes: 36 additions & 36 deletions selfservice/strategy/code/strategy_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (s *Strategy) Verify(w http.ResponseWriter, r *http.Request, f *verificatio

f.TransientPayload = body.TransientPayload

if err := flow.MethodEnabledAndAllowed(r.Context(), f.GetFlowName(), s.VerificationStrategyID(), string(body.getMethod()), s.deps); err != nil {
if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.VerificationStrategyID(), string(body.getMethod()), s.deps); err != nil {
return s.handleVerificationError(r, f, body, err)
}

Expand All @@ -158,15 +158,15 @@ func (s *Strategy) Verify(w http.ResponseWriter, r *http.Request, f *verificatio
case flow.StateChooseMethod:
fallthrough
case flow.StateEmailSent:
return s.verificationHandleFormSubmission(w, r, f, body)
return s.verificationHandleFormSubmission(ctx, w, r, f, body)
case flow.StatePassedChallenge:
return s.retryVerificationFlowWithMessage(w, r, f.Type, text.NewErrorValidationVerificationRetrySuccess())
return s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationRetrySuccess())
default:
return s.retryVerificationFlowWithMessage(w, r, f.Type, text.NewErrorValidationVerificationStateFailure())
return s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationStateFailure())
}
}

func (s *Strategy) handleLinkClick(w http.ResponseWriter, r *http.Request, f *verification.Flow, code string) error {
func (s *Strategy) handleLinkClick(ctx context.Context, w http.ResponseWriter, r *http.Request, f *verification.Flow, code string) error {
// Pre-fill the code
if codeField := f.UI.Nodes.Find("code"); codeField != nil {
codeField.Attributes.SetValue(code)
Expand All @@ -178,41 +178,41 @@ func (s *Strategy) handleLinkClick(w http.ResponseWriter, r *http.Request, f *ve
f.UI.SetCSRF(csrfToken)
f.CSRFToken = csrfToken

if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil {
if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {
return err
}

// we always redirect to the browser UI here to allow API flows to complete aswell
// TODO: In the future, we might want to redirect to a custom URI scheme here, to allow to open an app on the device of
// the user to handle the flow directly.
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther)
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)

return errors.WithStack(flow.ErrCompletedByStrategy)
}

func (s *Strategy) verificationHandleFormSubmission(w http.ResponseWriter, r *http.Request, f *verification.Flow, body *updateVerificationFlowWithCodeMethod) error {
func (s *Strategy) verificationHandleFormSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request, f *verification.Flow, body *updateVerificationFlowWithCodeMethod) error {
if len(body.Code) > 0 {
if r.Method == http.MethodGet {
// Special case: in the code strategy we send out links as well, that contain the code
return s.handleLinkClick(w, r, f, body.Code)
return s.handleLinkClick(ctx, w, r, f, body.Code)
}

// If not GET: try to use the submitted code
return s.verificationUseCode(w, r, body.Code, f)
return s.verificationUseCode(ctx, w, r, body.Code, f)
} else if len(body.Email) == 0 {
// If no code and no email was provided, fail with a validation error
return s.handleVerificationError(r, f, body, schema.NewRequiredError("#/email", "email"))
}

if err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(r.Context()), s.deps.GenerateCSRFToken, body.CSRFToken); err != nil {
if err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(ctx), s.deps.GenerateCSRFToken, body.CSRFToken); err != nil {
return s.handleVerificationError(r, f, body, err)
}

if err := s.deps.VerificationCodePersister().DeleteVerificationCodesOfFlow(r.Context(), f.ID); err != nil {
if err := s.deps.VerificationCodePersister().DeleteVerificationCodesOfFlow(ctx, f.ID); err != nil {
return s.handleVerificationError(r, f, body, err)
}

if err := s.deps.CodeSender().SendVerificationCode(r.Context(), f, identity.VerifiableAddressTypeEmail, body.Email); err != nil {
if err := s.deps.CodeSender().SendVerificationCode(ctx, f, identity.VerifiableAddressTypeEmail, body.Email); err != nil {
if !errors.Is(err, ErrUnknownAddress) {
return s.handleVerificationError(r, f, body, err)
}
Expand All @@ -232,47 +232,47 @@ func (s *Strategy) verificationHandleFormSubmission(w http.ResponseWriter, r *ht
)
}

if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil {
if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {
return s.handleVerificationError(r, f, body, err)
}

return nil
}

func (s *Strategy) verificationUseCode(w http.ResponseWriter, r *http.Request, codeString string, f *verification.Flow) error {
code, err := s.deps.VerificationCodePersister().UseVerificationCode(r.Context(), f.ID, codeString)
func (s *Strategy) verificationUseCode(ctx context.Context, w http.ResponseWriter, r *http.Request, codeString string, f *verification.Flow) error {
code, err := s.deps.VerificationCodePersister().UseVerificationCode(ctx, f.ID, codeString)
if errors.Is(err, ErrCodeNotFound) {
f.UI.Messages.Clear()
f.UI.Messages.Add(text.NewErrorValidationVerificationCodeInvalidOrAlreadyUsed())
if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil {
return s.retryVerificationFlowWithError(w, r, f.Type, err)
if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {
return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)
}

if x.IsBrowserRequest(r) {
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther)
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)
} else {
s.deps.Writer().Write(w, r, f)
}
return errors.WithStack(flow.ErrCompletedByStrategy)
} else if err != nil {
return s.retryVerificationFlowWithError(w, r, f.Type, err)
return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)
}

address := code.VerifiableAddress
address.Verified = true
verifiedAt := sqlxx.NullTime(time.Now().UTC())
address.VerifiedAt = &verifiedAt
address.Status = identity.VerifiableAddressStatusCompleted
if err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), address); err != nil {
return s.retryVerificationFlowWithError(w, r, f.Type, err)
if err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address); err != nil {
return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)
}

i, err := s.deps.IdentityPool().GetIdentity(r.Context(), code.VerifiableAddress.IdentityID, identity.ExpandDefault)
i, err := s.deps.IdentityPool().GetIdentity(ctx, code.VerifiableAddress.IdentityID, identity.ExpandDefault)
if err != nil {
return s.retryVerificationFlowWithError(w, r, f.Type, err)
return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)
}

returnTo := f.ContinueURL(r.Context(), s.deps.Config())
returnTo := f.ContinueURL(ctx, s.deps.Config())

f.UI = &container.Container{
Method: "GET",
Expand All @@ -288,54 +288,54 @@ func (s *Strategy) verificationUseCode(w http.ResponseWriter, r *http.Request, c
Append(node.NewAnchorField("continue", returnTo.String(), node.CodeGroup, text.NewInfoNodeLabelContinue()).
WithMetaLabel(text.NewInfoNodeLabelContinue()))

if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil {
return s.retryVerificationFlowWithError(w, r, flow.TypeBrowser, err)
if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {
return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)
}

if err := s.deps.VerificationExecutor().PostVerificationHook(w, r, f, i); err != nil {
return s.retryVerificationFlowWithError(w, r, f.Type, err)
return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)
}

return nil
}

func (s *Strategy) retryVerificationFlowWithMessage(w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error {
func (s *Strategy) retryVerificationFlowWithMessage(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error {
s.deps.
Logger().
WithRequest(r).
WithField("message", message).
Debug("A verification flow is being retried because a validation error occurred.")

f, err := verification.NewFlow(s.deps.Config(),
s.deps.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft)
s.deps.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft)
if err != nil {
return s.handleVerificationError(r, f, nil, err)
}

f.UI.Messages.Add(message)

if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(r.Context(), f); err != nil {
if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil {
return s.handleVerificationError(r, f, nil, err)
}

if x.IsJSONRequest(r) {
s.deps.Writer().WriteError(w, r, flow.NewFlowReplacedError(text.NewErrorSystemGeneric("An error occured, please use the new flow.")).WithFlow(f))
} else {
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther)
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)
}

return errors.WithStack(flow.ErrCompletedByStrategy)
}

func (s *Strategy) retryVerificationFlowWithError(w http.ResponseWriter, r *http.Request, ft flow.Type, verErr error) error {
func (s *Strategy) retryVerificationFlowWithError(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, verErr error) error {
s.deps.
Logger().
WithRequest(r).
WithError(verErr).
Debug("A verification flow is being retried because an error occurred.")

f, err := verification.NewFlow(s.deps.Config(),
s.deps.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft)
s.deps.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft)
if err != nil {
return s.handleVerificationError(r, f, nil, err)
}
Expand All @@ -349,7 +349,7 @@ func (s *Strategy) retryVerificationFlowWithError(w http.ResponseWriter, r *http
return err
}

if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(r.Context(), f); err != nil {
if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil {
return s.handleVerificationError(r, f, nil, err)
}

Expand All @@ -360,7 +360,7 @@ func (s *Strategy) retryVerificationFlowWithError(w http.ResponseWriter, r *http
}
s.deps.Writer().WriteError(w, r, toReturn)
} else {
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther)
http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)
}

return errors.WithStack(flow.ErrCompletedByStrategy)
Expand Down
10 changes: 5 additions & 5 deletions selfservice/strategy/link/strategy_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (s *Strategy) verificationHandleFormSubmission(ctx context.Context, r *http
f.Active = sqlxx.NullString(s.NodeGroup())
f.State = flow.StateEmailSent
f.UI.Messages.Set(text.NewVerificationEmailSent())
if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil {
if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {
return s.handleVerificationError(r, f, body, err)
}

Expand All @@ -221,16 +221,16 @@ func (s *Strategy) verificationUseToken(ctx context.Context, w http.ResponseWrit
verifiedAt := sqlxx.NullTime(time.Now().UTC())
address.VerifiedAt = &verifiedAt
address.Status = identity.VerifiableAddressStatusCompleted
if err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), address); err != nil {
if err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address); err != nil {
return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)
}

i, err := s.d.IdentityPool().GetIdentity(r.Context(), token.VerifiableAddress.IdentityID, identity.ExpandDefault)
i, err := s.d.IdentityPool().GetIdentity(ctx, token.VerifiableAddress.IdentityID, identity.ExpandDefault)
if err != nil {
return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)
}

returnTo := f.ContinueURL(r.Context(), s.d.Config())
returnTo := f.ContinueURL(ctx, s.d.Config())

f.UI.
Nodes.
Expand All @@ -251,7 +251,7 @@ func (s *Strategy) verificationUseToken(ctx context.Context, w http.ResponseWrit
Append(node.NewAnchorField("continue", returnTo.String(), node.LinkGroup, text.NewInfoNodeLabelContinue()).
WithMetaLabel(text.NewInfoNodeLabelContinue()))

if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil {
if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {
return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)
}

Expand Down

0 comments on commit 7e0b500

Please sign in to comment.