snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
223
selfservice/flow/error.go
Normal file
223
selfservice/flow/error.go
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package flow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/ui/container"
|
||||
"github.com/ory/kratos/ui/node"
|
||||
"github.com/ory/kratos/x/swagger"
|
||||
"github.com/ory/x/clock"
|
||||
"github.com/ory/x/logrusx"
|
||||
"github.com/ory/x/urlx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
"github.com/ory/kratos/text"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStrategyNotResponsible = errors.New("strategy is not responsible for this request")
|
||||
ErrCompletedByStrategy = errors.New("flow response completed by strategy")
|
||||
ErrStrategyAsksToReturnToUI = errors.New("flow strategy is redirecting to the ui")
|
||||
)
|
||||
|
||||
// Is sent when a flow is replaced by a different flow of the same class
|
||||
//
|
||||
// swagger:model errorFlowReplaced
|
||||
//
|
||||
//nolint:deadcode,unused
|
||||
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
|
||||
type errorFlowReplaced struct {
|
||||
Error swagger.GenericError `json:"error"`
|
||||
// The flow ID that should be used for the new flow as it contains the correct messages.
|
||||
FlowID uuid.UUID `json:"use_flow_id"`
|
||||
}
|
||||
|
||||
// ReplacedError is sent when a flow is replaced by a different flow of the same class
|
||||
type ReplacedError struct {
|
||||
*herodot.DefaultError `json:"error"`
|
||||
|
||||
// The flow ID that should be used for the new flow as it contains the correct messages.
|
||||
FlowID uuid.UUID `json:"use_flow_id"`
|
||||
|
||||
flow Flow
|
||||
|
||||
// TODO: This error could be enhanced by providing a "flow class" (e.g. "Recovery", "Settings", "Verification", "Login", etc.)
|
||||
}
|
||||
|
||||
func (e *ReplacedError) WithFlow(flow Flow) *ReplacedError {
|
||||
e.FlowID = flow.GetID()
|
||||
e.flow = flow
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *ReplacedError) GetFlow() Flow {
|
||||
return e.flow
|
||||
}
|
||||
|
||||
func (e *ReplacedError) EnhanceJSONError() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func NewFlowReplacedError(message *text.Message) *ReplacedError {
|
||||
return &ReplacedError{
|
||||
DefaultError: nosurfx.ErrGone().WithID(text.ErrIDSelfServiceFlowReplaced).
|
||||
WithError("self-service flow replaced").
|
||||
WithReason(message.Text),
|
||||
}
|
||||
}
|
||||
|
||||
// Is sent when a flow is expired
|
||||
//
|
||||
// swagger:model selfServiceFlowExpiredError
|
||||
//
|
||||
//nolint:deadcode,unused
|
||||
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
|
||||
type selfServiceFlowExpiredError struct {
|
||||
Error swagger.GenericError `json:"error"`
|
||||
|
||||
// When the flow has expired
|
||||
ExpiredAt time.Time `json:"expired_at"`
|
||||
|
||||
// Please use the "expired_at" field instead to have a more accurate result.
|
||||
//
|
||||
// Deprecated: true
|
||||
Since time.Duration `json:"since"`
|
||||
|
||||
// The flow ID that should be used for the new flow as it contains the correct messages.
|
||||
FlowID uuid.UUID `json:"use_flow_id"`
|
||||
}
|
||||
|
||||
// ExpiredError is sent when a flow is expired
|
||||
type ExpiredError struct {
|
||||
*herodot.DefaultError `json:"error"`
|
||||
|
||||
// When the flow has expired
|
||||
ExpiredAt time.Time `json:"expired_at"`
|
||||
|
||||
// DEPRECATED: Please use the "expired_at" field instead to have a more accurate result.
|
||||
Since time.Duration `json:"since"`
|
||||
|
||||
// The flow ID that should be used for the new flow as it contains the correct messages.
|
||||
FlowID uuid.UUID `json:"use_flow_id"`
|
||||
|
||||
flow Flow
|
||||
}
|
||||
|
||||
func (e *ExpiredError) Unwrap() error {
|
||||
return e.DefaultError
|
||||
}
|
||||
|
||||
func (e *ExpiredError) WithContinueWith(continueWith ...ContinueWith) *ExpiredError {
|
||||
e.DefaultError = ErrorWithContinueWith(e.DefaultError, continueWith...)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *ExpiredError) WithFlow(flow Flow) *ExpiredError {
|
||||
e.FlowID = flow.GetID()
|
||||
e.flow = flow
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *ExpiredError) GetFlow() Flow {
|
||||
return e.flow
|
||||
}
|
||||
|
||||
func (e *ExpiredError) EnhanceJSONError() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func NewFlowExpiredError(c clock.Clock, at time.Time) *ExpiredError {
|
||||
ago := c.Now().Sub(at)
|
||||
return &ExpiredError{
|
||||
ExpiredAt: at.UTC(),
|
||||
Since: ago,
|
||||
DefaultError: nosurfx.ErrGone().WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
WithError("self-service flow expired").
|
||||
WithReasonf("The self-service flow expired %.2f minutes ago, initialize a new one.", ago.Minutes()),
|
||||
}
|
||||
}
|
||||
|
||||
// Is sent when a flow requires a browser to change its location.
|
||||
//
|
||||
// swagger:model errorBrowserLocationChangeRequired
|
||||
//
|
||||
//nolint:deadcode,unused
|
||||
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
|
||||
type errorBrowserLocationChangeRequired struct {
|
||||
Error swagger.ErrorGeneric `json:"error"`
|
||||
|
||||
// Points to where to redirect the user to next.
|
||||
RedirectBrowserTo string `json:"redirect_browser_to"`
|
||||
}
|
||||
|
||||
// BrowserLocationChangeRequiredError is sent when a flow requires a browser to change its location.
|
||||
type BrowserLocationChangeRequiredError struct {
|
||||
*herodot.DefaultError `json:"error"`
|
||||
|
||||
// Points to where to redirect the user to next.
|
||||
RedirectBrowserTo string `json:"redirect_browser_to"`
|
||||
}
|
||||
|
||||
func (e *BrowserLocationChangeRequiredError) EnhanceJSONError() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func NewBrowserLocationChangeRequiredError(redirectTo string) *BrowserLocationChangeRequiredError {
|
||||
return &BrowserLocationChangeRequiredError{
|
||||
RedirectBrowserTo: redirectTo,
|
||||
DefaultError: &herodot.DefaultError{
|
||||
IDField: text.ErrIDSelfServiceBrowserLocationChangeRequiredError,
|
||||
CodeField: http.StatusUnprocessableEntity,
|
||||
StatusField: http.StatusText(http.StatusUnprocessableEntity),
|
||||
ReasonField: fmt.Sprintf("In order to complete this flow please redirect the browser to: %s", redirectTo),
|
||||
DebugField: "",
|
||||
ErrorField: "browser location change required",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func HandleHookError(_ http.ResponseWriter, r *http.Request, f Flow, traits identity.Traits, group node.UiNodeGroup, flowError error, logger logrusx.Provider, csrf nosurfx.CSRFTokenGeneratorProvider) error {
|
||||
if f != nil {
|
||||
if traits != nil {
|
||||
cont, err := container.NewFromStruct("", group, traits, "traits")
|
||||
if err != nil {
|
||||
logger.Logger().WithError(err).Error("could not update flow UI")
|
||||
return err
|
||||
}
|
||||
|
||||
for _, n := range cont.Nodes {
|
||||
// we only set the value and not the whole field because we want to keep types from the initial form generation
|
||||
f.GetUI().Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())
|
||||
}
|
||||
}
|
||||
|
||||
if f.GetType() == TypeBrowser {
|
||||
f.GetUI().SetCSRF(csrf.GenerateCSRFToken(r))
|
||||
}
|
||||
}
|
||||
|
||||
return flowError
|
||||
}
|
||||
|
||||
func GetFlowExpiredRedirectURL(ctx context.Context, config *config.Config, route, returnTo string) *url.URL {
|
||||
redirectURL := urlx.AppendPaths(config.SelfPublicURL(ctx), route)
|
||||
if returnTo != "" {
|
||||
redirectURL = urlx.CopyWithQuery(redirectURL, url.Values{"return_to": {returnTo}})
|
||||
}
|
||||
|
||||
return redirectURL
|
||||
}
|
||||
Reference in New Issue
Block a user