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

This commit is contained in:
upstream-mirror
2026-09-15 21:19:12 +05:00
commit c60cc7ca75
8092 changed files with 394160 additions and 0 deletions

223
selfservice/flow/error.go Normal file
View 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
}