mirror of https://github.com/ory/kratos
feat: enable JSONNet templating for password migration hook (#4390)
This enables JSONNet body templating for the password migration hook. There is also a significant refactoring of some internals around webhook config handling.
This commit is contained in:
parent
ea4da51f4d
commit
b1628976a0
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/cors"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
@ -97,7 +99,7 @@ func servePublic(ctx context.Context, r driver.Registry, cmd *cobra.Command, slO
|
|||
n.Use(r.PrometheusManager())
|
||||
|
||||
router := x.NewRouterPublic()
|
||||
csrf := x.NewCSRFHandler(router, r)
|
||||
csrf := nosurfx.NewCSRFHandler(router, r)
|
||||
|
||||
// we need to always load the CORS middleware even if it is disabled, to allow hot-enabling CORS
|
||||
n.UseFunc(func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func (c *courier) channels(ctx context.Context, id string) (Channel, error) {
|
|||
}
|
||||
return courierChannel, nil
|
||||
case "http":
|
||||
return newHttpChannel(channel.ID, channel.RequestConfig, c.deps), nil
|
||||
return newHttpChannel(channel.ID, &channel.RequestConfig, c.deps), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown courier channel type: %s", channel.Type)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
|
|
@ -29,7 +32,7 @@ type (
|
|||
handlerDependencies interface {
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
PersistenceProvider
|
||||
config.Provider
|
||||
}
|
||||
|
|
@ -47,8 +50,8 @@ func NewHandler(r handlerDependencies) *Handler {
|
|||
|
||||
func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
||||
h.r.CSRFHandler().IgnoreGlobs(x.AdminPrefix+AdminRouteListMessages, AdminRouteListMessages)
|
||||
public.GET(x.AdminPrefix+AdminRouteListMessages, x.RedirectToAdminRoute(h.r))
|
||||
public.GET(x.AdminPrefix+AdminRouteGetMessage, x.RedirectToAdminRoute(h.r))
|
||||
public.GET(x.AdminPrefix+AdminRouteListMessages, redir.RedirectToAdminRoute(h.r))
|
||||
public.GET(x.AdminPrefix+AdminRouteGetMessage, redir.RedirectToAdminRoute(h.r))
|
||||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@ package courier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/ory/kratos/courier/template"
|
||||
|
|
@ -22,7 +19,7 @@ import (
|
|||
type (
|
||||
httpChannel struct {
|
||||
id string
|
||||
requestConfig json.RawMessage
|
||||
requestConfig *request.Config
|
||||
d channelDependencies
|
||||
}
|
||||
channelDependencies interface {
|
||||
|
|
@ -36,7 +33,7 @@ type (
|
|||
|
||||
var _ Channel = new(httpChannel)
|
||||
|
||||
func newHttpChannel(id string, requestConfig json.RawMessage, d channelDependencies) *httpChannel {
|
||||
func newHttpChannel(id string, requestConfig *request.Config, d channelDependencies) *httpChannel {
|
||||
return &httpChannel{
|
||||
id: id,
|
||||
requestConfig: requestConfig,
|
||||
|
|
@ -96,7 +93,7 @@ func (c *httpChannel) Dispatch(ctx context.Context, msg Message) (err error) {
|
|||
}
|
||||
|
||||
logger := c.d.Logger().
|
||||
WithField("http_server", gjson.GetBytes(c.requestConfig, "url").String()).
|
||||
WithField("http_server", c.requestConfig.URL).
|
||||
WithField("message_id", msg.ID).
|
||||
WithField("message_nid", msg.NID).
|
||||
WithField("message_type", msg.Type).
|
||||
|
|
|
|||
|
|
@ -18,11 +18,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/ory/x/crdbx"
|
||||
"github.com/ory/x/pointerx"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/gofrs/uuid"
|
||||
|
|
@ -30,18 +25,22 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/rs/cors"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
"github.com/ory/jsonschema/v3"
|
||||
"github.com/ory/jsonschema/v3/httploader"
|
||||
"github.com/ory/kratos/embedx"
|
||||
"github.com/ory/kratos/request"
|
||||
"github.com/ory/x/configx"
|
||||
"github.com/ory/x/contextx"
|
||||
"github.com/ory/x/crdbx"
|
||||
"github.com/ory/x/httpx"
|
||||
"github.com/ory/x/jsonschemax"
|
||||
"github.com/ory/x/logrusx"
|
||||
"github.com/ory/x/otelx"
|
||||
"github.com/ory/x/pointerx"
|
||||
"github.com/ory/x/stringsx"
|
||||
"github.com/ory/x/tlsx"
|
||||
"github.com/ory/x/watcherx"
|
||||
|
|
@ -286,11 +285,10 @@ type (
|
|||
PlainText string `json:"plaintext"`
|
||||
}
|
||||
CourierChannel struct {
|
||||
ID string `json:"id" koanf:"id"`
|
||||
Type string `json:"type" koanf:"type"`
|
||||
SMTPConfig *SMTPConfig `json:"smtp_config" koanf:"smtp_config"`
|
||||
RequestConfig json.RawMessage `json:"request_config" koanf:"-"`
|
||||
RequestConfigRaw map[string]any `json:"-" koanf:"request_config"`
|
||||
ID string `json:"id" koanf:"id"`
|
||||
Type string `json:"type" koanf:"type"`
|
||||
SMTPConfig *SMTPConfig `json:"smtp_config" koanf:"smtp_config"`
|
||||
RequestConfig request.Config `json:"request_config" koanf:"request_config"`
|
||||
}
|
||||
SMTPConfig struct {
|
||||
ConnectionURI string `json:"connection_uri" koanf:"connection_uri"`
|
||||
|
|
@ -302,8 +300,8 @@ type (
|
|||
LocalName string `json:"local_name" koanf:"local_name"`
|
||||
}
|
||||
PasswordMigrationHook struct {
|
||||
Enabled bool `json:"enabled" koanf:"enabled"`
|
||||
Config json.RawMessage `json:"config" koanf:"config"`
|
||||
Enabled bool `json:"enabled" koanf:"enabled"`
|
||||
Config request.Config `json:"config" koanf:"config"`
|
||||
}
|
||||
Config struct {
|
||||
l *logrusx.Logger
|
||||
|
|
@ -1238,17 +1236,6 @@ func (p *Config) CourierChannels(ctx context.Context) (ccs []*CourierChannel, _
|
|||
if err := p.GetProvider(ctx).Koanf.Unmarshal(ViperKeyCourierChannels, &ccs); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
if len(ccs) != 0 {
|
||||
for _, c := range ccs {
|
||||
if c.RequestConfigRaw != nil {
|
||||
var err error
|
||||
c.RequestConfig, err = json.Marshal(c.RequestConfigRaw)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load legacy configs
|
||||
channel := CourierChannel{
|
||||
|
|
@ -1260,9 +1247,7 @@ func (p *Config) CourierChannels(ctx context.Context) (ccs []*CourierChannel, _
|
|||
return nil, errors.WithStack(err)
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
channel.RequestConfig, err = json.Marshal(p.GetProvider(ctx).Get(ViperKeyCourierHTTPRequestConfig))
|
||||
if err != nil {
|
||||
if err := p.GetProvider(ctx).Koanf.Unmarshal(ViperKeyCourierHTTPRequestConfig, &channel.RequestConfig); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1687,7 +1672,7 @@ func (p *Config) PasswordMigrationHook(ctx context.Context) *PasswordMigrationHo
|
|||
return hook
|
||||
}
|
||||
|
||||
hook.Config, _ = json.Marshal(p.GetProvider(ctx).Get(ViperKeyPasswordMigrationHook + ".config"))
|
||||
_ = p.GetProvider(ctx).Unmarshal(ViperKeyPasswordMigrationHook+".config", &hook.Config)
|
||||
|
||||
return hook
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
password2 "github.com/ory/kratos/selfservice/strategy/password"
|
||||
"github.com/ory/kratos/session"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/nosurf"
|
||||
"github.com/ory/x/contextx"
|
||||
"github.com/ory/x/dbal"
|
||||
|
|
@ -51,7 +52,7 @@ type Registry interface {
|
|||
WithJsonnetVMProvider(jsonnetsecure.VMProvider) Registry
|
||||
|
||||
WithCSRFHandler(c nosurf.Handler)
|
||||
WithCSRFTokenGenerator(cg x.CSRFToken)
|
||||
WithCSRFTokenGenerator(cg nosurfx.CSRFToken)
|
||||
|
||||
MetricsHandler() *prometheus.Handler
|
||||
HealthHandler(ctx context.Context) *healthx.Handler
|
||||
|
|
@ -70,7 +71,7 @@ type Registry interface {
|
|||
WithConfig(c *config.Config) Registry
|
||||
WithContextualizer(ctxer contextx.Contextualizer) Registry
|
||||
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.HTTPClientProvider
|
||||
|
|
@ -151,7 +152,7 @@ type Registry interface {
|
|||
recovery.HandlerProvider
|
||||
recovery.StrategyProvider
|
||||
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
}
|
||||
|
||||
func NewRegistryFromDSN(ctx context.Context, c *config.Config, l *logrusx.Logger) (Registry, error) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
|
||||
"github.com/ory/kratos/selfservice/strategy/idfirst"
|
||||
|
|
@ -157,7 +159,7 @@ type RegistryDefault struct {
|
|||
buildHash string
|
||||
buildDate string
|
||||
|
||||
csrfTokenGenerator x.CSRFToken
|
||||
csrfTokenGenerator nosurfx.CSRFToken
|
||||
|
||||
jsonnetVMProvider jsonnetsecure.VMProvider
|
||||
jsonnetPool jsonnetsecure.Pool
|
||||
|
|
@ -830,13 +832,13 @@ func (m *RegistryDefault) Ping() error {
|
|||
return m.persister.Ping(context.Background())
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) WithCSRFTokenGenerator(cg x.CSRFToken) {
|
||||
func (m *RegistryDefault) WithCSRFTokenGenerator(cg nosurfx.CSRFToken) {
|
||||
m.csrfTokenGenerator = cg
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) GenerateCSRFToken(r *http.Request) string {
|
||||
if m.csrfTokenGenerator == nil {
|
||||
m.csrfTokenGenerator = x.DefaultCSRFToken
|
||||
m.csrfTokenGenerator = nosurfx.DefaultCSRFToken
|
||||
}
|
||||
return m.csrfTokenGenerator(r)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@
|
|||
package driver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/request"
|
||||
"github.com/ory/kratos/selfservice/hook"
|
||||
)
|
||||
|
||||
|
|
@ -57,35 +63,50 @@ func (m *RegistryDefault) WithExtraHandlers(handlers []NewHandlerRegistrar) {
|
|||
m.extraHandlerFactories = handlers
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) getHooks(credentialsType string, configs []config.SelfServiceHook) (i []interface{}) {
|
||||
func getHooks[T any](m *RegistryDefault, credentialsType string, configs []config.SelfServiceHook) ([]T, error) {
|
||||
hooks := make([]T, 0, len(configs))
|
||||
|
||||
var addSessionIssuer bool
|
||||
allHooksLoop:
|
||||
for _, h := range configs {
|
||||
switch h.Name {
|
||||
case hook.KeySessionIssuer:
|
||||
// The session issuer hook always needs to come last.
|
||||
addSessionIssuer = true
|
||||
case hook.KeySessionDestroyer:
|
||||
i = append(i, m.HookSessionDestroyer())
|
||||
if h, ok := any(m.HookSessionDestroyer()).(T); ok {
|
||||
hooks = append(hooks, h)
|
||||
}
|
||||
case hook.KeyWebHook:
|
||||
i = append(i, hook.NewWebHook(m, h.Config))
|
||||
cfg := request.Config{}
|
||||
if err := json.Unmarshal(h.Config, &cfg); err != nil {
|
||||
m.l.WithError(err).WithField("raw_config", string(h.Config)).Error("failed to unmarshal hook configuration, ignoring hook")
|
||||
return nil, errors.WithStack(fmt.Errorf("failed to unmarshal webhook configuration for %s: %w", credentialsType, err))
|
||||
}
|
||||
if h, ok := any(hook.NewWebHook(m, &cfg)).(T); ok {
|
||||
hooks = append(hooks, h)
|
||||
}
|
||||
case hook.KeyAddressVerifier:
|
||||
i = append(i, m.HookAddressVerifier())
|
||||
if h, ok := any(m.HookAddressVerifier()).(T); ok {
|
||||
hooks = append(hooks, h)
|
||||
}
|
||||
case hook.KeyVerificationUI:
|
||||
i = append(i, m.HookShowVerificationUI())
|
||||
if h, ok := any(m.HookShowVerificationUI()).(T); ok {
|
||||
hooks = append(hooks, h)
|
||||
}
|
||||
case hook.KeyVerifier:
|
||||
i = append(i, m.HookVerifier())
|
||||
if h, ok := any(m.HookVerifier()).(T); ok {
|
||||
hooks = append(hooks, h)
|
||||
}
|
||||
default:
|
||||
var found bool
|
||||
for name, m := range m.injectedSelfserviceHooks {
|
||||
if name == h.Name {
|
||||
i = append(i, m(h))
|
||||
found = true
|
||||
break
|
||||
if h, ok := m(h).(T); ok {
|
||||
hooks = append(hooks, h)
|
||||
}
|
||||
continue allHooksLoop
|
||||
}
|
||||
}
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
m.l.
|
||||
WithField("for", credentialsType).
|
||||
WithField("hook", h.Name).
|
||||
|
|
@ -93,8 +114,10 @@ func (m *RegistryDefault) getHooks(credentialsType string, configs []config.Self
|
|||
}
|
||||
}
|
||||
if addSessionIssuer {
|
||||
i = append(i, m.HookSessionIssuer())
|
||||
if h, ok := any(m.HookSessionIssuer()).(T); ok {
|
||||
hooks = append(hooks, h)
|
||||
}
|
||||
}
|
||||
|
||||
return i
|
||||
return hooks, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,32 +18,22 @@ func (m *RegistryDefault) LoginHookExecutor() *login.HookExecutor {
|
|||
return m.selfserviceLoginExecutor
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PreLoginHooks(ctx context.Context) (b []login.PreHookExecutor) {
|
||||
for _, v := range m.getHooks("", m.Config().SelfServiceFlowLoginBeforeHooks(ctx)) {
|
||||
if hook, ok := v.(login.PreHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
return
|
||||
func (m *RegistryDefault) PreLoginHooks(ctx context.Context) ([]login.PreHookExecutor, error) {
|
||||
return getHooks[login.PreHookExecutor](m, "", m.Config().SelfServiceFlowLoginBeforeHooks(ctx))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PostLoginHooks(ctx context.Context, credentialsType identity.CredentialsType) (b []login.PostHookExecutor) {
|
||||
for _, v := range m.getHooks(string(credentialsType), m.Config().SelfServiceFlowLoginAfterHooks(ctx, string(credentialsType))) {
|
||||
if hook, ok := v.(login.PostHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
func (m *RegistryDefault) PostLoginHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]login.PostHookExecutor, error) {
|
||||
hooks, err := getHooks[login.PostHookExecutor](m, string(credentialsType), m.Config().SelfServiceFlowLoginAfterHooks(ctx, string(credentialsType)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hooks) > 0 {
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
// since we don't want merging hooks defined in a specific strategy and global hooks
|
||||
// global hooks are added only if no strategy specific hooks are defined
|
||||
for _, v := range m.getHooks(config.HookGlobal, m.Config().SelfServiceFlowLoginAfterHooks(ctx, "global")) {
|
||||
if hook, ok := v.(login.PostHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
// since we don't want merging hooks defined in a specific strategy and global hooks
|
||||
// global hooks are added only if no strategy specific hooks are defined
|
||||
return getHooks[login.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowLoginAfterHooks(ctx, config.HookGlobal))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) LoginHandler() *login.Handler {
|
||||
|
|
|
|||
|
|
@ -69,23 +69,12 @@ func (m *RegistryDefault) RecoveryExecutor() *recovery.HookExecutor {
|
|||
return m.selfserviceRecoveryExecutor
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PreRecoveryHooks(ctx context.Context) (b []recovery.PreHookExecutor) {
|
||||
for _, v := range m.getHooks("", m.Config().SelfServiceFlowRecoveryBeforeHooks(ctx)) {
|
||||
if hook, ok := v.(recovery.PreHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
return
|
||||
func (m *RegistryDefault) PreRecoveryHooks(ctx context.Context) ([]recovery.PreHookExecutor, error) {
|
||||
return getHooks[recovery.PreHookExecutor](m, "", m.Config().SelfServiceFlowRecoveryBeforeHooks(ctx))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PostRecoveryHooks(ctx context.Context) (b []recovery.PostHookExecutor) {
|
||||
for _, v := range m.getHooks(config.HookGlobal, m.Config().SelfServiceFlowRecoveryAfterHooks(ctx, config.HookGlobal)) {
|
||||
if hook, ok := v.(recovery.PostHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
func (m *RegistryDefault) PostRecoveryHooks(ctx context.Context) ([]recovery.PostHookExecutor, error) {
|
||||
return getHooks[recovery.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowRecoveryAfterHooks(ctx, config.HookGlobal))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) CodeSender() *code.Sender {
|
||||
|
|
|
|||
|
|
@ -5,59 +5,47 @@ package driver
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/selfservice/flow/registration"
|
||||
)
|
||||
|
||||
func (m *RegistryDefault) PostRegistrationPrePersistHooks(ctx context.Context, credentialsType identity.CredentialsType) (b []registration.PostHookPrePersistExecutor) {
|
||||
func (m *RegistryDefault) PostRegistrationPrePersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]registration.PostHookPrePersistExecutor, error) {
|
||||
hooks, err := getHooks[registration.PostHookPrePersistExecutor](m, string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if credentialsType == identity.CredentialsTypeCodeAuth && m.Config().SelfServiceCodeStrategy(ctx).PasswordlessEnabled {
|
||||
b = append(b, m.HookCodeAddressVerifier())
|
||||
hooks = slices.Insert(hooks, 0, registration.PostHookPrePersistExecutor(m.HookCodeAddressVerifier()))
|
||||
}
|
||||
|
||||
for _, v := range m.getHooks(string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType))) {
|
||||
if hook, ok := v.(registration.PostHookPrePersistExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PostRegistrationPostPersistHooks(ctx context.Context, credentialsType identity.CredentialsType) (b []registration.PostHookPostPersistExecutor) {
|
||||
initialHookCount := 0
|
||||
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
|
||||
b = append(b, m.HookVerifier())
|
||||
initialHookCount = 1
|
||||
func (m *RegistryDefault) PostRegistrationPostPersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]registration.PostHookPostPersistExecutor, error) {
|
||||
hooks, err := getHooks[registration.PostHookPostPersistExecutor](m, string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range m.getHooks(string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType))) {
|
||||
if hook, ok := v.(registration.PostHookPostPersistExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
|
||||
if len(b) == initialHookCount {
|
||||
if len(hooks) == 0 {
|
||||
// since we don't want merging hooks defined in a specific strategy and
|
||||
// global hooks are added only if no strategy specific hooks are defined
|
||||
for _, v := range m.getHooks(config.HookGlobal, m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, config.HookGlobal)) {
|
||||
if hook, ok := v.(registration.PostHookPostPersistExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
hooks, err = getHooks[registration.PostHookPostPersistExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, config.HookGlobal))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
|
||||
hooks = slices.Insert(hooks, 0, registration.PostHookPostPersistExecutor(m.HookVerifier()))
|
||||
}
|
||||
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PreRegistrationHooks(ctx context.Context) (b []registration.PreHookExecutor) {
|
||||
for _, v := range m.getHooks("", m.Config().SelfServiceFlowRegistrationBeforeHooks(ctx)) {
|
||||
if hook, ok := v.(registration.PreHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
return
|
||||
func (m *RegistryDefault) PreRegistrationHooks(ctx context.Context) ([]registration.PreHookExecutor, error) {
|
||||
return getHooks[registration.PreHookExecutor](m, "", m.Config().SelfServiceFlowRegistrationBeforeHooks(ctx))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) RegistrationExecutor() *registration.HookExecutor {
|
||||
|
|
|
|||
|
|
@ -5,53 +5,39 @@ package driver
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/selfservice/flow/settings"
|
||||
)
|
||||
|
||||
func (m *RegistryDefault) PostSettingsPrePersistHooks(ctx context.Context, settingsType string) (b []settings.PostHookPrePersistExecutor) {
|
||||
for _, v := range m.getHooks(settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType)) {
|
||||
if hook, ok := v.(settings.PostHookPrePersistExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
return
|
||||
func (m *RegistryDefault) PostSettingsPrePersistHooks(ctx context.Context, settingsType string) ([]settings.PostHookPrePersistExecutor, error) {
|
||||
return getHooks[settings.PostHookPrePersistExecutor](m, settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PreSettingsHooks(ctx context.Context) (b []settings.PreHookExecutor) {
|
||||
for _, v := range m.getHooks("", m.Config().SelfServiceFlowSettingsBeforeHooks(ctx)) {
|
||||
if hook, ok := v.(settings.PreHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
return
|
||||
func (m *RegistryDefault) PreSettingsHooks(ctx context.Context) ([]settings.PreHookExecutor, error) {
|
||||
return getHooks[settings.PreHookExecutor](m, "", m.Config().SelfServiceFlowSettingsBeforeHooks(ctx))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PostSettingsPostPersistHooks(ctx context.Context, settingsType string) (b []settings.PostHookPostPersistExecutor) {
|
||||
initialHookCount := 0
|
||||
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
|
||||
b = append(b, m.HookVerifier())
|
||||
initialHookCount = 1
|
||||
func (m *RegistryDefault) PostSettingsPostPersistHooks(ctx context.Context, settingsType string) ([]settings.PostHookPostPersistExecutor, error) {
|
||||
hooks, err := getHooks[settings.PostHookPostPersistExecutor](m, settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range m.getHooks(settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType)) {
|
||||
if hook, ok := v.(settings.PostHookPostPersistExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
|
||||
if len(b) == initialHookCount {
|
||||
// since we don't want merging hooks defined in a specific strategy and global hooks
|
||||
if len(hooks) == 0 {
|
||||
// since we don't want merging hooks defined in a specific strategy and
|
||||
// global hooks are added only if no strategy specific hooks are defined
|
||||
for _, v := range m.getHooks(config.HookGlobal, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, config.HookGlobal)) {
|
||||
if hook, ok := v.(settings.PostHookPostPersistExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
hooks, err = getHooks[settings.PostHookPostPersistExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, config.HookGlobal))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
|
||||
hooks = slices.Insert(hooks, 0, settings.PostHookPostPersistExecutor(m.HookVerifier()))
|
||||
}
|
||||
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) SettingsHookExecutor() *settings.HookExecutor {
|
||||
|
|
|
|||
|
|
@ -5,32 +5,28 @@ package driver_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
confighelpers "github.com/ory/kratos/driver/config/testhelpers"
|
||||
|
||||
"github.com/ory/x/contextx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow/recovery"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow/verification"
|
||||
|
||||
"github.com/ory/kratos/driver"
|
||||
"github.com/ory/x/configx"
|
||||
"github.com/ory/x/logrusx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ory/x/configx"
|
||||
"github.com/ory/x/contextx"
|
||||
"github.com/ory/x/logrusx"
|
||||
|
||||
"github.com/ory/kratos/driver"
|
||||
"github.com/ory/kratos/driver/config"
|
||||
confighelpers "github.com/ory/kratos/driver/config/testhelpers"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/internal"
|
||||
"github.com/ory/kratos/request"
|
||||
"github.com/ory/kratos/selfservice/flow/login"
|
||||
"github.com/ory/kratos/selfservice/flow/recovery"
|
||||
"github.com/ory/kratos/selfservice/flow/registration"
|
||||
"github.com/ory/kratos/selfservice/flow/settings"
|
||||
"github.com/ory/kratos/selfservice/flow/verification"
|
||||
"github.com/ory/kratos/selfservice/hook"
|
||||
)
|
||||
|
||||
|
|
@ -49,8 +45,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
expect func(reg *driver.RegistryDefault) []verification.PreHookExecutor
|
||||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []verification.PreHookExecutor { return nil },
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []verification.PreHookExecutor {
|
||||
return []verification.PreHookExecutor{}
|
||||
},
|
||||
},
|
||||
{
|
||||
uc: "Two web_hooks are configured",
|
||||
|
|
@ -62,8 +60,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []verification.PreHookExecutor {
|
||||
return []verification.PreHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -73,11 +71,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PreVerificationHooks(ctx)
|
||||
h, err := reg.PreVerificationHooks(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -89,9 +86,11 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
expect func(reg *driver.RegistryDefault) []verification.PostHookExecutor
|
||||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
prep: func(conf *config.Config) {},
|
||||
expect: func(reg *driver.RegistryDefault) []verification.PostHookExecutor { return nil },
|
||||
uc: "No hooks configured",
|
||||
prep: func(conf *config.Config) {},
|
||||
expect: func(reg *driver.RegistryDefault) []verification.PostHookExecutor {
|
||||
return []verification.PostHookExecutor{}
|
||||
},
|
||||
},
|
||||
{
|
||||
uc: "Multiple web_hooks configured",
|
||||
|
|
@ -103,8 +102,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []verification.PostHookExecutor {
|
||||
return []verification.PostHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -114,11 +113,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PostVerificationHooks(ctx)
|
||||
h, err := reg.PostVerificationHooks(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -133,7 +131,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []recovery.PreHookExecutor { return nil },
|
||||
expect: func(reg *driver.RegistryDefault) []recovery.PreHookExecutor { return []recovery.PreHookExecutor{} },
|
||||
},
|
||||
{
|
||||
uc: "Two web_hooks are configured",
|
||||
|
|
@ -145,8 +143,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []recovery.PreHookExecutor {
|
||||
return []recovery.PreHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -156,11 +154,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PreRecoveryHooks(ctx)
|
||||
h, err := reg.PreRecoveryHooks(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +169,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []recovery.PostHookExecutor { return nil },
|
||||
expect: func(reg *driver.RegistryDefault) []recovery.PostHookExecutor { return []recovery.PostHookExecutor{} },
|
||||
},
|
||||
{
|
||||
uc: "Multiple web_hooks configured",
|
||||
|
|
@ -184,8 +181,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []recovery.PostHookExecutor {
|
||||
return []recovery.PostHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -195,11 +192,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PostRecoveryHooks(ctx)
|
||||
h, err := reg.PostRecoveryHooks(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -215,7 +211,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []registration.PreHookExecutor {
|
||||
return nil
|
||||
return []registration.PreHookExecutor{}
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -228,8 +224,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []registration.PreHookExecutor {
|
||||
return []registration.PreHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -239,11 +235,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PreRegistrationHooks(ctx)
|
||||
h, err := reg.PreRegistrationHooks(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.EqualValues(t, expectedExecutors, h)
|
||||
assert.EqualValues(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -254,8 +249,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
expect func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor
|
||||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor { return nil },
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
|
||||
return []registration.PostHookPostPersistExecutor{}
|
||||
},
|
||||
},
|
||||
{
|
||||
uc: "Only session hook configured for password strategy",
|
||||
|
|
@ -284,7 +281,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
|
||||
return []registration.PostHookPostPersistExecutor{
|
||||
hook.NewVerifier(reg),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"body":"bar","headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{URL: "foo", Method: "POST", TemplateURI: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewSessionIssuer(reg),
|
||||
}
|
||||
},
|
||||
|
|
@ -299,8 +296,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
|
||||
return []registration.PostHookPostPersistExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -319,7 +316,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
|
||||
return []registration.PostHookPostPersistExecutor{
|
||||
hook.NewVerifier(reg),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewSessionIssuer(reg),
|
||||
}
|
||||
},
|
||||
|
|
@ -343,11 +340,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PostRegistrationPostPersistHooks(ctx, identity.CredentialsTypePassword)
|
||||
h, err := reg.PostRegistrationPostPersistHooks(ctx, identity.CredentialsTypePassword)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -362,7 +358,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []login.PreHookExecutor { return nil },
|
||||
expect: func(reg *driver.RegistryDefault) []login.PreHookExecutor { return []login.PreHookExecutor{} },
|
||||
},
|
||||
{
|
||||
uc: "Two web_hooks are configured",
|
||||
|
|
@ -374,8 +370,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []login.PreHookExecutor {
|
||||
return []login.PreHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -385,11 +381,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PreLoginHooks(ctx)
|
||||
h, err := reg.PreLoginHooks(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -401,7 +396,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []login.PostHookExecutor { return nil },
|
||||
expect: func(reg *driver.RegistryDefault) []login.PostHookExecutor { return []login.PostHookExecutor{} },
|
||||
},
|
||||
{
|
||||
uc: "Only revoke_active_sessions hook configured for password strategy",
|
||||
|
|
@ -440,7 +435,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {
|
||||
return []login.PostHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"body":"bar","headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{TemplateURI: "bar", Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewAddressVerifier(),
|
||||
hook.NewSessionDestroyer(reg),
|
||||
}
|
||||
|
|
@ -456,8 +451,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {
|
||||
return []login.PostHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -475,7 +470,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {
|
||||
return []login.PostHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewSessionDestroyer(reg),
|
||||
hook.NewAddressVerifier(),
|
||||
}
|
||||
|
|
@ -487,11 +482,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PostLoginHooks(ctx, identity.CredentialsTypePassword)
|
||||
h, err := reg.PostLoginHooks(ctx, identity.CredentialsTypePassword)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -506,7 +500,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []settings.PreHookExecutor { return nil },
|
||||
expect: func(reg *driver.RegistryDefault) []settings.PreHookExecutor { return []settings.PreHookExecutor{} },
|
||||
},
|
||||
{
|
||||
uc: "Two web_hooks are configured",
|
||||
|
|
@ -518,8 +512,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []settings.PreHookExecutor {
|
||||
return []settings.PreHookExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -529,11 +523,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PreSettingsHooks(ctx)
|
||||
h, err := reg.PreSettingsHooks(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -544,8 +537,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
expect func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor
|
||||
}{
|
||||
{
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor { return nil },
|
||||
uc: "No hooks configured",
|
||||
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
|
||||
return []settings.PostHookPostPersistExecutor{}
|
||||
},
|
||||
},
|
||||
{
|
||||
uc: "Only verify hook configured for the strategy",
|
||||
|
|
@ -565,14 +560,14 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
uc: "A verify hook and a web_hook are configured for profile strategy",
|
||||
config: map[string]any{
|
||||
config.ViperKeySelfServiceSettingsAfter + ".profile.hooks": []map[string]any{
|
||||
{"hook": "web_hook", "config": map[string]any{"headers": []map[string]string{{"X-Custom-Header": "test"}}, "url": "foo", "method": "POST", "body": "bar"}},
|
||||
{"hook": "web_hook", "config": map[string]any{"headers": map[string]string{"X-Custom-Header": "test"}, "url": "foo", "method": "POST", "body": "bar"}},
|
||||
},
|
||||
config.ViperKeySelfServiceVerificationEnabled: true,
|
||||
},
|
||||
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
|
||||
return []settings.PostHookPostPersistExecutor{
|
||||
hook.NewVerifier(reg),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"body":"bar","headers":[{"X-Custom-Header":"test"}],"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{TemplateURI: "bar", Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -586,8 +581,8 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
},
|
||||
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
|
||||
return []settings.PostHookPostPersistExecutor{
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"POST","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"bar"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "bar", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -605,7 +600,7 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
|
||||
return []settings.PostHookPostPersistExecutor{
|
||||
hook.NewVerifier(reg),
|
||||
hook.NewWebHook(reg, json.RawMessage(`{"headers":{"X-Custom-Header":"test"},"method":"GET","url":"foo"}`)),
|
||||
hook.NewWebHook(reg, &request.Config{Method: "GET", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -615,11 +610,10 @@ func TestDriverDefault_Hooks(t *testing.T) {
|
|||
|
||||
ctx := confighelpers.WithConfigValues(ctx, tc.config)
|
||||
|
||||
h := reg.PostSettingsPostPersistHooks(ctx, "profile")
|
||||
h, err := reg.PostSettingsPostPersistHooks(ctx, "profile")
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExecutors := tc.expect(reg)
|
||||
require.Len(t, h, len(expectedExecutors))
|
||||
assert.Equal(t, expectedExecutors, h)
|
||||
assert.Equal(t, tc.expect(reg), h)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -91,21 +91,10 @@ func (m *RegistryDefault) VerificationExecutor() *verification.HookExecutor {
|
|||
return m.selfserviceVerificationExecutor
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PreVerificationHooks(ctx context.Context) (b []verification.PreHookExecutor) {
|
||||
for _, v := range m.getHooks("", m.Config().SelfServiceFlowVerificationBeforeHooks(ctx)) {
|
||||
if hook, ok := v.(verification.PreHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
return
|
||||
func (m *RegistryDefault) PreVerificationHooks(ctx context.Context) ([]verification.PreHookExecutor, error) {
|
||||
return getHooks[verification.PreHookExecutor](m, "", m.Config().SelfServiceFlowVerificationBeforeHooks(ctx))
|
||||
}
|
||||
|
||||
func (m *RegistryDefault) PostVerificationHooks(ctx context.Context) (b []verification.PostHookExecutor) {
|
||||
for _, v := range m.getHooks(config.HookGlobal, m.Config().SelfServiceFlowVerificationAfterHooks(ctx, config.HookGlobal)) {
|
||||
if hook, ok := v.(verification.PostHookExecutor); ok {
|
||||
b = append(b, hook)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
func (m *RegistryDefault) PostVerificationHooks(ctx context.Context) ([]verification.PostHookExecutor, error) {
|
||||
return getHooks[verification.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowVerificationAfterHooks(ctx, config.HookGlobal))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -365,9 +365,7 @@
|
|||
"response": {
|
||||
"properties": {
|
||||
"ignore": {
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
"const": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
@ -383,9 +381,7 @@
|
|||
{
|
||||
"properties": {
|
||||
"can_interrupt": {
|
||||
"enum": [
|
||||
false
|
||||
]
|
||||
"const": false
|
||||
}
|
||||
},
|
||||
"require": [
|
||||
|
|
@ -1918,6 +1914,18 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"pattern": "^(http|https|file|base64)://",
|
||||
"description": "URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods, which support HTTP body payloads",
|
||||
"examples": [
|
||||
"file:///path/to/body.jsonnet",
|
||||
"file://./body.jsonnet",
|
||||
"base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=",
|
||||
"https://oryapis.com/default_body.jsonnet"
|
||||
]
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/ory/x/crdbx"
|
||||
|
|
@ -54,7 +57,7 @@ type (
|
|||
ManagementProvider
|
||||
x.WriterProvider
|
||||
config.Provider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
cipher.Provider
|
||||
hash.HashProvider
|
||||
}
|
||||
|
|
@ -86,21 +89,21 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
x.AdminPrefix+RouteCollection+"/*/credentials/*",
|
||||
)
|
||||
|
||||
public.GET(RouteCollection, x.RedirectToAdminRoute(h.r))
|
||||
public.GET(RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.POST(RouteCollection, x.RedirectToAdminRoute(h.r))
|
||||
public.PUT(RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.PATCH(RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(RouteCredentialItem, x.RedirectToAdminRoute(h.r))
|
||||
public.GET(RouteCollection, redir.RedirectToAdminRoute(h.r))
|
||||
public.GET(RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.POST(RouteCollection, redir.RedirectToAdminRoute(h.r))
|
||||
public.PUT(RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.PATCH(RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(RouteCredentialItem, redir.RedirectToAdminRoute(h.r))
|
||||
|
||||
public.GET(x.AdminPrefix+RouteCollection, x.RedirectToAdminRoute(h.r))
|
||||
public.GET(x.AdminPrefix+RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(x.AdminPrefix+RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.POST(x.AdminPrefix+RouteCollection, x.RedirectToAdminRoute(h.r))
|
||||
public.PUT(x.AdminPrefix+RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.PATCH(x.AdminPrefix+RouteItem, x.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(x.AdminPrefix+RouteCredentialItem, x.RedirectToAdminRoute(h.r))
|
||||
public.GET(x.AdminPrefix+RouteCollection, redir.RedirectToAdminRoute(h.r))
|
||||
public.GET(x.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(x.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.POST(x.AdminPrefix+RouteCollection, redir.RedirectToAdminRoute(h.r))
|
||||
public.PUT(x.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.PATCH(x.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))
|
||||
public.DELETE(x.AdminPrefix+RouteCredentialItem, redir.RedirectToAdminRoute(h.r))
|
||||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
confighelpers "github.com/ory/kratos/driver/config/testhelpers"
|
||||
|
||||
"github.com/ory/x/contextx"
|
||||
|
|
@ -31,7 +33,6 @@ import (
|
|||
"github.com/ory/kratos/driver"
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/selfservice/hook"
|
||||
"github.com/ory/kratos/x"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -70,8 +71,8 @@ func NewConfigurationWithDefaults(t testing.TB, opts ...configx.OptionModifier)
|
|||
// easier and way faster. This suite does not work for e2e or advanced integration tests.
|
||||
func NewFastRegistryWithMocks(t *testing.T, opts ...configx.OptionModifier) (*config.Config, *driver.RegistryDefault) {
|
||||
conf, reg := NewRegistryDefaultWithDSN(t, "", opts...)
|
||||
reg.WithCSRFTokenGenerator(x.FakeCSRFTokenGenerator)
|
||||
reg.WithCSRFHandler(x.NewFakeCSRFHandler(""))
|
||||
reg.WithCSRFTokenGenerator(nosurfx.FakeCSRFTokenGenerator)
|
||||
reg.WithCSRFHandler(nosurfx.NewFakeCSRFHandler(""))
|
||||
reg.WithHooks(map[string]func(config.SelfServiceHook) interface{}{
|
||||
"err": func(c config.SelfServiceHook) interface{} {
|
||||
return &hook.Error{Config: c.Config}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
|
|
@ -115,7 +117,7 @@ func AssertSchemDoesNotExist(t *testing.T, reg *driver.RegistryDefault, flows []
|
|||
values := url.Values{
|
||||
"traits.username": {testhelpers.RandomEmail()},
|
||||
"traits.foobar": {"bar"},
|
||||
"csrf_token": {x.FakeCSRFToken},
|
||||
"csrf_token": {nosurfx.FakeCSRFToken},
|
||||
}
|
||||
payload(values)
|
||||
|
||||
|
|
@ -180,7 +182,7 @@ func AssertCSRFFailures(t *testing.T, reg *driver.RegistryDefault, flows []strin
|
|||
|
||||
actual, res := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, values.Encode())
|
||||
assert.EqualValues(t, http.StatusOK, res.StatusCode)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken,
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken,
|
||||
json.RawMessage(actual), "%s", actual)
|
||||
})
|
||||
|
||||
|
|
@ -192,7 +194,7 @@ func AssertCSRFFailures(t *testing.T, reg *driver.RegistryDefault, flows []strin
|
|||
|
||||
actual, res := testhelpers.RegistrationMakeRequest(t, false, true, f, browserClient, values.Encode())
|
||||
assert.EqualValues(t, http.StatusForbidden, res.StatusCode)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken,
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken,
|
||||
json.RawMessage(gjson.Get(actual, "error").Raw), "%s", actual)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ import (
|
|||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/ui/node"
|
||||
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/x/pointerx"
|
||||
|
||||
kratos "github.com/ory/kratos/internal/httpclient"
|
||||
|
|
@ -69,7 +70,7 @@ func NewFakeCSRFNode() *kratos.UiNode {
|
|||
Name: "csrf_token",
|
||||
Required: pointerx.Bool(true),
|
||||
Type: "hidden",
|
||||
Value: x.FakeCSRFToken,
|
||||
Value: nosurfx.FakeCSRFToken,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
kratos "github.com/ory/kratos/internal/httpclient"
|
||||
|
|
@ -182,7 +184,7 @@ func NewSettingsAPIServer(t *testing.T, reg *driver.RegistryDefault, ids map[str
|
|||
|
||||
n := negroni.Classic()
|
||||
n.UseHandler(public)
|
||||
hh := x.NewTestCSRFHandler(n, reg)
|
||||
hh := nosurfx.NewTestCSRFHandler(n, reg)
|
||||
reg.WithCSRFHandler(hh)
|
||||
|
||||
reg.SettingsHandler().RegisterPublicRoutes(public)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/urfave/negroni"
|
||||
|
||||
"github.com/gobuffalo/httptest"
|
||||
|
|
@ -28,7 +30,7 @@ func NewKratosServerWithCSRF(t *testing.T, reg driver.Registry) (public, admin *
|
|||
|
||||
func NewKratosServerWithCSRFAndRouters(t *testing.T, reg driver.Registry) (public, admin *httptest.Server, rp *x.RouterPublic, ra *x.RouterAdmin) {
|
||||
rp, ra = x.NewRouterPublic(), x.NewRouterAdmin()
|
||||
csrfHandler := x.NewTestCSRFHandler(rp, reg)
|
||||
csrfHandler := nosurfx.NewTestCSRFHandler(rp, reg)
|
||||
reg.WithCSRFHandler(csrfHandler)
|
||||
ran := negroni.New()
|
||||
ran.UseFunc(x.RedirectAdminMiddleware)
|
||||
|
|
@ -36,7 +38,7 @@ func NewKratosServerWithCSRFAndRouters(t *testing.T, reg driver.Registry) (publi
|
|||
rpn := negroni.New()
|
||||
rpn.UseFunc(x.HTTPLoaderContextMiddleware(reg))
|
||||
rpn.UseHandler(rp)
|
||||
public = httptest.NewServer(x.NewTestCSRFHandler(rpn, reg))
|
||||
public = httptest.NewServer(nosurfx.NewTestCSRFHandler(rpn, reg))
|
||||
admin = httptest.NewServer(ran)
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,31 +4,91 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
type (
|
||||
noopAuthStrategy struct{}
|
||||
basicAuthStrategy struct {
|
||||
user string
|
||||
password string
|
||||
}
|
||||
apiKeyStrategy struct {
|
||||
name string
|
||||
value string
|
||||
in string
|
||||
}
|
||||
AuthStrategy interface {
|
||||
apply(req *retryablehttp.Request)
|
||||
}
|
||||
|
||||
authStrategyFactory func(c json.RawMessage) (AuthStrategy, error)
|
||||
)
|
||||
|
||||
var strategyFactories = map[string]authStrategyFactory{
|
||||
"": newNoopAuthStrategy,
|
||||
"api_key": newApiKeyStrategy,
|
||||
"basic_auth": newBasicAuthStrategy,
|
||||
}
|
||||
|
||||
func authStrategy(name string, config json.RawMessage) (AuthStrategy, error) {
|
||||
strategyFactory, ok := strategyFactories[name]
|
||||
if ok {
|
||||
return strategyFactory(config)
|
||||
func authStrategy(typ string, config map[string]any) (AuthStrategy, error) {
|
||||
switch typ {
|
||||
case "":
|
||||
return NewNoopAuthStrategy(), nil
|
||||
case "api_key":
|
||||
name, ok := config["name"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("api_key auth strategy requires a string name")
|
||||
}
|
||||
value, ok := config["value"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("api_key auth strategy requires a string value")
|
||||
}
|
||||
in, _ := config["in"].(string) // in is optional
|
||||
return NewAPIKeyStrategy(in, name, value), nil
|
||||
case "basic_auth":
|
||||
user, ok := config["user"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("basic_auth auth strategy requires a string user")
|
||||
}
|
||||
password, ok := config["password"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("basic_auth auth strategy requires a string password")
|
||||
}
|
||||
return NewBasicAuthStrategy(user, password), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported auth type: %s", name)
|
||||
return nil, fmt.Errorf("unsupported auth type: %s", typ)
|
||||
}
|
||||
|
||||
func NewNoopAuthStrategy() AuthStrategy {
|
||||
return &noopAuthStrategy{}
|
||||
}
|
||||
|
||||
func (c *noopAuthStrategy) apply(_ *retryablehttp.Request) {}
|
||||
|
||||
func NewBasicAuthStrategy(user, password string) AuthStrategy {
|
||||
return &basicAuthStrategy{
|
||||
user: user,
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *basicAuthStrategy) apply(req *retryablehttp.Request) {
|
||||
req.SetBasicAuth(c.user, c.password)
|
||||
}
|
||||
|
||||
func NewAPIKeyStrategy(in, name, value string) AuthStrategy {
|
||||
return &apiKeyStrategy{
|
||||
in: in,
|
||||
name: name,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *apiKeyStrategy) apply(req *retryablehttp.Request) {
|
||||
switch c.in {
|
||||
case "cookie":
|
||||
req.AddCookie(&http.Cookie{Name: c.name, Value: c.value})
|
||||
default:
|
||||
// TODO add deprecation warning
|
||||
fallthrough
|
||||
case "header", "":
|
||||
req.Header.Set(c.name, c.value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
type (
|
||||
noopAuthStrategy struct{}
|
||||
|
||||
basicAuthStrategy struct {
|
||||
user string
|
||||
password string
|
||||
}
|
||||
|
||||
apiKeyStrategy struct {
|
||||
name string
|
||||
value string
|
||||
in string
|
||||
}
|
||||
)
|
||||
|
||||
func newNoopAuthStrategy(_ json.RawMessage) (AuthStrategy, error) {
|
||||
return &noopAuthStrategy{}, nil
|
||||
}
|
||||
|
||||
func (c *noopAuthStrategy) apply(_ *retryablehttp.Request) {}
|
||||
|
||||
func newBasicAuthStrategy(raw json.RawMessage) (AuthStrategy, error) {
|
||||
type config struct {
|
||||
User string
|
||||
Password string
|
||||
}
|
||||
|
||||
var c config
|
||||
if err := json.Unmarshal(raw, &c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &basicAuthStrategy{
|
||||
user: c.User,
|
||||
password: c.Password,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *basicAuthStrategy) apply(req *retryablehttp.Request) {
|
||||
req.SetBasicAuth(c.user, c.password)
|
||||
}
|
||||
|
||||
func newApiKeyStrategy(raw json.RawMessage) (AuthStrategy, error) {
|
||||
type config struct {
|
||||
In string
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
var c config
|
||||
if err := json.Unmarshal(raw, &c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &apiKeyStrategy{
|
||||
in: c.In,
|
||||
name: c.Name,
|
||||
value: c.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *apiKeyStrategy) apply(req *retryablehttp.Request) {
|
||||
switch c.in {
|
||||
case "cookie":
|
||||
req.AddCookie(&http.Cookie{Name: c.name, Value: c.value})
|
||||
default:
|
||||
req.Header.Set(c.name, c.value)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNoopAuthStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := noopAuthStrategy{}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
assert.Empty(t, req.Header, "Empty auth strategy shall not modify any request headers")
|
||||
}
|
||||
|
||||
func TestBasicAuthStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := basicAuthStrategy{
|
||||
user: "test-user",
|
||||
password: "test-pass",
|
||||
}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
assert.Len(t, req.Header, 1)
|
||||
|
||||
user, pass, _ := req.BasicAuth()
|
||||
assert.Equal(t, "test-user", user)
|
||||
assert.Equal(t, "test-pass", pass)
|
||||
}
|
||||
|
||||
func TestApiKeyInHeaderStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := apiKeyStrategy{
|
||||
in: "header",
|
||||
name: "my-api-key-name",
|
||||
value: "my-api-key-value",
|
||||
}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
require.Len(t, req.Header, 1)
|
||||
|
||||
actualValue := req.Header.Get("my-api-key-name")
|
||||
assert.Equal(t, "my-api-key-value", actualValue)
|
||||
}
|
||||
|
||||
func TestApiKeyInCookieStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := apiKeyStrategy{
|
||||
in: "cookie",
|
||||
name: "my-api-key-name",
|
||||
value: "my-api-key-value",
|
||||
}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
cookies := req.Cookies()
|
||||
assert.Len(t, cookies, 1)
|
||||
|
||||
assert.Equal(t, "my-api-key-name", cookies[0].Name)
|
||||
assert.Equal(t, "my-api-key-value", cookies[0].Value)
|
||||
}
|
||||
|
|
@ -4,53 +4,114 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNoopAuthStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := noopAuthStrategy{}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
assert.Empty(t, req.Header, "Empty auth strategy shall not modify any request headers")
|
||||
}
|
||||
|
||||
func TestBasicAuthStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := basicAuthStrategy{
|
||||
user: "test-user",
|
||||
password: "test-pass",
|
||||
}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
assert.Len(t, req.Header, 1)
|
||||
|
||||
user, pass, _ := req.BasicAuth()
|
||||
assert.Equal(t, "test-user", user)
|
||||
assert.Equal(t, "test-pass", pass)
|
||||
}
|
||||
|
||||
func TestApiKeyInHeaderStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := apiKeyStrategy{
|
||||
in: "header",
|
||||
name: "my-api-key-name",
|
||||
value: "my-api-key-value",
|
||||
}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
require.Len(t, req.Header, 1)
|
||||
|
||||
actualValue := req.Header.Get("my-api-key-name")
|
||||
assert.Equal(t, "my-api-key-value", actualValue)
|
||||
}
|
||||
|
||||
func TestApiKeyInCookieStrategy(t *testing.T) {
|
||||
req := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}
|
||||
auth := apiKeyStrategy{
|
||||
in: "cookie",
|
||||
name: "my-api-key-name",
|
||||
value: "my-api-key-value",
|
||||
}
|
||||
|
||||
auth.apply(&req)
|
||||
|
||||
cookies := req.Cookies()
|
||||
assert.Len(t, cookies, 1)
|
||||
|
||||
assert.Equal(t, "my-api-key-name", cookies[0].Name)
|
||||
assert.Equal(t, "my-api-key-value", cookies[0].Value)
|
||||
}
|
||||
|
||||
func TestAuthStrategy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range map[string]struct {
|
||||
name string
|
||||
config string
|
||||
config map[string]any
|
||||
expected AuthStrategy
|
||||
}{
|
||||
"noop": {
|
||||
name: "",
|
||||
config: "",
|
||||
config: map[string]any{},
|
||||
expected: &noopAuthStrategy{},
|
||||
},
|
||||
"basic_auth": {
|
||||
name: "basic_auth",
|
||||
config: `{
|
||||
"user": "test-api-user",
|
||||
"password": "secret"
|
||||
}`,
|
||||
config: map[string]any{
|
||||
"user": "test-api-user",
|
||||
"password": "secret",
|
||||
},
|
||||
expected: &basicAuthStrategy{},
|
||||
},
|
||||
"api-key/header": {
|
||||
name: "api_key",
|
||||
config: `{
|
||||
"in": "header",
|
||||
"name": "my-api-key",
|
||||
"value": "secret"
|
||||
}`,
|
||||
config: map[string]any{
|
||||
"in": "header",
|
||||
"name": "my-api-key",
|
||||
"value": "secret",
|
||||
},
|
||||
expected: &apiKeyStrategy{},
|
||||
},
|
||||
"api-key/cookie": {
|
||||
name: "api_key",
|
||||
config: `{
|
||||
"in": "cookie",
|
||||
"name": "my-api-key",
|
||||
"value": "secret"
|
||||
}`,
|
||||
config: map[string]any{
|
||||
"in": "cookie",
|
||||
"name": "my-api-key",
|
||||
"value": "secret",
|
||||
},
|
||||
expected: &apiKeyStrategy{},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
strategy, err := authStrategy(tc.name, json.RawMessage(tc.config))
|
||||
strategy, err := authStrategy(tc.name, tc.config)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.IsTypef(t, tc.expected, strategy, "auth strategy should be of the expected type")
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func WithCache(cache *ristretto.Cache[[]byte, []byte]) BuilderOption {
|
|||
}
|
||||
}
|
||||
|
||||
func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies, o ...BuilderOption) (_ *Builder, err error) {
|
||||
func NewBuilder(ctx context.Context, c *Config, deps Dependencies, o ...BuilderOption) (_ *Builder, err error) {
|
||||
_, span := deps.Tracer(ctx).Tracer().Start(ctx, "request.NewBuilder")
|
||||
defer otelx.End(span, &err)
|
||||
|
||||
|
|
@ -67,11 +67,6 @@ func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies,
|
|||
f(&opts)
|
||||
}
|
||||
|
||||
c := Config{}
|
||||
if err := json.Unmarshal(config, &c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
span.SetAttributes(
|
||||
attribute.String("url", c.URL),
|
||||
attribute.String("method", c.Method),
|
||||
|
|
@ -82,27 +77,27 @@ func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
c.header = make(http.Header, len(c.Headers))
|
||||
for k, v := range c.Headers {
|
||||
c.header.Add(k, v)
|
||||
}
|
||||
if c.header.Get("Content-Type") == "" {
|
||||
c.header.Set("Content-Type", ContentTypeJSON)
|
||||
}
|
||||
|
||||
c.auth, err = authStrategy(c.Auth.Type, c.Auth.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Builder{
|
||||
r: r,
|
||||
Config: &c,
|
||||
Config: c,
|
||||
deps: deps,
|
||||
cache: opts.cache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *Builder) addAuth() error {
|
||||
authConfig := b.Config.Auth
|
||||
|
||||
strategy, err := authStrategy(authConfig.Type, authConfig.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
strategy.apply(b.r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) addBody(ctx context.Context, body interface{}) (err error) {
|
||||
ctx, span := b.deps.Tracer(ctx).Tracer().Start(ctx, "request.Builder.addBody")
|
||||
defer otelx.End(span, &err)
|
||||
|
|
@ -111,8 +106,6 @@ func (b *Builder) addBody(ctx context.Context, body interface{}) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
contentType := b.r.Header.Get("Content-Type")
|
||||
|
||||
if b.Config.TemplateURI == "" {
|
||||
return errors.New("got empty template path for request with body")
|
||||
}
|
||||
|
|
@ -122,11 +115,14 @@ func (b *Builder) addBody(ctx context.Context, body interface{}) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
switch contentType {
|
||||
switch b.r.Header.Get("Content-Type") {
|
||||
case ContentTypeForm:
|
||||
if err := b.addURLEncodedBody(ctx, tpl, body); err != nil {
|
||||
return err
|
||||
}
|
||||
case "":
|
||||
b.r.Header.Set("Content-Type", ContentTypeJSON)
|
||||
fallthrough
|
||||
case ContentTypeJSON:
|
||||
if err := b.addJSONBody(ctx, tpl, body); err != nil {
|
||||
return err
|
||||
|
|
@ -217,10 +213,8 @@ func (b *Builder) addURLEncodedBody(ctx context.Context, jsonnetSnippet []byte,
|
|||
}
|
||||
|
||||
func (b *Builder) BuildRequest(ctx context.Context, body interface{}) (*retryablehttp.Request, error) {
|
||||
b.r.Header = b.Config.Header
|
||||
if err := b.addAuth(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.r.Header = b.Config.header
|
||||
b.Config.auth.apply(b.r)
|
||||
|
||||
// According to the HTTP spec any request method, but TRACE is allowed to
|
||||
// have a body. Even this is a bad practice for some of them, like for GET
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
_ "embed"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
|
|
@ -31,76 +30,91 @@ type testRequestBody struct {
|
|||
var testJSONNetTemplate []byte
|
||||
|
||||
func TestBuildRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
method string
|
||||
url string
|
||||
authStrategy string
|
||||
expectedHeader http.Header
|
||||
bodyTemplateURI string
|
||||
body *testRequestBody
|
||||
expectedBody string
|
||||
rawConfig string
|
||||
name string
|
||||
method string
|
||||
url string
|
||||
authStrategy AuthStrategy
|
||||
expectedHeader http.Header
|
||||
bodyTemplateURI string
|
||||
body *testRequestBody
|
||||
expectedJSONBody string
|
||||
expectedRawBody string
|
||||
config Config
|
||||
}{
|
||||
{
|
||||
name: "POST request without auth",
|
||||
method: "POST",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint1",
|
||||
authStrategy: "", // noop strategy
|
||||
authStrategy: NewNoopAuthStrategy(),
|
||||
bodyTemplateURI: "file://./stub/test_body.jsonnet",
|
||||
body: &testRequestBody{
|
||||
To: "+15056445993",
|
||||
From: "+12288534869",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "{\n \"body\": \"test-sms-body\",\n \"from\": \"+12288534869\",\n \"to\": \"+15056445993\"\n}\n",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint1",
|
||||
"method": "POST",
|
||||
"body": "file://./stub/test_body.jsonnet"
|
||||
expectedJSONBody: `{
|
||||
"body": "test-sms-body",
|
||||
"from": "+12288534869",
|
||||
"to": "+15056445993"
|
||||
}`,
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint1",
|
||||
Method: "POST",
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "POST request with legacy template path",
|
||||
method: "POST",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint1",
|
||||
authStrategy: NewNoopAuthStrategy(),
|
||||
bodyTemplateURI: "./stub/test_body.jsonnet",
|
||||
body: &testRequestBody{
|
||||
To: "+15056445993",
|
||||
From: "+12288534869",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "{\n \"body\": \"test-sms-body\",\n \"from\": \"+12288534869\",\n \"to\": \"+15056445993\"\n}\n",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint1",
|
||||
"method": "POST",
|
||||
"body": "./stub/test_body.jsonnet"
|
||||
expectedJSONBody: `{
|
||||
"body": "test-sms-body",
|
||||
"from": "+12288534869",
|
||||
"to": "+15056445993"
|
||||
}`,
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint1",
|
||||
Method: "POST",
|
||||
TemplateURI: "./stub/test_body.jsonnet",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "POST request with base64 encoded template path",
|
||||
method: "POST",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint1",
|
||||
authStrategy: NewNoopAuthStrategy(),
|
||||
bodyTemplateURI: "base64://" + base64.StdEncoding.EncodeToString(testJSONNetTemplate),
|
||||
body: &testRequestBody{
|
||||
To: "+15056445993",
|
||||
From: "+12288534869",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "{\n \"body\": \"test-sms-body\",\n \"from\": \"+12288534869\",\n \"to\": \"+15056445993\"\n}\n",
|
||||
rawConfig: fmt.Sprintf(
|
||||
`{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint1",
|
||||
"method": "POST",
|
||||
"body": "base64://%s"
|
||||
}`, base64.StdEncoding.EncodeToString(testJSONNetTemplate),
|
||||
),
|
||||
expectedJSONBody: `{
|
||||
"body": "test-sms-body",
|
||||
"from": "+12288534869",
|
||||
"to": "+15056445993"
|
||||
}`,
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint1",
|
||||
Method: "POST",
|
||||
TemplateURI: "base64://" + base64.StdEncoding.EncodeToString(testJSONNetTemplate),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "POST request with custom header",
|
||||
method: "POST",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint2",
|
||||
authStrategy: "",
|
||||
authStrategy: NewNoopAuthStrategy(),
|
||||
expectedHeader: map[string][]string{"Custom-Header": {"test"}},
|
||||
bodyTemplateURI: "file://./stub/test_body.jsonnet",
|
||||
body: &testRequestBody{
|
||||
|
|
@ -108,184 +122,201 @@ func TestBuildRequest(t *testing.T) {
|
|||
From: "+15822228108",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "{\n \"body\": \"test-sms-body\",\n \"from\": \"+15822228108\",\n \"to\": \"+12127110378\"\n}\n",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint2",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"Custom-Header": "test"
|
||||
},
|
||||
"body": "file://./stub/test_body.jsonnet"
|
||||
expectedJSONBody: `{
|
||||
"body": "test-sms-body",
|
||||
"from": "+15822228108",
|
||||
"to": "+12127110378"
|
||||
}`,
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint2",
|
||||
Method: "POST",
|
||||
Headers: map[string]string{
|
||||
"Custom-Header": "test",
|
||||
},
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GET request with body",
|
||||
method: "GET",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint3",
|
||||
authStrategy: "basic_auth",
|
||||
authStrategy: NewBasicAuthStrategy("test-api-user", "secret"),
|
||||
bodyTemplateURI: "file://./stub/test_body.jsonnet",
|
||||
body: &testRequestBody{
|
||||
To: "+14134242223",
|
||||
From: "+13104661805",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "{\n \"body\": \"test-sms-body\",\n \"from\": \"+13104661805\",\n \"to\": \"+14134242223\"\n}\n",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint3",
|
||||
"method": "GET",
|
||||
"auth": {
|
||||
"type": "basic_auth",
|
||||
"config": {
|
||||
"user": "test-api-user",
|
||||
"password": "secret"
|
||||
}
|
||||
},
|
||||
"body": "file://./stub/test_body.jsonnet"
|
||||
expectedJSONBody: `{
|
||||
"body": "test-sms-body",
|
||||
"from": "+13104661805",
|
||||
"to": "+14134242223"
|
||||
}`,
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint3",
|
||||
Method: "GET",
|
||||
Auth: AuthConfig{
|
||||
Type: "basic_auth",
|
||||
Config: map[string]any{
|
||||
"user": "test-api-user",
|
||||
"password": "secret",
|
||||
},
|
||||
},
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GET request without body",
|
||||
method: "GET",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint4",
|
||||
authStrategy: "basic_auth",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint4",
|
||||
"method": "GET",
|
||||
"auth": {
|
||||
"type": "basic_auth",
|
||||
"config": {
|
||||
"user": "test-api-user",
|
||||
"password": "secret"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
authStrategy: NewBasicAuthStrategy("test-api-user", "secret"),
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint4",
|
||||
Method: "GET",
|
||||
Auth: AuthConfig{
|
||||
Type: "basic_auth",
|
||||
Config: map[string]any{
|
||||
"user": "test-api-user",
|
||||
"password": "secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DELETE request with body",
|
||||
method: "DELETE",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint5",
|
||||
authStrategy: "api_key",
|
||||
authStrategy: NewAPIKeyStrategy("header", "my-api-key", "secret"),
|
||||
bodyTemplateURI: "file://./stub/test_body.jsonnet",
|
||||
body: &testRequestBody{
|
||||
To: "+12235499085",
|
||||
From: "+14253787846",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "{\n \"body\": \"test-sms-body\",\n \"from\": \"+14253787846\",\n \"to\": \"+12235499085\"\n}\n",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint5",
|
||||
"method": "DELETE",
|
||||
"body": "file://./stub/test_body.jsonnet",
|
||||
"auth": {
|
||||
"type": "api_key",
|
||||
"config": {
|
||||
"in": "header",
|
||||
"name": "my-api-key",
|
||||
"value": "secret"
|
||||
}
|
||||
}
|
||||
expectedJSONBody: `{
|
||||
"body": "test-sms-body",
|
||||
"from": "+14253787846",
|
||||
"to": "+12235499085"
|
||||
}`,
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint5",
|
||||
Method: "DELETE",
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
Auth: AuthConfig{
|
||||
Type: "api_key",
|
||||
Config: map[string]any{
|
||||
"in": "header",
|
||||
"name": "my-api-key",
|
||||
"value": "secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "POST request with urlencoded body",
|
||||
method: "POST",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint6",
|
||||
bodyTemplateURI: "file://./stub/test_body.jsonnet",
|
||||
authStrategy: "api_key",
|
||||
authStrategy: NewAPIKeyStrategy("cookie", "my-api-key", "secret"),
|
||||
expectedHeader: map[string][]string{"Content-Type": {ContentTypeForm}},
|
||||
body: &testRequestBody{
|
||||
To: "+14134242223",
|
||||
From: "+13104661805",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "body=test-sms-body&from=%2B13104661805&to=%2B14134242223",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint6",
|
||||
"method": "POST",
|
||||
"body": "file://./stub/test_body.jsonnet",
|
||||
"headers": {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
expectedRawBody: "body=test-sms-body&from=%2B13104661805&to=%2B14134242223",
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint6",
|
||||
Method: "POST",
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
Headers: map[string]string{
|
||||
"Content-Type": ContentTypeForm,
|
||||
},
|
||||
"auth": {
|
||||
"type": "api_key",
|
||||
"config": {
|
||||
"in": "cookie",
|
||||
"name": "my-api-key",
|
||||
"value": "secret"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
Auth: AuthConfig{
|
||||
Type: "api_key",
|
||||
Config: map[string]any{
|
||||
"in": "cookie",
|
||||
"name": "my-api-key",
|
||||
"value": "secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "POST request with default body type",
|
||||
method: "POST",
|
||||
url: "https://test.kratos.ory.sh/my_endpoint7",
|
||||
bodyTemplateURI: "file://./stub/test_body.jsonnet",
|
||||
authStrategy: "basic_auth",
|
||||
authStrategy: NewBasicAuthStrategy("test-api-user", "secret"),
|
||||
expectedHeader: map[string][]string{"Content-Type": {ContentTypeJSON}},
|
||||
body: &testRequestBody{
|
||||
To: "+14134242223",
|
||||
From: "+13104661805",
|
||||
Body: "test-sms-body",
|
||||
},
|
||||
expectedBody: "{\n \"body\": \"test-sms-body\",\n \"from\": \"+13104661805\",\n \"to\": \"+14134242223\"\n}\n",
|
||||
rawConfig: `{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint7",
|
||||
"method": "POST",
|
||||
"body": "file://./stub/test_body.jsonnet",
|
||||
"auth": {
|
||||
"type": "basic_auth",
|
||||
"config": {
|
||||
"user": "test-api-user",
|
||||
"password": "secret"
|
||||
}
|
||||
}
|
||||
expectedJSONBody: `{
|
||||
"body": "test-sms-body",
|
||||
"from": "+13104661805",
|
||||
"to": "+14134242223"
|
||||
}`,
|
||||
config: Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint7",
|
||||
Method: "POST",
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
Auth: AuthConfig{
|
||||
Type: "basic_auth",
|
||||
Config: map[string]any{
|
||||
"user": "test-api-user",
|
||||
"password": "secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(
|
||||
"request-type="+tc.name, func(t *testing.T) {
|
||||
rb, err := NewBuilder(context.Background(), json.RawMessage(tc.rawConfig), newTestDependencyProvider(t))
|
||||
require.NoError(t, err)
|
||||
t.Run("request-type="+tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Equal(t, tc.bodyTemplateURI, rb.Config.TemplateURI)
|
||||
assert.Equal(t, tc.authStrategy, rb.Config.Auth.Type)
|
||||
|
||||
req, err := rb.BuildRequest(context.Background(), tc.body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.url, req.URL.String())
|
||||
assert.Equal(t, tc.method, req.Method)
|
||||
|
||||
if tc.body != nil {
|
||||
requestBody, err := req.BodyBytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.expectedBody, string(requestBody))
|
||||
}
|
||||
|
||||
if tc.expectedHeader != nil {
|
||||
mustContainHeader(t, tc.expectedHeader, req.Header)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
t.Run(
|
||||
"cancel request", func(t *testing.T) {
|
||||
rb, err := NewBuilder(context.Background(), json.RawMessage(
|
||||
`{
|
||||
"url": "https://test.kratos.ory.sh/my_endpoint6",
|
||||
"method": "POST",
|
||||
"body": "file://./stub/cancel_body.jsonnet"
|
||||
}`,
|
||||
), newTestDependencyProvider(t))
|
||||
rb, err := NewBuilder(context.Background(), &tc.config, newTestDependencyProvider(t))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = rb.BuildRequest(context.Background(), json.RawMessage(`{}`))
|
||||
require.ErrorIs(t, err, ErrCancel)
|
||||
},
|
||||
)
|
||||
assert.Equal(t, tc.bodyTemplateURI, rb.Config.TemplateURI)
|
||||
assert.Equal(t, tc.authStrategy, rb.Config.auth)
|
||||
|
||||
req, err := rb.BuildRequest(context.Background(), tc.body)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.url, req.URL.String())
|
||||
assert.Equal(t, tc.method, req.Method)
|
||||
|
||||
if tc.expectedJSONBody != "" {
|
||||
requestBody, err := req.BodyBytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, tc.expectedJSONBody, string(requestBody))
|
||||
} else if tc.expectedRawBody != "" {
|
||||
requestBody, err := req.BodyBytes()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.expectedRawBody, string(requestBody))
|
||||
}
|
||||
|
||||
if tc.expectedHeader != nil {
|
||||
mustContainHeader(t, tc.expectedHeader, req.Header)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("cancel request", func(t *testing.T) {
|
||||
rb, err := NewBuilder(context.Background(), &Config{
|
||||
URL: "https://test.kratos.ory.sh/my_endpoint6",
|
||||
Method: "POST",
|
||||
TemplateURI: "file://./stub/cancel_body.jsonnet",
|
||||
}, newTestDependencyProvider(t))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = rb.BuildRequest(context.Background(), json.RawMessage(`{}`))
|
||||
require.ErrorIs(t, err, ErrCancel)
|
||||
})
|
||||
}
|
||||
|
||||
type testDependencyProvider struct {
|
||||
|
|
|
|||
|
|
@ -4,49 +4,30 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type (
|
||||
Auth struct {
|
||||
Type string
|
||||
Config json.RawMessage
|
||||
AuthConfig = struct {
|
||||
Type string `json:"type" koanf:"type"`
|
||||
Config map[string]any `json:"config" koanf:"config"`
|
||||
}
|
||||
ResponseConfig = struct {
|
||||
Parse bool `json:"parse" koanf:"parse"`
|
||||
Ignore bool `json:"ignore" koanf:"ignore"`
|
||||
}
|
||||
|
||||
Config struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
TemplateURI string `json:"body"`
|
||||
Header http.Header `json:"-"`
|
||||
RawHeader json.RawMessage `json:"headers"`
|
||||
Auth Auth `json:"auth"`
|
||||
ID string `json:"id" koanf:"id"`
|
||||
Method string `json:"method" koanf:"method"`
|
||||
URL string `json:"url" koanf:"url"`
|
||||
TemplateURI string `json:"body" koanf:"body"`
|
||||
Headers map[string]string `json:"headers" koanf:"headers"`
|
||||
Auth AuthConfig `json:"auth" koanf:"auth"`
|
||||
EmitAnalyticsEvent *bool `json:"emit_analytics_event" koanf:"emit_analytics_event"`
|
||||
CanInterrupt bool `json:"can_interrupt" koanf:"can_interrupt"`
|
||||
Response ResponseConfig `json:"response" koanf:"response"`
|
||||
|
||||
auth AuthStrategy
|
||||
header http.Header
|
||||
}
|
||||
)
|
||||
|
||||
func (c *Config) UnmarshalJSON(raw []byte) error {
|
||||
type Alias Config
|
||||
var a Alias
|
||||
err := json.Unmarshal(raw, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawHeader := gjson.ParseBytes(a.RawHeader).Map()
|
||||
a.Header = make(http.Header, len(rawHeader))
|
||||
|
||||
_, ok := rawHeader["Content-Type"]
|
||||
if !ok {
|
||||
a.Header.Set("Content-Type", ContentTypeJSON)
|
||||
}
|
||||
|
||||
for key, value := range rawHeader {
|
||||
a.Header.Set(key, value.String())
|
||||
}
|
||||
|
||||
*c = Config(a)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
|
@ -28,7 +31,7 @@ type (
|
|||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
IdentitySchemaProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
config.Provider
|
||||
x.TracingProvider
|
||||
x.HTTPClientProvider
|
||||
|
|
@ -59,8 +62,8 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
admin.GET(fmt.Sprintf("/%s/:id", SchemasPath), x.RedirectToPublicRoute(h.r))
|
||||
admin.GET(fmt.Sprintf("/%s", SchemasPath), x.RedirectToPublicRoute(h.r))
|
||||
admin.GET(fmt.Sprintf("/%s/:id", SchemasPath), redir.RedirectToPublicRoute(h.r))
|
||||
admin.GET(fmt.Sprintf("/%s", SchemasPath), redir.RedirectToPublicRoute(h.r))
|
||||
}
|
||||
|
||||
// Raw JSON Schema
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/ory/x/stringsx"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
|
|
@ -33,7 +36,7 @@ type (
|
|||
}
|
||||
Handler struct {
|
||||
r handlerDependencies
|
||||
csrf x.CSRFToken
|
||||
csrf nosurfx.CSRFToken
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -52,7 +55,7 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(public *x.RouterAdmin) {
|
||||
public.GET(RouteGet, x.RedirectToPublicRoute(h.r))
|
||||
public.GET(RouteGet, redir.RedirectToPublicRoute(h.r))
|
||||
}
|
||||
|
||||
// swagger:parameters getFlowError
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/x/assertx"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
|
@ -34,7 +36,7 @@ func TestHandler(t *testing.T) {
|
|||
|
||||
t.Run("case=public authorization", func(t *testing.T) {
|
||||
router := x.NewRouterPublic()
|
||||
ns := x.NewTestCSRFHandler(router, reg)
|
||||
ns := nosurfx.NewTestCSRFHandler(router, reg)
|
||||
|
||||
h.RegisterPublicRoutes(router)
|
||||
router.GET("/regen", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
|
||||
"github.com/ory/x/urlx"
|
||||
|
|
@ -20,7 +22,7 @@ type (
|
|||
PersistenceProvider
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
config.Provider
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"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"
|
||||
|
|
@ -71,7 +73,7 @@ func (e *ReplacedError) EnhanceJSONError() interface{} {
|
|||
|
||||
func NewFlowReplacedError(message *text.Message) *ReplacedError {
|
||||
return &ReplacedError{
|
||||
DefaultError: x.ErrGone.WithID(text.ErrIDSelfServiceFlowReplaced).
|
||||
DefaultError: nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowReplaced).
|
||||
WithError("self-service flow replaced").
|
||||
WithReason(message.Text),
|
||||
}
|
||||
|
|
@ -142,7 +144,7 @@ func NewFlowExpiredError(at time.Time) *ExpiredError {
|
|||
return &ExpiredError{
|
||||
ExpiredAt: at.UTC(),
|
||||
Since: ago,
|
||||
DefaultError: x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
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()),
|
||||
}
|
||||
|
|
@ -187,7 +189,7 @@ func NewBrowserLocationChangeRequiredError(redirectTo string) *BrowserLocationCh
|
|||
}
|
||||
}
|
||||
|
||||
func HandleHookError(_ http.ResponseWriter, r *http.Request, f Flow, traits identity.Traits, group node.UiNodeGroup, flowError error, logger x.LoggingProvider, csrf x.CSRFTokenGeneratorProvider) error {
|
||||
func HandleHookError(_ http.ResponseWriter, r *http.Request, f Flow, traits identity.Traits, group node.UiNodeGroup, flowError error, logger x.LoggingProvider, csrf nosurfx.CSRFTokenGeneratorProvider) error {
|
||||
if f != nil {
|
||||
if traits != nil {
|
||||
cont, err := container.NewFromStruct("", group, traits, "traits")
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
|
@ -44,5 +46,5 @@ type Flow interface {
|
|||
}
|
||||
|
||||
type FlowWithRedirect interface {
|
||||
SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []x.SecureRedirectOption)
|
||||
SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []redir.SecureRedirectOption)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
|
@ -167,11 +169,11 @@ func NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Reques
|
|||
requestURL := x.RequestURL(r).String()
|
||||
|
||||
// Pre-validate the return to URL which is contained in the HTTP request.
|
||||
_, err := x.SecureRedirectTo(r,
|
||||
_, err := redir.SecureRedirectTo(r,
|
||||
conf.SelfServiceBrowserDefaultReturnTo(r.Context()),
|
||||
x.SecureRedirectUseSourceURL(requestURL),
|
||||
x.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
x.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
redir.SecureRedirectUseSourceURL(requestURL),
|
||||
redir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -290,13 +292,13 @@ func (f *Flow) GetUI() *container.Container {
|
|||
return f.UI
|
||||
}
|
||||
|
||||
func (f *Flow) SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []x.SecureRedirectOption) {
|
||||
return []x.SecureRedirectOption{
|
||||
x.SecureRedirectReturnTo(f.ReturnTo),
|
||||
x.SecureRedirectUseSourceURL(f.RequestURL),
|
||||
x.SecureRedirectAllowURLs(cfg.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
x.SecureRedirectAllowSelfServiceURLs(cfg.Config().SelfPublicURL(ctx)),
|
||||
x.SecureRedirectOverrideDefaultReturnTo(cfg.Config().SelfServiceFlowLoginReturnTo(ctx, f.Active.String())),
|
||||
func (f *Flow) SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []redir.SecureRedirectOption) {
|
||||
return []redir.SecureRedirectOption{
|
||||
redir.SecureRedirectReturnTo(f.ReturnTo),
|
||||
redir.SecureRedirectUseSourceURL(f.RequestURL),
|
||||
redir.SecureRedirectAllowURLs(cfg.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(cfg.Config().SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectOverrideDefaultReturnTo(cfg.Config().SelfServiceFlowLoginReturnTo(ctx, f.Active.String())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"github.com/ory/kratos/x/events"
|
||||
"github.com/ory/x/otelx"
|
||||
"github.com/ory/x/otelx/semconv"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
hydraclientgo "github.com/ory/hydra-client-go/v2"
|
||||
"github.com/ory/kratos/driver/config"
|
||||
|
|
@ -31,8 +27,13 @@ import (
|
|||
"github.com/ory/kratos/text"
|
||||
"github.com/ory/kratos/ui/node"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/kratos/x/events"
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
"github.com/ory/nosurf"
|
||||
"github.com/ory/x/decoderx"
|
||||
"github.com/ory/x/otelx"
|
||||
"github.com/ory/x/otelx/semconv"
|
||||
"github.com/ory/x/sqlxx"
|
||||
"github.com/ory/x/stringsx"
|
||||
"github.com/ory/x/urlx"
|
||||
|
|
@ -57,8 +58,8 @@ type (
|
|||
session.HandlerProvider
|
||||
session.ManagementProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.TracingProvider
|
||||
config.Provider
|
||||
ErrorHandlerProvider
|
||||
|
|
@ -91,12 +92,12 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
admin.GET(RouteInitBrowserFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))
|
||||
|
||||
admin.POST(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
}
|
||||
|
||||
type FlowOption func(f *Flow)
|
||||
|
|
@ -565,9 +566,9 @@ func (h *Handler) createBrowserLoginFlow(w http.ResponseWriter, r *http.Request,
|
|||
return
|
||||
}
|
||||
|
||||
returnTo, redirErr := x.SecureRedirectTo(r, h.d.Config().SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
x.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(ctx)),
|
||||
x.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
returnTo, redirErr := redir.SecureRedirectTo(r, h.d.Config().SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
)
|
||||
if redirErr != nil {
|
||||
h.d.SelfServiceErrorManager().Forward(ctx, w, r, redirErr)
|
||||
|
|
@ -667,7 +668,7 @@ func (h *Handler) getLoginFlow(w http.ResponseWriter, r *http.Request, _ httprou
|
|||
//
|
||||
// Resolves: https://github.com/ory/kratos/issues/1282
|
||||
if ar.Type == flow.TypeBrowser && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), ar.CSRFToken) {
|
||||
h.d.Writer().WriteError(w, r, x.CSRFErrorReason(r, h.d))
|
||||
h.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -675,13 +676,13 @@ func (h *Handler) getLoginFlow(w http.ResponseWriter, r *http.Request, _ httprou
|
|||
if ar.Type == flow.TypeBrowser {
|
||||
redirectURL := flow.GetFlowExpiredRedirectURL(ctx, h.d.Config(), RouteInitBrowserFlow, ar.ReturnTo)
|
||||
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
WithReason("The login flow has expired. Redirect the user to the login flow init endpoint to initialize a new login flow.").
|
||||
WithDetail("redirect_to", redirectURL.String()).
|
||||
WithDetail("return_to", ar.ReturnTo)))
|
||||
return
|
||||
}
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
WithReason("The login flow has expired. Call the login flow init API endpoint to initialize a new login flow.").
|
||||
WithDetail("api", urlx.AppendPaths(h.d.Config().SelfPublicURL(ctx), RouteInitAPIFlow).String())))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
|
@ -221,7 +223,7 @@ func TestFlowLifecycle(t *testing.T) {
|
|||
require.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))
|
||||
|
||||
hc := testhelpers.NewClientWithCookies(t)
|
||||
res, err := hc.PostForm(ts.URL+login.RouteSubmitFlow+"?flow="+f.ID.String(), url.Values{"method": {"password"}, "password_identifier": {id1mail}, "password": {"foobar"}, "csrf_token": {x.FakeCSRFToken}})
|
||||
res, err := hc.PostForm(ts.URL+login.RouteSubmitFlow+"?flow="+f.ID.String(), url.Values{"method": {"password"}, "password_identifier": {id1mail}, "password": {"foobar"}, "csrf_token": {nosurfx.FakeCSRFToken}})
|
||||
require.NoError(t, err)
|
||||
firstSession := x.MustReadAll(res.Body)
|
||||
require.NoError(t, res.Body.Close())
|
||||
|
|
@ -229,7 +231,7 @@ func TestFlowLifecycle(t *testing.T) {
|
|||
f = login.Flow{Type: tt, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(), UI: container.New(""), Refresh: true, RequestedAAL: "aal1"}
|
||||
require.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))
|
||||
|
||||
vv := testhelpers.EncodeFormAsJSON(t, tt == flow.TypeAPI, url.Values{"method": {"password"}, "password_identifier": {id2mail}, "password": {"foobar"}, "csrf_token": {x.FakeCSRFToken}})
|
||||
vv := testhelpers.EncodeFormAsJSON(t, tt == flow.TypeAPI, url.Values{"method": {"password"}, "password_identifier": {id2mail}, "password": {"foobar"}, "csrf_token": {nosurfx.FakeCSRFToken}})
|
||||
|
||||
req, err := http.NewRequest("POST", ts.URL+login.RouteSubmitFlow+"?flow="+f.ID.String(), strings.NewReader(vv))
|
||||
require.NoError(t, err)
|
||||
|
|
@ -284,7 +286,7 @@ func TestFlowLifecycle(t *testing.T) {
|
|||
|
||||
// Submit Login
|
||||
hc := testhelpers.NewClientWithCookies(t)
|
||||
res, err := hc.PostForm(ts.URL+login.RouteSubmitFlow+"?flow="+f.ID.String(), url.Values{"method": {"password"}, "password_identifier": {id1mail}, "password": {"foobar"}, "csrf_token": {x.FakeCSRFToken}})
|
||||
res, err := hc.PostForm(ts.URL+login.RouteSubmitFlow+"?flow="+f.ID.String(), url.Values{"method": {"password"}, "password_identifier": {id1mail}, "password": {"foobar"}, "csrf_token": {nosurfx.FakeCSRFToken}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check response and session cookie presence
|
||||
|
|
@ -306,7 +308,7 @@ func TestFlowLifecycle(t *testing.T) {
|
|||
f = login.Flow{Type: flow.TypeBrowser, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(), UI: container.New(""), Refresh: true, RequestedAAL: "aal1"}
|
||||
require.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))
|
||||
|
||||
vv := testhelpers.EncodeFormAsJSON(t, false, url.Values{"method": {"password"}, "password_identifier": {id1mail}, "password": {"foobar"}, "csrf_token": {x.FakeCSRFToken}})
|
||||
vv := testhelpers.EncodeFormAsJSON(t, false, url.Values{"method": {"password"}, "password_identifier": {id1mail}, "password": {"foobar"}, "csrf_token": {nosurfx.FakeCSRFToken}})
|
||||
|
||||
req, err = http.NewRequest("POST", ts.URL+login.RouteSubmitFlow+"?flow="+f.ID.String(), strings.NewReader(vv))
|
||||
require.NoError(t, err)
|
||||
|
|
@ -843,7 +845,7 @@ func TestGetFlow(t *testing.T) {
|
|||
setupLoginUI(t, client)
|
||||
body := testhelpers.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow)
|
||||
|
||||
assert.EqualValues(t, x.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
assert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
})
|
||||
|
||||
t.Run("case=expired", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
|
|
@ -37,8 +40,8 @@ type (
|
|||
}
|
||||
|
||||
HooksProvider interface {
|
||||
PreLoginHooks(ctx context.Context) []PreHookExecutor
|
||||
PostLoginHooks(ctx context.Context, credentialsType identity.CredentialsType) []PostHookExecutor
|
||||
PreLoginHooks(ctx context.Context) ([]PreHookExecutor, error)
|
||||
PostLoginHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]PostHookExecutor, error)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -50,7 +53,7 @@ type (
|
|||
identity.ManagementProvider
|
||||
session.ManagementProvider
|
||||
session.PersistenceProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.TracingProvider
|
||||
|
|
@ -148,13 +151,13 @@ func (e *HookExecutor) PostLoginHook(
|
|||
|
||||
c := e.d.Config()
|
||||
// Verify the redirect URL before we do any other processing.
|
||||
returnTo, err := x.SecureRedirectTo(r,
|
||||
returnTo, err := redir.SecureRedirectTo(r,
|
||||
c.SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
x.SecureRedirectReturnTo(f.ReturnTo),
|
||||
x.SecureRedirectUseSourceURL(f.RequestURL),
|
||||
x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),
|
||||
x.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowLoginReturnTo(ctx, f.Active.String())),
|
||||
redir.SecureRedirectReturnTo(f.ReturnTo),
|
||||
redir.SecureRedirectUseSourceURL(f.RequestURL),
|
||||
redir.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowLoginReturnTo(ctx, f.Active.String())),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -177,14 +180,18 @@ func (e *HookExecutor) PostLoginHook(
|
|||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", f.Active).
|
||||
Debug("Running ExecuteLoginPostHook.")
|
||||
for k, executor := range e.d.PostLoginHooks(ctx, f.Active) {
|
||||
hooks, err := e.d.PostLoginHooks(ctx, f.Active)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, executor := range hooks {
|
||||
if err := executor.ExecuteLoginPostHook(w, r, g, f, s); err != nil {
|
||||
if errors.Is(err, ErrHookAbortFlow) {
|
||||
e.d.Logger().
|
||||
WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", PostHookExecutorNames(e.d.PostLoginHooks(ctx, f.Active))).
|
||||
WithField("executors", PostHookExecutorNames(hooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", f.Active).
|
||||
Debug("A ExecuteLoginPostHook hook aborted early.")
|
||||
|
|
@ -200,7 +207,7 @@ func (e *HookExecutor) PostLoginHook(
|
|||
WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", PostHookExecutorNames(e.d.PostLoginHooks(ctx, f.Active))).
|
||||
WithField("executors", PostHookExecutorNames(hooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", f.Active).
|
||||
Debug("ExecuteLoginPostHook completed successfully.")
|
||||
|
|
@ -377,13 +384,17 @@ func (e *HookExecutor) PostLoginHook(
|
|||
span.SetAttributes(attribute.String("redirect_reason", "verification requested"))
|
||||
}
|
||||
|
||||
x.ContentNegotiationRedirection(w, r, s, e.d.Writer(), finalReturnTo)
|
||||
redir.ContentNegotiationRedirection(w, r, s, e.d.Writer(), finalReturnTo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *HookExecutor) PreLoginHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
|
||||
for _, executor := range e.d.PreLoginHooks(r.Context()) {
|
||||
if err := executor.ExecuteLoginPreHook(w, r, a); err != nil {
|
||||
hooks, err := e.d.PreLoginHooks(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, h := range hooks {
|
||||
if err := h.ExecuteLoginPreHook(w, r, a); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
|
@ -36,7 +39,7 @@ const (
|
|||
type (
|
||||
handlerDependencies interface {
|
||||
x.WriterProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
session.ManagementProvider
|
||||
session.PersistenceProvider
|
||||
errorx.ManagementProvider
|
||||
|
|
@ -67,9 +70,9 @@ func (h *Handler) RegisterPublicRoutes(router *x.RouterPublic) {
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
admin.GET(RouteInitBrowserFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.DELETE(RouteAPIFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.DELETE(RouteAPIFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
}
|
||||
|
||||
// Logout Flow
|
||||
|
|
@ -153,11 +156,11 @@ func (h *Handler) createBrowserLogoutFlow(w http.ResponseWriter, r *http.Request
|
|||
|
||||
if requestURL.Query().Get("return_to") != "" {
|
||||
// Pre-validate the return to URL which is contained in the HTTP request.
|
||||
returnTo, err = x.SecureRedirectTo(r,
|
||||
returnTo, err = redir.SecureRedirectTo(r,
|
||||
h.d.Config().SelfServiceFlowLogoutRedirectURL(r.Context()),
|
||||
x.SecureRedirectUseSourceURL(requestURL.String()),
|
||||
x.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
x.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
redir.SecureRedirectUseSourceURL(requestURL.String()),
|
||||
redir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
)
|
||||
if err != nil {
|
||||
h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)
|
||||
|
|
@ -354,10 +357,10 @@ func (h *Handler) updateLogoutFlow(w http.ResponseWriter, r *http.Request, ps ht
|
|||
func (h *Handler) completeLogout(w http.ResponseWriter, r *http.Request) {
|
||||
_ = h.d.CSRFHandler().RegenerateToken(w, r)
|
||||
|
||||
ret, err := x.SecureRedirectTo(r, h.d.Config().SelfServiceFlowLogoutRedirectURL(r.Context()),
|
||||
x.SecureRedirectUseSourceURL(r.RequestURI),
|
||||
x.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
x.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(r.Context())),
|
||||
ret, err := redir.SecureRedirectTo(r, h.d.Config().SelfServiceFlowLogoutRedirectURL(r.Context()),
|
||||
redir.SecureRedirectUseSourceURL(r.RequestURI),
|
||||
redir.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(r.Context())),
|
||||
)
|
||||
if err != nil {
|
||||
h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/session"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
|
@ -121,7 +123,7 @@ func TestLogout(t *testing.T) {
|
|||
defer res.Body.Close()
|
||||
assert.EqualValues(t, http.StatusForbidden, res.StatusCode)
|
||||
body := x.MustReadAll(res.Body)
|
||||
assert.EqualValues(t, x.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
assert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
}
|
||||
|
||||
t.Run("type=browser", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ package flow
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
)
|
||||
|
||||
func GetCSRFToken(reg interface {
|
||||
x.CSRFProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
}, w http.ResponseWriter, r *http.Request, p Type) string {
|
||||
token := reg.GenerateCSRFToken(r)
|
||||
if p != TypeBrowser {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
|
@ -39,7 +41,7 @@ type (
|
|||
errorx.ManagementProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
config.Provider
|
||||
StrategyProvider
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/ory/x/ioutilx"
|
||||
|
|
@ -74,7 +76,7 @@ func TestHandleError(t *testing.T) {
|
|||
req := &http.Request{URL: urlx.ParseOrPanic("/")}
|
||||
s, err := reg.GetActiveRecoveryStrategy(context.Background())
|
||||
require.NoError(t, err)
|
||||
f, err := recovery.NewFlow(conf, ttl, x.FakeCSRFToken, req, s, ft)
|
||||
f, err := recovery.NewFlow(conf, ttl, nosurfx.FakeCSRFToken, req, s, ft)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(context.Background(), f))
|
||||
f, err = reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), f.ID)
|
||||
|
|
@ -332,7 +334,7 @@ func TestHandleError_WithContinueWith(t *testing.T) {
|
|||
req := &http.Request{URL: urlx.ParseOrPanic("/")}
|
||||
s, err := reg.GetActiveRecoveryStrategy(context.Background())
|
||||
require.NoError(t, err)
|
||||
f, err := recovery.NewFlow(conf, ttl, x.FakeCSRFToken, req, s, ft)
|
||||
f, err := recovery.NewFlow(conf, ttl, nosurfx.FakeCSRFToken, req, s, ft)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(context.Background(), f))
|
||||
f, err = reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), f.ID)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
|
@ -118,11 +120,11 @@ func NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Reques
|
|||
|
||||
// Pre-validate the return to URL which is contained in the HTTP request.
|
||||
requestURL := x.RequestURL(r).String()
|
||||
_, err := x.SecureRedirectTo(r,
|
||||
_, err := redir.SecureRedirectTo(r,
|
||||
conf.SelfServiceBrowserDefaultReturnTo(r.Context()),
|
||||
x.SecureRedirectUseSourceURL(requestURL),
|
||||
x.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
x.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
redir.SecureRedirectUseSourceURL(requestURL),
|
||||
redir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/ory/nosurf"
|
||||
|
||||
"github.com/ory/kratos/schema"
|
||||
|
|
@ -49,9 +52,9 @@ type (
|
|||
session.HandlerProvider
|
||||
StrategyProvider
|
||||
FlowPersistenceProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.WriterProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
config.Provider
|
||||
ErrorHandlerProvider
|
||||
HookExecutorProvider
|
||||
|
|
@ -88,11 +91,11 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
admin.GET(RouteInitBrowserFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.POST(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
}
|
||||
|
||||
// swagger:route GET /self-service/recovery/api frontend createNativeRecoveryFlow
|
||||
|
|
@ -291,7 +294,7 @@ func (h *Handler) getRecoveryFlow(w http.ResponseWriter, r *http.Request, _ http
|
|||
//
|
||||
// Resolves: https://github.com/ory/kratos/issues/1282
|
||||
if f.Type.IsBrowser() && !f.DangerousSkipCSRFCheck && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), f.CSRFToken) {
|
||||
h.d.Writer().WriteError(w, r, x.CSRFErrorReason(r, h.d))
|
||||
h.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -299,7 +302,7 @@ func (h *Handler) getRecoveryFlow(w http.ResponseWriter, r *http.Request, _ http
|
|||
if f.Type == flow.TypeBrowser {
|
||||
redirectURL := flow.GetFlowExpiredRedirectURL(r.Context(), h.d.Config(), RouteInitBrowserFlow, f.ReturnTo)
|
||||
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.
|
||||
WithReason("The recovery flow has expired. Redirect the user to the recovery flow init endpoint to initialize a new recovery flow.").
|
||||
WithDetail("redirect_to", redirectURL.String()).
|
||||
WithDetail("return_to", f.ReturnTo)))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/ory/kratos/corpx"
|
||||
|
|
@ -228,7 +230,7 @@ func TestGetFlow(t *testing.T) {
|
|||
setupRecoveryTS(t, client)
|
||||
body := testhelpers.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow)
|
||||
|
||||
assert.EqualValues(t, x.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
assert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
})
|
||||
|
||||
t.Run("case=valid", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/ory/kratos/x/events"
|
||||
|
|
@ -32,8 +34,8 @@ type (
|
|||
PostHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error
|
||||
|
||||
HooksProvider interface {
|
||||
PreRecoveryHooks(ctx context.Context) []PreHookExecutor
|
||||
PostRecoveryHooks(ctx context.Context) []PostHookExecutor
|
||||
PreRecoveryHooks(ctx context.Context) ([]PreHookExecutor, error)
|
||||
PostRecoveryHooks(ctx context.Context) ([]PostHookExecutor, error)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -60,7 +62,7 @@ type (
|
|||
identity.ValidationProvider
|
||||
session.PersistenceProvider
|
||||
HooksProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
}
|
||||
|
|
@ -89,7 +91,11 @@ func (e *HookExecutor) PostRecoveryHook(w http.ResponseWriter, r *http.Request,
|
|||
}
|
||||
|
||||
logger.Debug("Running ExecutePostRecoveryHooks.")
|
||||
for k, executor := range e.d.PostRecoveryHooks(r.Context()) {
|
||||
hooks, err := e.d.PostRecoveryHooks(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, executor := range hooks {
|
||||
if err := executor.ExecutePostRecoveryHook(w, r, a, s); err != nil {
|
||||
var traits identity.Traits
|
||||
if s.Identity != nil {
|
||||
|
|
@ -101,7 +107,7 @@ func (e *HookExecutor) PostRecoveryHook(w http.ResponseWriter, r *http.Request,
|
|||
logger.
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", PostHookRecoveryExecutorNames(e.d.PostRecoveryHooks(r.Context()))).
|
||||
WithField("executors", PostHookRecoveryExecutorNames(hooks)).
|
||||
Debug("ExecutePostRecoveryHook completed successfully.")
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +119,11 @@ func (e *HookExecutor) PostRecoveryHook(w http.ResponseWriter, r *http.Request,
|
|||
}
|
||||
|
||||
func (e *HookExecutor) PreRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
|
||||
for _, executor := range e.d.PreRecoveryHooks(r.Context()) {
|
||||
hooks, err := e.d.PreRecoveryHooks(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, executor := range hooks {
|
||||
if err := executor.ExecuteRecoveryPreHook(w, r, a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow/recovery"
|
||||
"github.com/ory/kratos/selfservice/strategy/code"
|
||||
|
||||
|
|
@ -35,7 +37,7 @@ func TestRecoveryExecutor(t *testing.T) {
|
|||
newServer := func(t *testing.T, i *identity.Identity, ft flow.Type) *httptest.Server {
|
||||
router := httprouter.New()
|
||||
router.GET("/recovery/pre", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
a, err := recovery.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, s, ft)
|
||||
a, err := recovery.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, s, ft)
|
||||
require.NoError(t, err)
|
||||
if testhelpers.SelfServiceHookErrorHandler(t, w, r, recovery.ErrHookAbortFlow, reg.RecoveryExecutor().PreRecoveryHook(w, r, a)) {
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
|
|
@ -43,7 +45,7 @@ func TestRecoveryExecutor(t *testing.T) {
|
|||
})
|
||||
|
||||
router.GET("/recovery/post", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
a, err := recovery.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, s, ft)
|
||||
a, err := recovery.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, s, ft)
|
||||
require.NoError(t, err)
|
||||
s, err := testhelpers.NewActiveSession(r,
|
||||
reg,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
|
@ -135,11 +137,11 @@ func NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Reques
|
|||
|
||||
// Pre-validate the return to URL which is contained in the HTTP request.
|
||||
requestURL := x.RequestURL(r).String()
|
||||
_, err := x.SecureRedirectTo(r,
|
||||
_, err := redir.SecureRedirectTo(r,
|
||||
conf.SelfServiceBrowserDefaultReturnTo(r.Context()),
|
||||
x.SecureRedirectUseSourceURL(requestURL),
|
||||
x.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
x.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
redir.SecureRedirectUseSourceURL(requestURL),
|
||||
redir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -250,13 +252,13 @@ func (f *Flow) ContinueWith() []flow.ContinueWith {
|
|||
return f.ContinueWithItems
|
||||
}
|
||||
|
||||
func (f *Flow) SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []x.SecureRedirectOption) {
|
||||
return []x.SecureRedirectOption{
|
||||
x.SecureRedirectReturnTo(f.ReturnTo),
|
||||
x.SecureRedirectUseSourceURL(f.RequestURL),
|
||||
x.SecureRedirectAllowURLs(cfg.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
x.SecureRedirectAllowSelfServiceURLs(cfg.Config().SelfPublicURL(ctx)),
|
||||
x.SecureRedirectOverrideDefaultReturnTo(cfg.Config().SelfServiceFlowRegistrationReturnTo(ctx, f.Active.String())),
|
||||
func (f *Flow) SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []redir.SecureRedirectOption) {
|
||||
return []redir.SecureRedirectOption{
|
||||
redir.SecureRedirectReturnTo(f.ReturnTo),
|
||||
redir.SecureRedirectUseSourceURL(f.RequestURL),
|
||||
redir.SecureRedirectAllowURLs(cfg.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(cfg.Config().SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectOverrideDefaultReturnTo(cfg.Config().SelfServiceFlowRegistrationReturnTo(ctx, f.Active.String())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
|
@ -49,8 +52,8 @@ type (
|
|||
session.HandlerProvider
|
||||
session.ManagementProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
StrategyProvider
|
||||
HookExecutorProvider
|
||||
FlowPersistenceProvider
|
||||
|
|
@ -94,11 +97,11 @@ func (h *Handler) onAuthenticated(w http.ResponseWriter, r *http.Request, ps htt
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
admin.GET(RouteInitBrowserFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.POST(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
}
|
||||
|
||||
type FlowOption func(f *Flow)
|
||||
|
|
@ -412,9 +415,9 @@ func (h *Handler) createBrowserRegistrationFlow(w http.ResponseWriter, r *http.R
|
|||
return
|
||||
}
|
||||
|
||||
returnTo, redirErr := x.SecureRedirectTo(r, h.d.Config().SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
x.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(ctx)),
|
||||
x.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
returnTo, redirErr := redir.SecureRedirectTo(r, h.d.Config().SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
)
|
||||
if redirErr != nil {
|
||||
h.d.SelfServiceErrorManager().Forward(ctx, w, r, redirErr)
|
||||
|
|
@ -510,7 +513,7 @@ func (h *Handler) getRegistrationFlow(w http.ResponseWriter, r *http.Request, ps
|
|||
//
|
||||
// Resolves: https://github.com/ory/kratos/issues/1282
|
||||
if ar.Type == flow.TypeBrowser && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), ar.CSRFToken) {
|
||||
h.d.Writer().WriteError(w, r, x.CSRFErrorReason(r, h.d))
|
||||
h.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -518,13 +521,13 @@ func (h *Handler) getRegistrationFlow(w http.ResponseWriter, r *http.Request, ps
|
|||
if ar.Type == flow.TypeBrowser {
|
||||
redirectURL := flow.GetFlowExpiredRedirectURL(r.Context(), h.d.Config(), RouteInitBrowserFlow, ar.ReturnTo)
|
||||
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
WithReason("The registration flow has expired. Redirect the user to the registration flow init endpoint to initialize a new registration flow.").
|
||||
WithDetail("redirect_to", redirectURL.String()).
|
||||
WithDetail("return_to", ar.ReturnTo)))
|
||||
return
|
||||
}
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).
|
||||
WithReason("The registration flow has expired. Call the registration flow init API endpoint to initialize a new registration flow.").
|
||||
WithDetail("api", urlx.AppendPaths(h.d.Config().SelfPublicURL(r.Context()), RouteInitAPIFlow).String())))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/go-faker/faker/v4"
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
|
|
@ -355,7 +357,7 @@ func TestGetFlow(t *testing.T) {
|
|||
_ = setupRegistrationUI(t, client)
|
||||
body := testhelpers.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow)
|
||||
|
||||
assert.EqualValues(t, x.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
assert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
})
|
||||
|
||||
t.Run("case=expired", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
|
|
@ -42,9 +45,9 @@ type (
|
|||
PostHookPrePersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error
|
||||
|
||||
HooksProvider interface {
|
||||
PreRegistrationHooks(ctx context.Context) []PreHookExecutor
|
||||
PostRegistrationPrePersistHooks(ctx context.Context, credentialsType identity.CredentialsType) []PostHookPrePersistExecutor
|
||||
PostRegistrationPostPersistHooks(ctx context.Context, credentialsType identity.CredentialsType) []PostHookPostPersistExecutor
|
||||
PreRegistrationHooks(ctx context.Context) ([]PreHookExecutor, error)
|
||||
PostRegistrationPrePersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]PostHookPrePersistExecutor, error)
|
||||
PostRegistrationPostPersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]PostHookPostPersistExecutor, error)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -81,7 +84,7 @@ type (
|
|||
HooksProvider
|
||||
FlowPersistenceProvider
|
||||
hydra.Provider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.HTTPClientProvider
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
|
|
@ -111,14 +114,18 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
Debug("Running PostRegistrationPrePersistHooks.")
|
||||
for k, executor := range e.d.PostRegistrationPrePersistHooks(ctx, ct) {
|
||||
preHooks, err := e.d.PostRegistrationPrePersistHooks(ctx, ct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, executor := range preHooks {
|
||||
if err := executor.ExecutePostRegistrationPrePersistHook(w, r, registrationFlow, i); err != nil {
|
||||
if errors.Is(err, ErrHookAbortFlow) {
|
||||
e.d.Logger().
|
||||
WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(ctx, ct))).
|
||||
WithField("executors", ExecutorNames(preHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
Debug("A ExecutePostRegistrationPrePersistHook hook aborted early.")
|
||||
|
|
@ -129,7 +136,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(ctx, ct))).
|
||||
WithField("executors", ExecutorNames(preHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
WithError(err).
|
||||
|
|
@ -142,7 +149,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
e.d.Logger().WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(ctx, ct))).
|
||||
WithField("executors", ExecutorNames(preHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
Debug("ExecutePostRegistrationPrePersistHook completed successfully.")
|
||||
|
|
@ -187,12 +194,12 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
|
||||
// Verify the redirect URL before we do any other processing.
|
||||
c := e.d.Config()
|
||||
returnTo, err := x.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
x.SecureRedirectReturnTo(registrationFlow.ReturnTo),
|
||||
x.SecureRedirectUseSourceURL(registrationFlow.RequestURL),
|
||||
x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),
|
||||
x.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowRegistrationReturnTo(ctx, ct.String())),
|
||||
returnTo, err := redir.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
redir.SecureRedirectReturnTo(registrationFlow.ReturnTo),
|
||||
redir.SecureRedirectUseSourceURL(registrationFlow.RequestURL),
|
||||
redir.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowRegistrationReturnTo(ctx, ct.String())),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -232,14 +239,18 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
Debug("Running PostRegistrationPostPersistHooks.")
|
||||
for k, executor := range e.d.PostRegistrationPostPersistHooks(ctx, ct) {
|
||||
postHooks, err := e.d.PostRegistrationPostPersistHooks(ctx, ct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, executor := range postHooks {
|
||||
if err := executor.ExecutePostRegistrationPostPersistHook(w, r, registrationFlow, s); err != nil {
|
||||
if errors.Is(err, ErrHookAbortFlow) {
|
||||
e.d.Logger().
|
||||
WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(ctx, ct))).
|
||||
WithField("executors", ExecutorNames(postHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
Debug("A ExecutePostRegistrationPostPersistHook hook aborted early.")
|
||||
|
|
@ -253,7 +264,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(ctx, ct))).
|
||||
WithField("executors", ExecutorNames(postHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
WithError(err).
|
||||
|
|
@ -268,7 +279,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
e.d.Logger().WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(ctx, ct))).
|
||||
WithField("executors", ExecutorNames(postHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", ct).
|
||||
Debug("ExecutePostRegistrationPostPersistHook completed successfully.")
|
||||
|
|
@ -325,7 +336,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
span.SetAttributes(attribute.String("return_to", finalReturnTo))
|
||||
|
||||
x.ContentNegotiationRedirection(w, r, s.Declassified(), e.d.Writer(), finalReturnTo)
|
||||
redir.ContentNegotiationRedirection(w, r, s.Declassified(), e.d.Writer(), finalReturnTo)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +349,11 @@ func (e *HookExecutor) getDuplicateIdentifier(ctx context.Context, i *identity.I
|
|||
}
|
||||
|
||||
func (e *HookExecutor) PreRegistrationHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
|
||||
for _, executor := range e.d.PreRegistrationHooks(r.Context()) {
|
||||
hooks, err := e.d.PreRegistrationHooks(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, executor := range hooks {
|
||||
if err := executor.ExecuteRegistrationPreHook(w, r, a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gobuffalo/httptest"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
|
@ -49,7 +51,7 @@ func TestRegistrationExecutor(t *testing.T) {
|
|||
|
||||
handleErr := testhelpers.SelfServiceHookRegistrationErrorHandler
|
||||
router.GET("/registration/pre", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
f, err := registration.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, ft)
|
||||
f, err := registration.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, ft)
|
||||
require.NoError(t, err)
|
||||
if handleErr(t, w, r, reg.RegistrationHookExecutor().PreRegistrationHook(w, r, f)) {
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
|
|
@ -60,7 +62,7 @@ func TestRegistrationExecutor(t *testing.T) {
|
|||
if i == nil {
|
||||
i = testhelpers.SelfServiceHookFakeIdentity(t)
|
||||
}
|
||||
regFlow, err := registration.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, ft)
|
||||
regFlow, err := registration.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, ft)
|
||||
require.NoError(t, err)
|
||||
regFlow.RequestURL = x.RequestURL(r).String()
|
||||
for _, callback := range flowCallbacks {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
|
|
@ -19,7 +21,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/nosurf"
|
||||
)
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ func EnsureCSRF(
|
|||
return nil
|
||||
default:
|
||||
if !nosurf.VerifyToken(generator(r), actual) {
|
||||
return errors.WithStack(x.CSRFErrorReason(r, reg))
|
||||
return errors.WithStack(nosurfx.CSRFErrorReason(r, reg))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
|
|
@ -20,25 +22,23 @@ import (
|
|||
"github.com/ory/kratos/selfservice/flow"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ory/kratos/x"
|
||||
)
|
||||
|
||||
func TestVerifyRequest(t *testing.T) {
|
||||
_, reg := internal.NewFastRegistryWithMocks(t)
|
||||
require.EqualError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeBrowser, false, x.FakeCSRFTokenGenerator, "not_csrf_token"), x.ErrInvalidCSRFToken.Error())
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeBrowser, false, x.FakeCSRFTokenGenerator, x.FakeCSRFToken), nil)
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""))
|
||||
require.EqualError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeBrowser, false, nosurfx.FakeCSRFTokenGenerator, "not_csrf_token"), nosurfx.ErrInvalidCSRFToken.Error())
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeBrowser, false, nosurfx.FakeCSRFTokenGenerator, nosurfx.FakeCSRFToken), nil)
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""))
|
||||
require.EqualError(t, flow.EnsureCSRF(reg, &http.Request{
|
||||
Header: http.Header{"Origin": {"https://www.ory.sh"}},
|
||||
}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""), flow.ErrOriginHeaderNeedsBrowserFlow.Error())
|
||||
}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""), flow.ErrOriginHeaderNeedsBrowserFlow.Error())
|
||||
require.EqualError(t, flow.EnsureCSRF(reg, &http.Request{
|
||||
Header: http.Header{"Cookie": {"cookie=ory"}},
|
||||
}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), "should error because of cookie=ory")
|
||||
}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), "should error because of cookie=ory")
|
||||
|
||||
err := flow.EnsureCSRF(reg, &http.Request{
|
||||
Header: http.Header{"Cookie": {"cookie1=cookievalue", "cookie2=cookievalue"}},
|
||||
}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, "")
|
||||
}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, "")
|
||||
var he herodot.DetailsCarrier
|
||||
require.ErrorAs(t, err, &he)
|
||||
cs, ok := he.Details()["found cookies"].([]string)
|
||||
|
|
@ -48,17 +48,17 @@ func TestVerifyRequest(t *testing.T) {
|
|||
// Cloudflare
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{
|
||||
Header: http.Header{"Cookie": {"__cflb=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7", "_cfuvid=blub", "cf_clearance=bla"}},
|
||||
}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""), "should ignore Cloudflare cookies")
|
||||
}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""), "should ignore Cloudflare cookies")
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{
|
||||
Header: http.Header{"Cookie": {"__cflb=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7; __cfruid=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7"}},
|
||||
}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""), "should ignore Cloudflare cookies")
|
||||
}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""), "should ignore Cloudflare cookies")
|
||||
require.EqualError(t, flow.EnsureCSRF(reg, &http.Request{
|
||||
Header: http.Header{"Cookie": {"__cflb=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7; __cfruid=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7; some_cookie=some_value"}},
|
||||
}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), "should error because of some_cookie")
|
||||
}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), "should error because of some_cookie")
|
||||
require.EqualError(t, flow.EnsureCSRF(reg, &http.Request{
|
||||
Header: http.Header{"Cookie": {"some_cookie=some_value"}},
|
||||
}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), "should error because of some_cookie")
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeAPI, false, x.FakeCSRFTokenGenerator, ""), "no cookie, no error")
|
||||
}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), "should error because of some_cookie")
|
||||
require.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, ""), "no cookie, no error")
|
||||
}
|
||||
|
||||
func TestMethodEnabledAndAllowed(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
|
||||
"github.com/ory/kratos/text"
|
||||
|
|
@ -142,11 +144,11 @@ func NewFlow(conf *config.Config, exp time.Duration, r *http.Request, i *identit
|
|||
|
||||
// Pre-validate the return to URL which is contained in the HTTP request.
|
||||
requestURL := x.RequestURL(r).String()
|
||||
_, err := x.SecureRedirectTo(r,
|
||||
_, err := redir.SecureRedirectTo(r,
|
||||
conf.SelfServiceBrowserDefaultReturnTo(r.Context()),
|
||||
x.SecureRedirectUseSourceURL(requestURL),
|
||||
x.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
x.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
redir.SecureRedirectUseSourceURL(requestURL),
|
||||
redir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/ory/x/otelx"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
|
@ -48,7 +51,7 @@ func ContinuityKey(id string) string {
|
|||
|
||||
type (
|
||||
handlerDependencies interface {
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.TracingProvider
|
||||
|
|
@ -70,7 +73,7 @@ type (
|
|||
FlowPersistenceProvider
|
||||
StrategyProvider
|
||||
HookExecutorProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
|
||||
schema.IdentitySchemaProvider
|
||||
|
||||
|
|
@ -81,7 +84,7 @@ type (
|
|||
}
|
||||
Handler struct {
|
||||
d handlerDependencies
|
||||
csrf x.CSRFToken
|
||||
csrf nosurfx.CSRFToken
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -98,7 +101,7 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
h.d.Writer().WriteError(w, r, session.NewErrNoActiveSessionFound())
|
||||
} else {
|
||||
loginFlowUrl := h.d.Config().SelfPublicURL(r.Context()).JoinPath(login.RouteInitBrowserFlow).String()
|
||||
redirectUrl, err := x.TakeOverReturnToParameter(r.URL.String(), loginFlowUrl)
|
||||
redirectUrl, err := redir.TakeOverReturnToParameter(r.URL.String(), loginFlowUrl)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, h.d.Config().SelfServiceFlowLoginUI(r.Context()).String(), http.StatusSeeOther)
|
||||
} else {
|
||||
|
|
@ -115,13 +118,13 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
admin.GET(RouteInitBrowserFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))
|
||||
|
||||
admin.GET(RouteInitAPIFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))
|
||||
|
||||
admin.POST(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
}
|
||||
|
||||
func (h *Handler) NewFlow(ctx context.Context, w http.ResponseWriter, r *http.Request, i *identity.Identity, ft flow.Type) (_ *Flow, err error) {
|
||||
|
|
@ -437,13 +440,13 @@ func (h *Handler) getSettingsFlow(w http.ResponseWriter, r *http.Request, _ http
|
|||
if pr.Type == flow.TypeBrowser {
|
||||
redirectURL := flow.GetFlowExpiredRedirectURL(ctx, h.d.Config(), RouteInitBrowserFlow, pr.ReturnTo)
|
||||
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.
|
||||
WithReason("The settings flow has expired. Redirect the user to the settings flow init endpoint to initialize a new settings flow.").
|
||||
WithDetail("redirect_to", redirectURL.String()).
|
||||
WithDetail("return_to", pr.ReturnTo)))
|
||||
return
|
||||
}
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.
|
||||
WithReason("The settings flow has expired. Call the settings flow init API endpoint to initialize a new settings flow.").
|
||||
WithDetail("api", urlx.AppendPaths(h.d.Config().SelfPublicURL(ctx), RouteInitAPIFlow).String())))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/text"
|
||||
|
||||
"github.com/ory/x/assertx"
|
||||
|
|
@ -579,7 +581,7 @@ func TestHandler(t *testing.T) {
|
|||
var f kratos.SettingsFlow
|
||||
require.NoError(t, json.Unmarshal(body, &f))
|
||||
|
||||
actual, res := testhelpers.SettingsMakeRequest(t, false, true, &f, primaryUser, fmt.Sprintf(`{"method":"profile", "numby": 15, "csrf_token": "%s"}`, x.FakeCSRFToken))
|
||||
actual, res := testhelpers.SettingsMakeRequest(t, false, true, &f, primaryUser, fmt.Sprintf(`{"method":"profile", "numby": 15, "csrf_token": "%s"}`, nosurfx.FakeCSRFToken))
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.Len(t, primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL+login.RouteGetFlow)), 1)
|
||||
require.Contains(t, fmt.Sprintf("%v", primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL))), "ory_kratos_session")
|
||||
|
|
@ -591,7 +593,7 @@ func TestHandler(t *testing.T) {
|
|||
var f kratos.SettingsFlow
|
||||
require.NoError(t, json.Unmarshal(body, &f))
|
||||
|
||||
actual, res := testhelpers.SettingsMakeRequest(t, false, false, &f, primaryUser, `method=profile&traits.numby=15&csrf_token=`+x.FakeCSRFToken)
|
||||
actual, res := testhelpers.SettingsMakeRequest(t, false, false, &f, primaryUser, `method=profile&traits.numby=15&csrf_token=`+nosurfx.FakeCSRFToken)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.Len(t, primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL+login.RouteGetFlow)), 1)
|
||||
require.Contains(t, fmt.Sprintf("%v", primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL))), "ory_kratos_session")
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/ory/x/otelx"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
|
@ -51,9 +54,9 @@ type (
|
|||
PostHookPostPersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, id *identity.Identity, s *session.Session) error
|
||||
|
||||
HooksProvider interface {
|
||||
PreSettingsHooks(ctx context.Context) []PreHookExecutor
|
||||
PostSettingsPrePersistHooks(ctx context.Context, settingsType string) []PostHookPrePersistExecutor
|
||||
PostSettingsPostPersistHooks(ctx context.Context, settingsType string) []PostHookPostPersistExecutor
|
||||
PreSettingsHooks(ctx context.Context) ([]PreHookExecutor, error)
|
||||
PostSettingsPrePersistHooks(ctx context.Context, settingsType string) ([]PostHookPrePersistExecutor, error)
|
||||
PostSettingsPostPersistHooks(ctx context.Context, settingsType string) ([]PostHookPostPersistExecutor, error)
|
||||
}
|
||||
|
||||
executorDependencies interface {
|
||||
|
|
@ -66,7 +69,7 @@ type (
|
|||
HooksProvider
|
||||
FlowPersistenceProvider
|
||||
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
x.TracingProvider
|
||||
|
|
@ -166,11 +169,11 @@ func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWrit
|
|||
|
||||
// Verify the redirect URL before we do any other processing.
|
||||
c := e.d.Config()
|
||||
returnTo, err := x.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
x.SecureRedirectUseSourceURL(ctxUpdate.Flow.RequestURL),
|
||||
x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),
|
||||
x.SecureRedirectOverrideDefaultReturnTo(
|
||||
returnTo, err := redir.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx),
|
||||
redir.SecureRedirectUseSourceURL(ctxUpdate.Flow.RequestURL),
|
||||
redir.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectOverrideDefaultReturnTo(
|
||||
e.d.Config().SelfServiceFlowSettingsReturnTo(ctx, settingsType,
|
||||
ctxUpdate.Flow.AppendTo(e.d.Config().SelfServiceFlowSettingsUI(ctx)))),
|
||||
)
|
||||
|
|
@ -183,11 +186,15 @@ func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWrit
|
|||
f(hookOptions)
|
||||
}
|
||||
|
||||
for k, executor := range e.d.PostSettingsPrePersistHooks(ctx, settingsType) {
|
||||
preHooks, err := e.d.PostSettingsPrePersistHooks(ctx, settingsType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, executor := range preHooks {
|
||||
logFields := logrus.Fields{
|
||||
"executor": fmt.Sprintf("%T", executor),
|
||||
"executor_position": k,
|
||||
"executors": PostHookPrePersistExecutorNames(e.d.PostSettingsPrePersistHooks(ctx, settingsType)),
|
||||
"executors": PostHookPrePersistExecutorNames(preHooks),
|
||||
"identity_id": i.ID,
|
||||
"flow_method": settingsType,
|
||||
}
|
||||
|
|
@ -253,14 +260,18 @@ func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWrit
|
|||
return err
|
||||
}
|
||||
|
||||
for k, executor := range e.d.PostSettingsPostPersistHooks(ctx, settingsType) {
|
||||
postHooks, err := e.d.PostSettingsPostPersistHooks(ctx, settingsType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, executor := range postHooks {
|
||||
if err := executor.ExecuteSettingsPostPersistHook(w, r, ctxUpdate.Flow, i, ctxUpdate.Session); err != nil {
|
||||
if errors.Is(err, ErrHookAbortFlow) {
|
||||
e.d.Logger().
|
||||
WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(ctx, settingsType))).
|
||||
WithField("executors", PostHookPostPersistExecutorNames(postHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", settingsType).
|
||||
Debug("A ExecuteSettingsPostPersistHook hook aborted early.")
|
||||
|
|
@ -272,7 +283,7 @@ func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWrit
|
|||
e.d.Logger().WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(ctx, settingsType))).
|
||||
WithField("executors", PostHookPostPersistExecutorNames(postHooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
WithField("flow_method", settingsType).
|
||||
Debug("ExecuteSettingsPostPersistHook completed successfully.")
|
||||
|
|
@ -318,7 +329,7 @@ func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
x.ContentNegotiationRedirection(w, r, i.CopyWithoutCredentials(), e.d.Writer(), returnTo.String())
|
||||
redir.ContentNegotiationRedirection(w, r, i.CopyWithoutCredentials(), e.d.Writer(), returnTo.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -326,7 +337,11 @@ func (e *HookExecutor) PreSettingsHook(ctx context.Context, w http.ResponseWrite
|
|||
ctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.flow.settings.HookExecutor.PreSettingsHook")
|
||||
defer otelx.End(span, &err)
|
||||
|
||||
for _, executor := range e.d.PreSettingsHooks(ctx) {
|
||||
hooks, err := e.d.PreSettingsHooks(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, executor := range hooks {
|
||||
if err := executor.ExecuteSettingsPreHook(w, r, a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
|
@ -35,8 +37,8 @@ type (
|
|||
errorx.ManagementProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.CSRFProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
config.Provider
|
||||
FlowPersistenceProvider
|
||||
StrategyProvider
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/ory/x/jsonx"
|
||||
|
|
@ -72,7 +74,7 @@ func TestHandleError(t *testing.T) {
|
|||
req := &http.Request{URL: urlx.ParseOrPanic("/")}
|
||||
strategy, err := reg.GetActiveVerificationStrategy(context.Background())
|
||||
require.NoError(t, err)
|
||||
f, err := verification.NewFlow(conf, ttl, x.FakeCSRFToken, req, strategy, ft)
|
||||
f, err := verification.NewFlow(conf, ttl, nosurfx.FakeCSRFToken, req, strategy, ft)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(context.Background(), f))
|
||||
f, err = reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), f.ID)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
|
@ -131,11 +133,11 @@ func NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Reques
|
|||
|
||||
// Pre-validate the return to URL which is contained in the HTTP request.
|
||||
requestURL := x.RequestURL(r).String()
|
||||
_, err := x.SecureRedirectTo(r,
|
||||
_, err := redir.SecureRedirectTo(r,
|
||||
conf.SelfServiceBrowserDefaultReturnTo(r.Context()),
|
||||
x.SecureRedirectUseSourceURL(requestURL),
|
||||
x.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
x.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
redir.SecureRedirectUseSourceURL(requestURL),
|
||||
redir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),
|
||||
redir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -272,9 +274,9 @@ func (f *Flow) ContinueURL(ctx context.Context, config *config.Config) *url.URL
|
|||
|
||||
verificationRequest := http.Request{URL: verificationRequestURL}
|
||||
|
||||
returnTo, err := x.SecureRedirectTo(&verificationRequest, flowContinueURL,
|
||||
x.SecureRedirectAllowSelfServiceURLs(config.SelfPublicURL(ctx)),
|
||||
x.SecureRedirectAllowURLs(config.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
returnTo, err := redir.SecureRedirectTo(&verificationRequest, flowContinueURL,
|
||||
redir.SecureRedirectAllowSelfServiceURLs(config.SelfPublicURL(ctx)),
|
||||
redir.SecureRedirectAllowURLs(config.SelfServiceBrowserAllowedReturnToDomains(ctx)),
|
||||
)
|
||||
if err != nil {
|
||||
// an error occured return flow default, or global default return URL
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/ory/kratos/hydra"
|
||||
"github.com/ory/kratos/session"
|
||||
"github.com/ory/nosurf"
|
||||
|
|
@ -50,9 +53,9 @@ type (
|
|||
session.PersistenceProvider
|
||||
session.ManagementProvider
|
||||
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.WriterProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.LoggingProvider
|
||||
|
||||
FlowPersistenceProvider
|
||||
|
|
@ -82,12 +85,12 @@ func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
|
|||
}
|
||||
|
||||
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
|
||||
admin.GET(RouteInitBrowserFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))
|
||||
|
||||
admin.POST(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
|
||||
admin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
admin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))
|
||||
}
|
||||
|
||||
type FlowOption func(f *Flow)
|
||||
|
|
@ -298,7 +301,7 @@ func (h *Handler) getVerificationFlow(w http.ResponseWriter, r *http.Request, _
|
|||
//
|
||||
// Resolves: https://github.com/ory/kratos/issues/1282
|
||||
if req.Type == flow.TypeBrowser && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), req.CSRFToken) {
|
||||
h.d.Writer().WriteError(w, r, x.CSRFErrorReason(r, h.d))
|
||||
h.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -306,13 +309,13 @@ func (h *Handler) getVerificationFlow(w http.ResponseWriter, r *http.Request, _
|
|||
if req.Type == flow.TypeBrowser {
|
||||
redirectURL := flow.GetFlowExpiredRedirectURL(r.Context(), h.d.Config(), RouteInitBrowserFlow, req.ReturnTo)
|
||||
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.
|
||||
WithReason("The verification flow has expired. Redirect the user to the verification flow init endpoint to initialize a new verification flow.").
|
||||
WithDetail("redirect_to", redirectURL.String()).
|
||||
WithDetail("return_to", req.ReturnTo)))
|
||||
return
|
||||
}
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.
|
||||
h.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.
|
||||
WithReason("The verification flow has expired. Call the verification flow init API endpoint to initialize a new verification flow.").
|
||||
WithDetail("api", urlx.AppendPaths(h.d.Config().SelfPublicURL(r.Context()), RouteInitAPIFlow).String())))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gobuffalo/httptest"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -99,7 +101,7 @@ func TestGetFlow(t *testing.T) {
|
|||
_ = setupVerificationUI(t, client)
|
||||
body := testhelpers.EasyGetBody(t, client, public.URL+verification.RouteInitBrowserFlow)
|
||||
|
||||
assert.EqualValues(t, x.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
assert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, "error.reason").String(), "%s", body)
|
||||
})
|
||||
|
||||
t.Run("case=expired", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/ory/kratos/x/events"
|
||||
|
|
@ -32,8 +34,8 @@ type (
|
|||
PostHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error
|
||||
|
||||
HooksProvider interface {
|
||||
PostVerificationHooks(ctx context.Context) []PostHookExecutor
|
||||
PreVerificationHooks(ctx context.Context) []PreHookExecutor
|
||||
PostVerificationHooks(ctx context.Context) ([]PostHookExecutor, error)
|
||||
PreVerificationHooks(ctx context.Context) ([]PreHookExecutor, error)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -60,7 +62,7 @@ type (
|
|||
identity.ValidationProvider
|
||||
session.PersistenceProvider
|
||||
HooksProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
}
|
||||
|
|
@ -81,7 +83,11 @@ func NewHookExecutor(d executorDependencies) *HookExecutor {
|
|||
}
|
||||
|
||||
func (e *HookExecutor) PreVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
|
||||
for _, executor := range e.d.PreVerificationHooks(r.Context()) {
|
||||
hooks, err := e.d.PreVerificationHooks(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, executor := range hooks {
|
||||
if err := executor.ExecuteVerificationPreHook(w, r, a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -95,19 +101,19 @@ func (e *HookExecutor) PostVerificationHook(w http.ResponseWriter, r *http.Reque
|
|||
WithRequest(r).
|
||||
WithField("identity_id", i.ID).
|
||||
Debug("Running ExecutePostVerificationHooks.")
|
||||
for k, executor := range e.d.PostVerificationHooks(r.Context()) {
|
||||
hooks, err := e.d.PostVerificationHooks(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, executor := range hooks {
|
||||
if err := executor.ExecutePostVerificationHook(w, r, a, i); err != nil {
|
||||
var traits identity.Traits
|
||||
if i != nil {
|
||||
traits = i.Traits
|
||||
}
|
||||
return flow.HandleHookError(w, r, a, traits, node.LinkGroup, err, e.d, e.d)
|
||||
return flow.HandleHookError(w, r, a, i.Traits, node.LinkGroup, err, e.d, e.d)
|
||||
}
|
||||
|
||||
e.d.Logger().WithRequest(r).
|
||||
WithField("executor", fmt.Sprintf("%T", executor)).
|
||||
WithField("executor_position", k).
|
||||
WithField("executors", PostHookVerificationExecutorNames(e.d.PostVerificationHooks(r.Context()))).
|
||||
WithField("executors", PostHookVerificationExecutorNames(hooks)).
|
||||
WithField("identity_id", i.ID).
|
||||
Debug("ExecutePostVerificationHook completed successfully.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow/verification"
|
||||
|
||||
"github.com/gobuffalo/httptest"
|
||||
|
|
@ -34,7 +36,7 @@ func TestVerificationExecutor(t *testing.T) {
|
|||
router.GET("/verification/pre", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
strategy, err := reg.GetActiveVerificationStrategy(r.Context())
|
||||
require.NoError(t, err)
|
||||
a, err := verification.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, strategy, ft)
|
||||
a, err := verification.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, strategy, ft)
|
||||
require.NoError(t, err)
|
||||
if testhelpers.SelfServiceHookErrorHandler(t, w, r, verification.ErrHookAbortFlow, reg.VerificationExecutor().PreVerificationHook(w, r, a)) {
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
|
|
@ -44,7 +46,7 @@ func TestVerificationExecutor(t *testing.T) {
|
|||
router.GET("/verification/post", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
strategy, err := reg.GetActiveVerificationStrategy(r.Context())
|
||||
require.NoError(t, err)
|
||||
a, err := verification.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, strategy, ft)
|
||||
a, err := verification.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, strategy, ft)
|
||||
require.NoError(t, err)
|
||||
a.RequestURL = x.RequestURL(r).String()
|
||||
if testhelpers.SelfServiceHookErrorHandler(t, w, r, verification.ErrHookAbortFlow, reg.VerificationExecutor().PostVerificationHook(w, r, a, i)) {
|
||||
|
|
|
|||
|
|
@ -10,16 +10,18 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tidwall/gjson"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.11.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
grpccodes "google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/request"
|
||||
"github.com/ory/kratos/schema"
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/x/otelx"
|
||||
)
|
||||
|
|
@ -27,25 +29,26 @@ import (
|
|||
type (
|
||||
PasswordMigration struct {
|
||||
deps webHookDependencies
|
||||
conf json.RawMessage
|
||||
conf *request.Config
|
||||
}
|
||||
PasswordMigrationRequest struct {
|
||||
Identifier string `json:"identifier"`
|
||||
Password string `json:"password"`
|
||||
Identifier string `json:"identifier"`
|
||||
Password string `json:"password"`
|
||||
Identity *identity.Identity `json:"-"`
|
||||
}
|
||||
PasswordMigrationResponse struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewPasswordMigrationHook(deps webHookDependencies, conf json.RawMessage) *PasswordMigration {
|
||||
func NewPasswordMigrationHook(deps webHookDependencies, conf *request.Config) *PasswordMigration {
|
||||
return &PasswordMigration{deps: deps, conf: conf}
|
||||
}
|
||||
|
||||
func (p *PasswordMigration) Execute(ctx context.Context, data *PasswordMigrationRequest) (err error) {
|
||||
func (p *PasswordMigration) Execute(ctx context.Context, req *http.Request, flow flow.Flow, data *PasswordMigrationRequest) (err error) {
|
||||
var (
|
||||
httpClient = p.deps.HTTPClient(ctx)
|
||||
emitEvent = gjson.GetBytes(p.conf, "emit_analytics_event").Bool() || !gjson.GetBytes(p.conf, "emit_analytics_event").Exists() // default true
|
||||
emitEvent = p.conf.EmitAnalyticsEvent == nil || *p.conf.EmitAnalyticsEvent // default true
|
||||
tracer = trace.SpanFromContext(ctx).TracerProvider().Tracer("kratos-webhooks")
|
||||
)
|
||||
|
||||
|
|
@ -59,22 +62,46 @@ func (p *PasswordMigration) Execute(ctx context.Context, data *PasswordMigration
|
|||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
req, err := builder.BuildRequest(ctx, nil) // passing a nil body here skips Jsonnet
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
rawData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if err = req.SetBody(rawData); err != nil {
|
||||
return errors.WithStack(err)
|
||||
var whReq *retryablehttp.Request
|
||||
if p.conf.TemplateURI == "" {
|
||||
whReq, err = builder.BuildRequest(ctx, nil) // passing a nil body here skips Jsonnet
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if err = whReq.SetBody(rawData); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
} else {
|
||||
type templateContextMerged struct {
|
||||
templateContext
|
||||
Password string `json:"password"`
|
||||
Identifier string `json:"identifier"`
|
||||
}
|
||||
whReq, err = builder.BuildRequest(ctx, templateContextMerged{
|
||||
templateContext: templateContext{
|
||||
Flow: flow,
|
||||
RequestHeaders: req.Header,
|
||||
RequestMethod: req.Method,
|
||||
RequestURL: x.RequestURL(req).String(),
|
||||
RequestCookies: cookies(req),
|
||||
Identity: data.Identity,
|
||||
},
|
||||
Password: data.Password,
|
||||
Identifier: data.Identifier,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p.deps.Logger().WithRequest(req.Request).Info("Dispatching password migration hook")
|
||||
req = req.WithContext(ctx)
|
||||
p.deps.Logger().WithRequest(whReq.Request).Info("Dispatching password migration hook")
|
||||
whReq = whReq.WithContext(ctx)
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
resp, err := httpClient.Do(whReq)
|
||||
if err != nil {
|
||||
return herodot.DefaultError{
|
||||
CodeField: http.StatusBadGateway,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/ory/kratos/text"
|
||||
"github.com/ory/kratos/ui/node"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/x/otelx"
|
||||
)
|
||||
|
||||
|
|
@ -32,8 +33,8 @@ var (
|
|||
type (
|
||||
verifierDependencies interface {
|
||||
config.Provider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
verification.StrategyProvider
|
||||
verification.FlowPersistenceProvider
|
||||
identity.PrivilegedPoolProvider
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import (
|
|||
"github.com/gofrs/uuid"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tidwall/gjson"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.11.0"
|
||||
|
|
@ -90,7 +89,7 @@ type (
|
|||
|
||||
WebHook struct {
|
||||
deps webHookDependencies
|
||||
conf json.RawMessage
|
||||
conf *request.Config
|
||||
}
|
||||
|
||||
detailedMessage struct {
|
||||
|
|
@ -120,7 +119,7 @@ func cookies(req *http.Request) map[string]string {
|
|||
return cookies
|
||||
}
|
||||
|
||||
func NewWebHook(r webHookDependencies, c json.RawMessage) *WebHook {
|
||||
func NewWebHook(r webHookDependencies, c *request.Config) *WebHook {
|
||||
return &WebHook{deps: r, conf: c}
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +212,7 @@ func (e *WebHook) ExecuteRegistrationPreHook(_ http.ResponseWriter, req *http.Re
|
|||
}
|
||||
|
||||
func (e *WebHook) ExecutePostRegistrationPrePersistHook(_ http.ResponseWriter, req *http.Request, flow *registration.Flow, id *identity.Identity) error {
|
||||
if !(gjson.GetBytes(e.conf, "can_interrupt").Bool() || gjson.GetBytes(e.conf, "response.parse").Bool()) {
|
||||
if !(e.conf.CanInterrupt || e.conf.Response.Parse) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +229,7 @@ func (e *WebHook) ExecutePostRegistrationPrePersistHook(_ http.ResponseWriter, r
|
|||
}
|
||||
|
||||
func (e *WebHook) ExecutePostRegistrationPostPersistHook(_ http.ResponseWriter, req *http.Request, flow *registration.Flow, session *session.Session) error {
|
||||
if gjson.GetBytes(e.conf, "can_interrupt").Bool() || gjson.GetBytes(e.conf, "response.parse").Bool() {
|
||||
if e.conf.CanInterrupt || e.conf.Response.Parse {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +262,7 @@ func (e *WebHook) ExecuteSettingsPreHook(_ http.ResponseWriter, req *http.Reques
|
|||
}
|
||||
|
||||
func (e *WebHook) ExecuteSettingsPostPersistHook(_ http.ResponseWriter, req *http.Request, flow *settings.Flow, id *identity.Identity, _ *session.Session) error {
|
||||
if gjson.GetBytes(e.conf, "can_interrupt").Bool() || gjson.GetBytes(e.conf, "response.parse").Bool() {
|
||||
if e.conf.CanInterrupt || e.conf.Response.Parse {
|
||||
return nil
|
||||
}
|
||||
return otelx.WithSpan(req.Context(), "selfservice.hook.WebHook.ExecuteSettingsPostPersistHook", func(ctx context.Context) error {
|
||||
|
|
@ -279,7 +278,7 @@ func (e *WebHook) ExecuteSettingsPostPersistHook(_ http.ResponseWriter, req *htt
|
|||
}
|
||||
|
||||
func (e *WebHook) ExecuteSettingsPrePersistHook(_ http.ResponseWriter, req *http.Request, flow *settings.Flow, id *identity.Identity) error {
|
||||
if !(gjson.GetBytes(e.conf, "can_interrupt").Bool() || gjson.GetBytes(e.conf, "response.parse").Bool()) {
|
||||
if !(e.conf.CanInterrupt || e.conf.Response.Parse) {
|
||||
return nil
|
||||
}
|
||||
return otelx.WithSpan(req.Context(), "selfservice.hook.WebHook.ExecuteSettingsPrePersistHook", func(ctx context.Context) error {
|
||||
|
|
@ -297,11 +296,11 @@ func (e *WebHook) ExecuteSettingsPrePersistHook(_ http.ResponseWriter, req *http
|
|||
func (e *WebHook) execute(ctx context.Context, data *templateContext) error {
|
||||
var (
|
||||
httpClient = e.deps.HTTPClient(ctx)
|
||||
ignoreResponse = gjson.GetBytes(e.conf, "response.ignore").Bool()
|
||||
canInterrupt = gjson.GetBytes(e.conf, "can_interrupt").Bool()
|
||||
parseResponse = gjson.GetBytes(e.conf, "response.parse").Bool()
|
||||
emitEvent = gjson.GetBytes(e.conf, "emit_analytics_event").Bool() || !gjson.GetBytes(e.conf, "emit_analytics_event").Exists() // default true
|
||||
webhookID = gjson.GetBytes(e.conf, "id").Str
|
||||
ignoreResponse = e.conf.Response.Ignore
|
||||
canInterrupt = e.conf.CanInterrupt
|
||||
parseResponse = e.conf.Response.Parse
|
||||
emitEvent = e.conf.EmitAnalyticsEvent == nil || *e.conf.EmitAnalyticsEvent // default true
|
||||
webhookID = e.conf.ID
|
||||
// The trigger ID is a random ID. It can be used to correlate webhook requests across retries.
|
||||
triggerID = x.NewUUID()
|
||||
tracer = trace.SpanFromContext(ctx).TracerProvider().Tracer("kratos-webhooks")
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/internal"
|
||||
"github.com/ory/kratos/request"
|
||||
"github.com/ory/kratos/schema"
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
"github.com/ory/kratos/selfservice/flow/login"
|
||||
|
|
@ -50,6 +51,7 @@ import (
|
|||
"github.com/ory/x/logrusx"
|
||||
"github.com/ory/x/otelx"
|
||||
"github.com/ory/x/otelx/semconv"
|
||||
"github.com/ory/x/pointerx"
|
||||
"github.com/ory/x/snapshotx"
|
||||
)
|
||||
|
||||
|
|
@ -297,58 +299,43 @@ func TestWebHooks(t *testing.T) {
|
|||
t.Run("uc="+tc.uc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, auth := range []struct {
|
||||
uc string
|
||||
createAuthConfig func() string
|
||||
expectedHeader func(header http.Header)
|
||||
uc string
|
||||
authConfig request.AuthConfig
|
||||
expectedHeader func(header http.Header)
|
||||
}{
|
||||
{
|
||||
uc: "no auth",
|
||||
createAuthConfig: func() string { return "{}" },
|
||||
expectedHeader: func(header http.Header) {},
|
||||
uc: "no auth",
|
||||
authConfig: request.AuthConfig{},
|
||||
expectedHeader: func(header http.Header) {},
|
||||
},
|
||||
{
|
||||
uc: "api key in header",
|
||||
createAuthConfig: func() string {
|
||||
return `{
|
||||
"type": "api_key",
|
||||
"config": {
|
||||
"name": "My-Key",
|
||||
"value": "My-Key-Value",
|
||||
"in": "header"
|
||||
}
|
||||
}`
|
||||
},
|
||||
authConfig: request.AuthConfig{Type: "api_key", Config: map[string]any{
|
||||
"name": "My-Key",
|
||||
"value": "My-Key-Value",
|
||||
"in": "header",
|
||||
}},
|
||||
expectedHeader: func(header http.Header) {
|
||||
header.Set("My-Key", "My-Key-Value")
|
||||
},
|
||||
},
|
||||
{
|
||||
uc: "api key in cookie",
|
||||
createAuthConfig: func() string {
|
||||
return `{
|
||||
"type": "api_key",
|
||||
"config": {
|
||||
"name": "My-Key",
|
||||
"value": "My-Key-Value",
|
||||
"in": "cookie"
|
||||
}
|
||||
}`
|
||||
},
|
||||
authConfig: request.AuthConfig{Type: "api_key", Config: map[string]any{
|
||||
"name": "My-Key",
|
||||
"value": "My-Key-Value",
|
||||
"in": "cookie",
|
||||
}},
|
||||
expectedHeader: func(header http.Header) {
|
||||
header.Set("Cookie", "My-Key=My-Key-Value")
|
||||
},
|
||||
},
|
||||
{
|
||||
uc: "basic auth",
|
||||
createAuthConfig: func() string {
|
||||
return `{
|
||||
"type": "basic_auth",
|
||||
"config": {
|
||||
"user": "My-User",
|
||||
"password": "Super-Secret"
|
||||
}
|
||||
}`
|
||||
},
|
||||
authConfig: request.AuthConfig{Type: "basic_auth", Config: map[string]any{
|
||||
"user": "My-User",
|
||||
"password": "Super-Secret",
|
||||
}},
|
||||
expectedHeader: func(header http.Header) {
|
||||
header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("My-User:Super-Secret")))
|
||||
},
|
||||
|
|
@ -387,14 +374,13 @@ func TestWebHooks(t *testing.T) {
|
|||
s := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}
|
||||
whr := &WebHookRequest{}
|
||||
ts := newServer(webHookEndPoint(whr))
|
||||
conf := json.RawMessage(fmt.Sprintf(`{
|
||||
"url": "%s",
|
||||
"method": "%s",
|
||||
"body": "%s",
|
||||
"auth": %s
|
||||
}`, ts.URL+path, method, "file://./stub/test_body.jsonnet", auth.createAuthConfig()))
|
||||
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: method,
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
Auth: auth.authConfig,
|
||||
})
|
||||
|
||||
err = tc.callWebHook(wh, req, f, s)
|
||||
if method == "GARBAGE" {
|
||||
|
|
@ -668,14 +654,13 @@ func TestWebHooks(t *testing.T) {
|
|||
s := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}
|
||||
code, res := tc.webHookResponse()
|
||||
ts := newServer(webHookHttpCodeWithBodyEndPoint(t, code, res))
|
||||
conf := json.RawMessage(fmt.Sprintf(`{
|
||||
"url": "%s",
|
||||
"method": "%s",
|
||||
"body": "%s",
|
||||
"can_interrupt": true
|
||||
}`, ts.URL+path, method, "file://./stub/test_body.jsonnet"))
|
||||
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: method,
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
CanInterrupt: true,
|
||||
})
|
||||
|
||||
err := tc.callWebHook(wh, req, f, s)
|
||||
if tc.expectedError == nil {
|
||||
|
|
@ -705,8 +690,14 @@ func TestWebHooks(t *testing.T) {
|
|||
URL: &url.URL{Path: "some_end_point"},
|
||||
}
|
||||
ts := newServer(webHookHttpCodeWithBodyEndPoint(t, responseCode, response))
|
||||
conf := json.RawMessage(fmt.Sprintf(`{"url": "%s", "method": "POST", "body": "%s", "response": {"parse":true}}`, ts.URL+path, "file://./stub/test_body.jsonnet"))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "POST",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
Response: request.ResponseConfig{
|
||||
Parse: true,
|
||||
},
|
||||
})
|
||||
in := &id
|
||||
err := wh.ExecutePostRegistrationPrePersistHook(nil, req, f, in)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -777,24 +768,6 @@ func TestWebHooks(t *testing.T) {
|
|||
})
|
||||
})
|
||||
|
||||
t.Run("must error when config is erroneous", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
req := &http.Request{
|
||||
Header: map[string][]string{"Some-Header": {"Some-Value"}},
|
||||
Host: "www.ory.sh",
|
||||
TLS: new(tls.ConnectionState),
|
||||
URL: &url.URL{Path: "/some_end_point"},
|
||||
|
||||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage("not valid json")
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("cannot have parse and ignore both set", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ts := newServer(webHookHttpCodeEndPoint(200))
|
||||
|
|
@ -807,8 +780,15 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(fmt.Sprintf(`{"url": "%s", "method": "GET", "body": "./stub/test_body.jsonnet", "response": {"ignore": true, "parse": true}}`, ts.URL+path))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "GET",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "./stub/test_body.jsonnet",
|
||||
Response: request.ResponseConfig{
|
||||
Ignore: true,
|
||||
Parse: true,
|
||||
},
|
||||
})
|
||||
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
assert.Error(t, err)
|
||||
|
|
@ -821,7 +801,6 @@ func TestWebHooks(t *testing.T) {
|
|||
{uc: "Post Settings Hook - parse true", parse: true},
|
||||
{uc: "Post Settings Hook - parse false", parse: false},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run("uc="+tc.uc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ts := newServer(webHookHttpCodeWithBodyEndPoint(t, 200, []byte(`{"identity":{"traits":{"email":"some@other-example.org"}}}`)))
|
||||
|
|
@ -834,8 +813,14 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &settings.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(fmt.Sprintf(`{"url": "%s", "method": "POST", "body": "%s", "response": {"parse":%t}}`, ts.URL+path, "file://./stub/test_body.jsonnet", tc.parse))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "POST",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
Response: request.ResponseConfig{
|
||||
Parse: tc.parse,
|
||||
},
|
||||
})
|
||||
uuid := x.NewUUID()
|
||||
in := &identity.Identity{ID: uuid}
|
||||
s := &session.Session{ID: x.NewUUID(), Identity: in}
|
||||
|
|
@ -865,12 +850,11 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(fmt.Sprintf(`{
|
||||
"url": "%s",
|
||||
"method": "%s",
|
||||
"body": "%s"
|
||||
}`, ts.URL+path, "POST", "file://./stub/bad_template.jsonnet"))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "POST",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/bad_template.jsonnet",
|
||||
})
|
||||
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
assert.Error(t, err)
|
||||
|
|
@ -886,8 +870,14 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(fmt.Sprintf(`{"url": "%s", "method": "GET", "body": "file://./stub/bad_template.jsonnet", "response": {"ignore": true}}`, ts.URL+path))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "GET",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/bad_template.jsonnet",
|
||||
Response: request.ResponseConfig{
|
||||
Ignore: true,
|
||||
},
|
||||
})
|
||||
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -904,12 +894,11 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(`{
|
||||
"url": "https://i-do-not-exist/",
|
||||
"method": "POST",
|
||||
"body": "./stub/cancel_template.jsonnet"
|
||||
}`)
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "POST",
|
||||
URL: "https://i-do-not-exist/",
|
||||
TemplateURI: "file://./stub/cancel_template.jsonnet",
|
||||
})
|
||||
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -942,8 +931,14 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(fmt.Sprintf(`{"url": "%s", "method": "GET", "body": "./stub/test_body.jsonnet", "response": {"ignore": true}}`, ts.URL+path))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "GET",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
Response: request.ResponseConfig{
|
||||
Ignore: true,
|
||||
},
|
||||
})
|
||||
|
||||
start := time.Now()
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
|
|
@ -975,8 +970,11 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(fmt.Sprintf(`{"url": "%s", "method": "GET", "body": "./stub/test_body.jsonnet"}`, ts.URL+path))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "GET",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
})
|
||||
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
require.Error(t, err)
|
||||
|
|
@ -1010,12 +1008,11 @@ func TestWebHooks(t *testing.T) {
|
|||
Method: http.MethodPost,
|
||||
}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
conf := json.RawMessage(fmt.Sprintf(`{
|
||||
"url": "%s",
|
||||
"method": "%s",
|
||||
"body": "%s"
|
||||
}`, ts.URL+path, "POST", "file://./stub/test_body.jsonnet"))
|
||||
wh := hook.NewWebHook(&whDeps, conf)
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
Method: "POST",
|
||||
URL: ts.URL + path,
|
||||
TemplateURI: "file://./stub/test_body.jsonnet",
|
||||
})
|
||||
|
||||
err := wh.ExecuteLoginPreHook(nil, req, f)
|
||||
if tc.mustSuccess {
|
||||
|
|
@ -1056,11 +1053,11 @@ func TestDisallowPrivateIPRanges(t *testing.T) {
|
|||
|
||||
t.Run("not allowed to call url", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
wh := hook.NewWebHook(&whDeps, json.RawMessage(`{
|
||||
"url": "https://localhost:1234/",
|
||||
"method": "GET",
|
||||
"body": "file://stub/test_body.jsonnet"
|
||||
}`))
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
URL: "https://localhost:1234/",
|
||||
Method: "GET",
|
||||
TemplateURI: "file://stub/test_body.jsonnet",
|
||||
})
|
||||
err := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "is not a permitted destination")
|
||||
|
|
@ -1068,11 +1065,11 @@ func TestDisallowPrivateIPRanges(t *testing.T) {
|
|||
|
||||
t.Run("allowed to call exempt url", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
wh := hook.NewWebHook(&whDeps, json.RawMessage(`{
|
||||
"url": "http://localhost/exception",
|
||||
"method": "GET",
|
||||
"body": "file://stub/test_body.jsonnet"
|
||||
}`))
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
URL: "http://localhost/exception",
|
||||
Method: "GET",
|
||||
TemplateURI: "file://stub/test_body.jsonnet",
|
||||
})
|
||||
err := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)
|
||||
require.Error(t, err, "the target does not exist and we still receive an error")
|
||||
require.NotContains(t, err.Error(), "is not a permitted destination", "but the error is not related to the IP range.")
|
||||
|
|
@ -1089,11 +1086,11 @@ func TestDisallowPrivateIPRanges(t *testing.T) {
|
|||
}
|
||||
s := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}
|
||||
f := &login.Flow{ID: x.NewUUID()}
|
||||
wh := hook.NewWebHook(&whDeps, json.RawMessage(`{
|
||||
"url": "https://www.google.com/",
|
||||
"method": "GET",
|
||||
"body": "http://192.168.178.0/test_body.jsonnet"
|
||||
}`))
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
URL: "https://www.google.com/",
|
||||
Method: "GET",
|
||||
TemplateURI: "http://192.168.178.0/test_body.jsonnet",
|
||||
})
|
||||
err := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "is not a permitted destination")
|
||||
|
|
@ -1144,15 +1141,14 @@ func TestAsyncWebhook(t *testing.T) {
|
|||
}))
|
||||
t.Cleanup(webhookReceiver.Close)
|
||||
|
||||
wh := hook.NewWebHook(&whDeps, json.RawMessage(fmt.Sprintf(`
|
||||
{
|
||||
"url": %q,
|
||||
"method": "GET",
|
||||
"body": "file://stub/test_body.jsonnet",
|
||||
"response": {
|
||||
"ignore": true
|
||||
}
|
||||
}`, webhookReceiver.URL)))
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
URL: webhookReceiver.URL,
|
||||
Method: "GET",
|
||||
TemplateURI: "file://stub/test_body.jsonnet",
|
||||
Response: request.ResponseConfig{
|
||||
Ignore: true,
|
||||
},
|
||||
})
|
||||
err := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)
|
||||
require.NoError(t, err) // execution returns immediately for async webhook
|
||||
select {
|
||||
|
|
@ -1234,17 +1230,12 @@ func TestWebhookEvents(t *testing.T) {
|
|||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
whID := x.NewUUID()
|
||||
wh := hook.NewWebHook(&whDeps, json.RawMessage(fmt.Sprintf(`
|
||||
{
|
||||
"id": %q,
|
||||
"url": %q,
|
||||
"method": "GET",
|
||||
"body": "file://stub/test_body.jsonnet",
|
||||
"response": {
|
||||
"ignore": false,
|
||||
"parse": false
|
||||
}
|
||||
}`, whID, webhookReceiver.URL+"/ok")))
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
ID: whID.String(),
|
||||
URL: webhookReceiver.URL + "/ok",
|
||||
Method: "GET",
|
||||
TemplateURI: "file://stub/test_body.jsonnet",
|
||||
})
|
||||
|
||||
recorder := tracetest.NewSpanRecorder()
|
||||
tracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer("test")
|
||||
|
|
@ -1286,17 +1277,12 @@ func TestWebhookEvents(t *testing.T) {
|
|||
|
||||
t.Run("failed", func(t *testing.T) {
|
||||
whID := x.NewUUID()
|
||||
wh := hook.NewWebHook(&whDeps, json.RawMessage(fmt.Sprintf(`
|
||||
{
|
||||
"id": %q,
|
||||
"url": %q,
|
||||
"method": "GET",
|
||||
"body": "file://stub/test_body.jsonnet",
|
||||
"response": {
|
||||
"ignore": false,
|
||||
"parse": false
|
||||
}
|
||||
}`, whID, webhookReceiver.URL+"/fail")))
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
ID: whID.String(),
|
||||
URL: webhookReceiver.URL + "/fail",
|
||||
Method: "GET",
|
||||
TemplateURI: "file://stub/test_body.jsonnet",
|
||||
})
|
||||
|
||||
recorder := tracetest.NewSpanRecorder()
|
||||
tracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer("test")
|
||||
|
|
@ -1347,17 +1333,12 @@ func TestWebhookEvents(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("event disabled", func(t *testing.T) {
|
||||
wh := hook.NewWebHook(&whDeps, json.RawMessage(fmt.Sprintf(`
|
||||
{
|
||||
"url": %q,
|
||||
"method": "GET",
|
||||
"body": "file://stub/test_body.jsonnet",
|
||||
"response": {
|
||||
"ignore": false,
|
||||
"parse": false
|
||||
},
|
||||
"emit_analytics_event": false
|
||||
}`, webhookReceiver.URL+"/fail")))
|
||||
wh := hook.NewWebHook(&whDeps, &request.Config{
|
||||
URL: webhookReceiver.URL + "/fail",
|
||||
Method: "GET",
|
||||
TemplateURI: "file://stub/test_body.jsonnet",
|
||||
EmitAnalyticsEvent: pointerx.Ptr(false),
|
||||
})
|
||||
|
||||
recorder := tracetest.NewSpanRecorder()
|
||||
tracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer("test")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
|
@ -64,8 +66,8 @@ type (
|
|||
}
|
||||
|
||||
strategyDependencies interface {
|
||||
x.CSRFProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.TracingProvider
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/ory/x/pointerx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
|
@ -225,7 +227,7 @@ func (s *Strategy) recoveryIssueSession(w http.ResponseWriter, r *http.Request,
|
|||
if returnToURL != nil {
|
||||
returnTo = returnToURL.String()
|
||||
}
|
||||
sf.RequestURL, err = x.TakeOverReturnToParameter(f.RequestURL, sf.RequestURL, returnTo)
|
||||
sf.RequestURL, err = redir.TakeOverReturnToParameter(f.RequestURL, sf.RequestURL, returnTo)
|
||||
if err != nil {
|
||||
return s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
|
@ -35,7 +37,7 @@ const (
|
|||
|
||||
func (s *Strategy) RegisterPublicRecoveryRoutes(public *x.RouterPublic) {
|
||||
s.deps.CSRFHandler().IgnorePath(RouteAdminCreateRecoveryCode)
|
||||
public.POST(RouteAdminCreateRecoveryCode, x.RedirectToAdminRoute(s.deps))
|
||||
public.POST(RouteAdminCreateRecoveryCode, redir.RedirectToAdminRoute(s.deps))
|
||||
}
|
||||
|
||||
func (s *Strategy) RegisterAdminRecoveryRoutes(admin *x.RouterAdmin) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/ory/x/urlx"
|
||||
|
|
@ -383,14 +385,14 @@ func TestVerification(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
body := string(ioutilx.MustReadAll(res.Body))
|
||||
require.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)
|
||||
assert.Contains(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL))[0].Name, x.CSRFTokenName)
|
||||
assert.Contains(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL))[0].Name, nosurfx.CSRFTokenName)
|
||||
|
||||
actualBody, _ := submitVerificationCode(t, body, cl, code)
|
||||
assert.EqualValues(t, "passed_challenge", gjson.Get(actualBody, "state").String())
|
||||
})
|
||||
|
||||
newValidFlow := func(t *testing.T, fType flow.Type, requestURL string) (*verification.Flow, *code.VerificationCode, string) {
|
||||
f, err := verification.NewFlow(conf, time.Hour, x.FakeCSRFToken, httptest.NewRequest("GET", requestURL, nil), code.NewStrategy(reg), fType)
|
||||
f, err := verification.NewFlow(conf, time.Hour, nosurfx.FakeCSRFToken, httptest.NewRequest("GET", requestURL, nil), code.NewStrategy(reg), fType)
|
||||
require.NoError(t, err)
|
||||
f.State = flow.StateEmailSent
|
||||
u, err := url.Parse(f.RequestURL)
|
||||
|
|
@ -429,7 +431,7 @@ func TestVerification(t *testing.T) {
|
|||
|
||||
res, err := client.PostForm(action, url.Values{
|
||||
"code": {rawCode},
|
||||
"csrf_token": {x.FakeCSRFToken},
|
||||
"csrf_token": {nosurfx.FakeCSRFToken},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
body := ioutilx.MustReadAll(res.Body)
|
||||
|
|
|
|||
|
|
@ -14,14 +14,15 @@ import (
|
|||
"github.com/ory/kratos/session"
|
||||
"github.com/ory/kratos/ui/node"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/x/decoderx"
|
||||
)
|
||||
|
||||
type dependencies interface {
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.TracingProvider
|
||||
|
||||
config.Provider
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/strategy/oidc"
|
||||
|
||||
"github.com/ory/kratos/selfservice/strategy/idfirst"
|
||||
|
|
@ -180,7 +182,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
})
|
||||
|
||||
values := url.Values{
|
||||
"csrf_token": {x.FakeCSRFToken},
|
||||
"csrf_token": {nosurfx.FakeCSRFToken},
|
||||
"identifier": {"identifier"},
|
||||
"method": {"identifier_first"},
|
||||
}
|
||||
|
|
@ -236,7 +238,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
|
||||
actual, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values.Encode())
|
||||
assert.EqualValues(t, http.StatusOK, res.StatusCode)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken,
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken,
|
||||
json.RawMessage(actual), "%s", actual)
|
||||
})
|
||||
|
||||
|
|
@ -246,7 +248,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
|
||||
actual, res := testhelpers.LoginMakeRequest(t, false, true, f, browserClient, values.Encode())
|
||||
assert.EqualValues(t, http.StatusForbidden, res.StatusCode)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken,
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken,
|
||||
json.RawMessage(gjson.Get(actual, "error").Raw), "%s", actual)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/ory/kratos/ui/container"
|
||||
"github.com/ory/kratos/ui/node"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/x/decoderx"
|
||||
)
|
||||
|
||||
|
|
@ -38,8 +39,8 @@ type (
|
|||
}
|
||||
|
||||
strategyDependencies interface {
|
||||
x.CSRFProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.TracingProvider
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gobuffalo/pop/v6"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
|
@ -46,7 +48,7 @@ func (s *Strategy) RecoveryStrategyID() string {
|
|||
|
||||
func (s *Strategy) RegisterPublicRecoveryRoutes(public *x.RouterPublic) {
|
||||
s.d.CSRFHandler().IgnorePath(RouteAdminCreateRecoveryLink)
|
||||
public.POST(RouteAdminCreateRecoveryLink, x.RedirectToAdminRoute(s.d))
|
||||
public.POST(RouteAdminCreateRecoveryLink, redir.RedirectToAdminRoute(s.d))
|
||||
}
|
||||
|
||||
func (s *Strategy) RegisterAdminRecoveryRoutes(admin *x.RouterAdmin) {
|
||||
|
|
@ -347,7 +349,7 @@ func (s *Strategy) recoveryIssueSession(ctx context.Context, w http.ResponseWrit
|
|||
returnTo = returnToURL.String()
|
||||
}
|
||||
|
||||
sf.RequestURL, err = x.TakeOverReturnToParameter(f.RequestURL, sf.RequestURL, returnTo)
|
||||
sf.RequestURL, err = redir.TakeOverReturnToParameter(f.RequestURL, sf.RequestURL, returnTo)
|
||||
if err != nil {
|
||||
return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
confighelpers "github.com/ory/kratos/driver/config/testhelpers"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
|
@ -649,7 +651,7 @@ func TestRecovery(t *testing.T) {
|
|||
assert.Equal(t, http.StatusSeeOther, res.StatusCode)
|
||||
require.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)
|
||||
cookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))
|
||||
assert.Contains(t, cookies, x.CSRFTokenName)
|
||||
assert.Contains(t, cookies, nosurfx.CSRFTokenName)
|
||||
assert.Contains(t, cookies, "ory_kratos_session")
|
||||
returnTo, err := res.Location()
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/x/urlx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/strategy/link"
|
||||
|
|
@ -346,7 +348,7 @@ func TestVerification(t *testing.T) {
|
|||
body := string(ioutilx.MustReadAll(res.Body))
|
||||
require.NoError(t, res.Body.Close())
|
||||
require.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)
|
||||
assert.Contains(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL))[0].Name, x.CSRFTokenName)
|
||||
assert.Contains(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL))[0].Name, nosurfx.CSRFTokenName)
|
||||
|
||||
actualRes, err := cl.Get(public.URL + verification.RouteGetFlow + "?id=" + gjson.Get(body, "id").String())
|
||||
require.NoError(t, err)
|
||||
|
|
@ -366,7 +368,7 @@ func TestVerification(t *testing.T) {
|
|||
})
|
||||
|
||||
newValidFlow := func(t *testing.T, fType flow.Type, requestURL string) (*verification.Flow, *link.VerificationToken) {
|
||||
f, err := verification.NewFlow(conf, time.Hour, x.FakeCSRFToken, httptest.NewRequest("GET", requestURL, nil), nil, fType)
|
||||
f, err := verification.NewFlow(conf, time.Hour, nosurfx.FakeCSRFToken, httptest.NewRequest("GET", requestURL, nil), nil, fType)
|
||||
require.NoError(t, err)
|
||||
f.State = flow.StateEmailSent
|
||||
require.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(context.Background(), f))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
|
@ -311,7 +313,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
}, id)
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), errTS.URL)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
})
|
||||
|
||||
t.Run("type=spa", func(t *testing.T) {
|
||||
|
|
@ -321,7 +323,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
}, id)
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/x/sqlcon"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
|
@ -191,7 +193,7 @@ func TestCompleteSettings(t *testing.T) {
|
|||
}, id)
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), errTS.URL)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
})
|
||||
|
||||
t.Run("type=spa", func(t *testing.T) {
|
||||
|
|
@ -201,7 +203,7 @@ func TestCompleteSettings(t *testing.T) {
|
|||
}, id)
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/ory/kratos/continuity"
|
||||
|
|
@ -32,8 +34,8 @@ var (
|
|||
type lookupStrategyDependencies interface {
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.TransactionPersistenceProvider
|
||||
x.TracingProvider
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
"github.com/ory/kratos/x/redir"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/pkg/errors"
|
||||
|
|
@ -69,8 +72,8 @@ type Dependencies interface {
|
|||
|
||||
x.LoggingProvider
|
||||
x.CookieProvider
|
||||
x.CSRFProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.WriterProvider
|
||||
x.HTTPClientProvider
|
||||
x.TracingProvider
|
||||
|
|
@ -416,7 +419,7 @@ func (s *Strategy) alreadyAuthenticated(ctx context.Context, w http.ResponseWrit
|
|||
} else if !isForced(f) {
|
||||
returnTo := s.d.Config().SelfServiceBrowserDefaultReturnTo(ctx)
|
||||
if redirecter, ok := f.(flow.FlowWithRedirect); ok {
|
||||
r, err := x.SecureRedirectTo(r, returnTo, redirecter.SecureRedirectToOpts(ctx, s.d)...)
|
||||
r, err := redir.SecureRedirectTo(r, returnTo, redirecter.SecureRedirectToOpts(ctx, s.d)...)
|
||||
if err == nil {
|
||||
returnTo = r
|
||||
}
|
||||
|
|
@ -662,7 +665,7 @@ func (s *Strategy) HandleError(ctx context.Context, w http.ResponseWriter, r *ht
|
|||
if lf.Type == flow.TypeAPI {
|
||||
returnTo := s.d.Config().SelfServiceBrowserDefaultReturnTo(ctx)
|
||||
if redirecter, ok := f.(flow.FlowWithRedirect); ok {
|
||||
secureReturnTo, err := x.SecureRedirectTo(r, returnTo, redirecter.SecureRedirectToOpts(ctx, s.d)...)
|
||||
secureReturnTo, err := redir.SecureRedirectTo(r, returnTo, redirecter.SecureRedirectToOpts(ctx, s.d)...)
|
||||
if err == nil {
|
||||
returnTo = secureReturnTo
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/x/snapshotx"
|
||||
|
||||
"github.com/ory/kratos/driver"
|
||||
|
|
@ -278,7 +280,7 @@ func TestSettingsStrategy(t *testing.T) {
|
|||
unlink := func(t *testing.T, agent, provider string) (body []byte, res *http.Response, req *kratos.SettingsFlow) {
|
||||
req = nprSDK(t, agents[agent], "", time.Hour)
|
||||
body, res = testhelpers.HTTPPostForm(t, agents[agent], action(req),
|
||||
&url.Values{"csrf_token": {x.FakeCSRFToken}, "unlink": {provider}})
|
||||
&url.Values{"csrf_token": {nosurfx.FakeCSRFToken}, "unlink": {provider}})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +366,7 @@ func TestSettingsStrategy(t *testing.T) {
|
|||
conf.MustSet(ctx, config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, time.Minute*5)
|
||||
|
||||
body, res := testhelpers.HTTPPostForm(t, agents[agent], action(req),
|
||||
&url.Values{"csrf_token": {x.FakeCSRFToken}, "unlink": {provider}})
|
||||
&url.Values{"csrf_token": {nosurfx.FakeCSRFToken}, "unlink": {provider}})
|
||||
assert.Contains(t, res.Request.URL.String(), uiTS.URL+"/settings?flow="+req.Id)
|
||||
|
||||
assert.Equal(t, "success", gjson.GetBytes(body, "state").String())
|
||||
|
|
@ -378,7 +380,7 @@ func TestSettingsStrategy(t *testing.T) {
|
|||
link := func(t *testing.T, agent, provider string) (body []byte, res *http.Response, req *kratos.SettingsFlow) {
|
||||
req = nprSDK(t, agents[agent], "", time.Hour)
|
||||
body, res = testhelpers.HTTPPostForm(t, agents[agent], action(req),
|
||||
&url.Values{"csrf_token": {x.FakeCSRFToken}, "link": {provider}})
|
||||
&url.Values{"csrf_token": {nosurfx.FakeCSRFToken}, "link": {provider}})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -516,7 +518,7 @@ func TestSettingsStrategy(t *testing.T) {
|
|||
}
|
||||
|
||||
values := &url.Values{}
|
||||
values.Set("csrf_token", x.FakeCSRFToken)
|
||||
values.Set("csrf_token", nosurfx.FakeCSRFToken)
|
||||
values.Set("link", provider)
|
||||
values.Set("upstream_parameters.login_hint", "foo@bar.com")
|
||||
values.Set("upstream_parameters.hd", "bar.com")
|
||||
|
|
@ -545,7 +547,7 @@ func TestSettingsStrategy(t *testing.T) {
|
|||
}
|
||||
|
||||
values := &url.Values{}
|
||||
values.Set("csrf_token", x.FakeCSRFToken)
|
||||
values.Set("csrf_token", nosurfx.FakeCSRFToken)
|
||||
values.Set("link", provider)
|
||||
values.Set("upstream_parameters.lol", "invalid")
|
||||
|
||||
|
|
@ -601,7 +603,7 @@ func TestSettingsStrategy(t *testing.T) {
|
|||
conf.MustSet(ctx, config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, time.Minute*5)
|
||||
|
||||
body, res := testhelpers.HTTPPostForm(t, agents[agent], action(req),
|
||||
&url.Values{"csrf_token": {x.FakeCSRFToken}, "unlink": {provider}})
|
||||
&url.Values{"csrf_token": {nosurfx.FakeCSRFToken}, "unlink": {provider}})
|
||||
assert.Contains(t, res.Request.URL.String(), uiTS.URL+"/settings?flow="+req.Id)
|
||||
|
||||
assert.Equal(t, "success", gjson.GetBytes(body, "state").String())
|
||||
|
|
@ -678,7 +680,7 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{
|
||||
c: []oidc.Configuration{},
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -686,14 +688,14 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{Provider: "generic", ID: "github"},
|
||||
},
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewLinkNode("github", "github"),
|
||||
},
|
||||
},
|
||||
{
|
||||
c: defaultConfig,
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewLinkNode("facebook", "facebook"),
|
||||
oidc.NewLinkNode("google", "google"),
|
||||
oidc.NewLinkNode("github", "github"),
|
||||
|
|
@ -702,7 +704,7 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{
|
||||
c: defaultConfig,
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewLinkNode("facebook", "facebook"),
|
||||
oidc.NewLinkNode("google", "google"),
|
||||
oidc.NewLinkNode("github", "github"),
|
||||
|
|
@ -712,7 +714,7 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{
|
||||
c: defaultConfig,
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewLinkNode("facebook", "facebook"),
|
||||
oidc.NewLinkNode("github", "github"),
|
||||
},
|
||||
|
|
@ -723,7 +725,7 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{
|
||||
c: defaultConfig,
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewLinkNode("facebook", "facebook"),
|
||||
oidc.NewLinkNode("github", "github"),
|
||||
oidc.NewUnlinkNode("google", "google"),
|
||||
|
|
@ -739,7 +741,7 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{
|
||||
c: defaultConfig,
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewLinkNode("github", "github"),
|
||||
oidc.NewUnlinkNode("google", "google"),
|
||||
oidc.NewUnlinkNode("facebook", "facebook"),
|
||||
|
|
@ -757,7 +759,7 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{Provider: "generic", ID: "labeled", Label: "Labeled"},
|
||||
},
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewLinkNode("labeled", "Labeled"),
|
||||
},
|
||||
},
|
||||
|
|
@ -767,7 +769,7 @@ func TestPopulateSettingsMethod(t *testing.T) {
|
|||
{Provider: "generic", ID: "facebook"},
|
||||
},
|
||||
e: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
oidc.NewUnlinkNode("labeled", "Labeled"),
|
||||
oidc.NewUnlinkNode("facebook", "facebook"),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
"github.com/ory/kratos/selfservice/strategy/passkey"
|
||||
|
||||
|
|
@ -142,10 +144,10 @@ func TestCompleteSettings(t *testing.T) {
|
|||
}, id)
|
||||
if spa {
|
||||
assert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
} else {
|
||||
assert.Contains(t, res.Request.URL.String(), fix.errTS.URL)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/ory/kratos/continuity"
|
||||
|
|
@ -27,8 +29,8 @@ import (
|
|||
type strategyDependencies interface {
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.TracingProvider
|
||||
|
||||
config.Provider
|
||||
|
|
|
|||
|
|
@ -96,8 +96,12 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow,
|
|||
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Password migration hook is not enabled but password migration is requested."))
|
||||
}
|
||||
|
||||
migrationHook := hook.NewPasswordMigrationHook(s.d, pwHook.Config)
|
||||
err = migrationHook.Execute(ctx, &hook.PasswordMigrationRequest{Identifier: identifier, Password: p.Password})
|
||||
migrationHook := hook.NewPasswordMigrationHook(s.d, &pwHook.Config)
|
||||
err = migrationHook.Execute(ctx, r, f, &hook.PasswordMigrationRequest{
|
||||
Identifier: identifier,
|
||||
Password: p.Password,
|
||||
Identity: i,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, s.handleLoginError(r, f, p, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/strategy/idfirst"
|
||||
|
||||
configtesthelpers "github.com/ory/kratos/driver/config/testhelpers"
|
||||
|
|
@ -205,7 +207,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
conf.MustSet(ctx, config.ViperKeySelfServiceLoginRequestLifespan, "10m")
|
||||
})
|
||||
values := url.Values{
|
||||
"csrf_token": {x.FakeCSRFToken},
|
||||
"csrf_token": {nosurfx.FakeCSRFToken},
|
||||
"identifier": {"identifier"},
|
||||
"password": {"password"},
|
||||
}
|
||||
|
|
@ -257,7 +259,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
|
||||
actual, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values.Encode())
|
||||
assert.EqualValues(t, http.StatusOK, res.StatusCode)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken,
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken,
|
||||
json.RawMessage(actual), "%s", actual)
|
||||
})
|
||||
|
||||
|
|
@ -267,7 +269,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
|
||||
actual, res := testhelpers.LoginMakeRequest(t, false, true, f, browserClient, values.Encode())
|
||||
assert.EqualValues(t, http.StatusForbidden, res.StatusCode)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken,
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken,
|
||||
json.RawMessage(gjson.Get(actual, "error").Raw), "%s", actual)
|
||||
})
|
||||
|
||||
|
|
@ -727,7 +729,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
|
||||
values := url.Values{
|
||||
"method": {"password"}, "identifier": {identifier},
|
||||
"password": {pwd}, "csrf_token": {x.FakeCSRFToken},
|
||||
"password": {pwd}, "csrf_token": {nosurfx.FakeCSRFToken},
|
||||
}.Encode()
|
||||
|
||||
body1, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)
|
||||
|
|
@ -748,7 +750,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
browserClient := testhelpers.NewClientWithCookies(t)
|
||||
f := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)
|
||||
|
||||
values := url.Values{"method": {"password"}, "identifier": {strings.ToUpper(identifier)}, "password": {pwd}, "csrf_token": {x.FakeCSRFToken}}.Encode()
|
||||
values := url.Values{"method": {"password"}, "identifier": {strings.ToUpper(identifier)}, "password": {pwd}, "csrf_token": {nosurfx.FakeCSRFToken}}.Encode()
|
||||
|
||||
body, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)
|
||||
|
||||
|
|
@ -762,7 +764,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
|
||||
browserClient := testhelpers.NewClientWithCookies(t)
|
||||
f := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, true, false, false)
|
||||
values := url.Values{"method": {"password"}, "identifier": {strings.ToUpper(identifier)}, "password": {pwd}, "csrf_token": {x.FakeCSRFToken}}.Encode()
|
||||
values := url.Values{"method": {"password"}, "identifier": {strings.ToUpper(identifier)}, "password": {pwd}, "csrf_token": {nosurfx.FakeCSRFToken}}.Encode()
|
||||
body, res := testhelpers.LoginMakeRequest(t, false, true, f, browserClient, values)
|
||||
|
||||
assert.EqualValues(t, http.StatusOK, res.StatusCode)
|
||||
|
|
@ -789,7 +791,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
browserClient := testhelpers.NewClientWithCookies(t)
|
||||
f := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)
|
||||
|
||||
values := url.Values{"method": {"password"}, "password_identifier": {strings.ToUpper(identifier)}, "password": {pwd}, "csrf_token": {x.FakeCSRFToken}}.Encode()
|
||||
values := url.Values{"method": {"password"}, "password_identifier": {strings.ToUpper(identifier)}, "password": {pwd}, "csrf_token": {nosurfx.FakeCSRFToken}}.Encode()
|
||||
|
||||
body, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)
|
||||
|
||||
|
|
@ -804,7 +806,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
browserClient := testhelpers.NewClientWithCookies(t)
|
||||
f := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)
|
||||
|
||||
values := url.Values{"method": {"password"}, "identifier": {" " + identifier + " "}, "password": {pwd}, "csrf_token": {x.FakeCSRFToken}}.Encode()
|
||||
values := url.Values{"method": {"password"}, "identifier": {" " + identifier + " "}, "password": {pwd}, "csrf_token": {nosurfx.FakeCSRFToken}}.Encode()
|
||||
|
||||
body, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)
|
||||
|
||||
|
|
@ -1165,6 +1167,70 @@ func TestCompleteLogin(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("case=custom hook payload", func(t *testing.T) {
|
||||
var rawBody []byte
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
rawBody, err = io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
_ = r.Body.Close()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{"status":"password_match"}`))
|
||||
}))
|
||||
|
||||
t.Cleanup(ts.Close)
|
||||
require.NoError(t, reg.Config().Set(ctx, config.ViperKeyPasswordMigrationHook, map[string]any{
|
||||
"config": map[string]any{
|
||||
"url": ts.URL,
|
||||
"body": "base64://" + base64.StdEncoding.EncodeToString([]byte(`function(ctx) ctx`)),
|
||||
},
|
||||
}))
|
||||
|
||||
identifier := x.NewUUID().String() + "@google.com"
|
||||
identityID := x.NewUUID()
|
||||
values := func(v url.Values) {
|
||||
v.Set("identifier", identifier)
|
||||
v.Set("method", identity.CredentialsTypePassword.String())
|
||||
v.Set("password", x.NewUUID().String())
|
||||
}
|
||||
|
||||
require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, &identity.Identity{
|
||||
ID: identityID,
|
||||
SchemaID: "migration",
|
||||
Traits: identity.Traits(fmt.Sprintf(`{"email":"%s"}`, identifier)),
|
||||
Credentials: map[identity.CredentialsType]identity.Credentials{
|
||||
identity.CredentialsTypePassword: {
|
||||
Type: identity.CredentialsTypePassword,
|
||||
Identifiers: []string{identifier},
|
||||
Config: sqlxx.JSONRawMessage(`{"use_password_migration_hook": true}`),
|
||||
},
|
||||
},
|
||||
VerifiableAddresses: []identity.VerifiableAddress{
|
||||
{
|
||||
ID: x.NewUUID(),
|
||||
Value: identifier,
|
||||
Verified: true,
|
||||
IdentityID: identityID,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
browserClient := testhelpers.NewClientWithCookies(t)
|
||||
body := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,
|
||||
false, false, http.StatusOK, redirTS.URL)
|
||||
assert.Equalf(t, identifier, gjson.Get(body, "identity.traits.email").String(), "%s", body)
|
||||
|
||||
for _, path := range []string{
|
||||
"identifier", "password",
|
||||
"identity", "identity.traits",
|
||||
"flow", "flow.id",
|
||||
"request_headers", "request_cookies", "request_method", "request_url",
|
||||
} {
|
||||
assert.Truef(t, gjson.GetBytes(rawBody, path).Exists(), "%s does not exist in %s", path, rawBody)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,20 +12,16 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/phayes/freeport"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
hydraclientgo "github.com/ory/hydra-client-go/v2"
|
||||
"github.com/ory/x/logrusx"
|
||||
"github.com/ory/x/resilience"
|
||||
"github.com/ory/x/urlx"
|
||||
|
||||
"github.com/phayes/freeport"
|
||||
)
|
||||
|
||||
type clientAppConfig struct {
|
||||
|
|
@ -171,23 +167,15 @@ func newHydra(t *testing.T, loginUI string, consentUI string) (hydraAdmin string
|
|||
Follow: true,
|
||||
Container: hydraResource.Container.ID,
|
||||
})
|
||||
hl := logrusx.New("hydra-ready-check", "hydra-ready-check")
|
||||
err = resilience.Retry(hl, time.Second*1, time.Second*5, func() error {
|
||||
pr := hydraPublic + "/health/ready"
|
||||
res, err := http.DefaultClient.Get(pr)
|
||||
if err != nil || res.StatusCode != 200 {
|
||||
return errors.Errorf("Hydra public is not ready at %s", pr)
|
||||
}
|
||||
require.EventuallyWithT(t, func(t *assert.CollectT) {
|
||||
res, err := http.DefaultClient.Get(hydraPublic + "/health/ready")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, res.StatusCode)
|
||||
|
||||
ar := hydraAdmin + "/health/ready"
|
||||
res, err = http.DefaultClient.Get(ar)
|
||||
if err != nil && res.StatusCode != 200 {
|
||||
return errors.Errorf("Hydra admin is not ready at %s", ar)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
res, err = http.DefaultClient.Get(hydraAdmin + "/health/ready")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, res.StatusCode)
|
||||
}, 5*time.Second, time.Second)
|
||||
|
||||
t.Logf("Ory Hydra running at: %s %s", hydraPublic, hydraAdmin)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/urfave/negroni"
|
||||
|
|
@ -100,7 +102,7 @@ func TestOAuth2Provider(t *testing.T) {
|
|||
lf := testhelpers.GetLoginFlow(t, c.browserClient, c.kratosPublicTS, flowID)
|
||||
require.NotNil(t, lf)
|
||||
|
||||
values := url.Values{"method": {"password"}, "identifier": {c.identifier}, "password": {c.password}, "csrf_token": {x.FakeCSRFToken}}.Encode()
|
||||
values := url.Values{"method": {"password"}, "identifier": {c.identifier}, "password": {c.password}, "csrf_token": {nosurfx.FakeCSRFToken}}.Encode()
|
||||
_, res := testhelpers.LoginMakeRequest(t, false, false, lf, c.browserClient, values)
|
||||
assert.EqualValues(t, http.StatusOK, res.StatusCode)
|
||||
return
|
||||
|
|
@ -211,7 +213,7 @@ func TestOAuth2Provider(t *testing.T) {
|
|||
loginToAccount := func(t *testing.T, browserClient *http.Client, identifier, pwd string) {
|
||||
f := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, kratosPublicTS, false, false, false, false)
|
||||
|
||||
values := url.Values{"method": {"password"}, "identifier": {identifier}, "password": {pwd}, "csrf_token": {x.FakeCSRFToken}}.Encode()
|
||||
values := url.Values{"method": {"password"}, "identifier": {identifier}, "password": {pwd}, "csrf_token": {nosurfx.FakeCSRFToken}}.Encode()
|
||||
|
||||
body, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ory/x/snapshotx"
|
||||
|
|
@ -676,7 +678,7 @@ func TestRegistration(t *testing.T) {
|
|||
Action: conf.SelfPublicURL(ctx).String() + registration.RouteSubmitFlow + "?flow=" + f.Id,
|
||||
Method: "POST",
|
||||
Nodes: node.Nodes{
|
||||
node.NewCSRFNode(x.FakeCSRFToken),
|
||||
node.NewCSRFNode(nosurfx.FakeCSRFToken),
|
||||
node.NewInputField("traits.username", nil, node.DefaultGroup, node.InputAttributeTypeText),
|
||||
node.NewInputField("password", nil, node.PasswordGroup, node.InputAttributeTypePassword, node.WithRequiredInputAttribute, node.WithInputAttributes(func(a *node.InputAttributes) {
|
||||
a.Autocomplete = node.InputAttributeAutocompleteNewPassword
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
|
||||
"github.com/ory/kratos/internal/settingshelpers"
|
||||
|
|
@ -275,7 +277,7 @@ func TestSettings(t *testing.T) {
|
|||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
assert.Contains(t, res.Request.URL.String(), conf.GetProvider(ctx).String(config.ViperKeySelfServiceErrorUI))
|
||||
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken, json.RawMessage(actual), "%s", actual)
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken, json.RawMessage(actual), "%s", actual)
|
||||
})
|
||||
|
||||
t.Run("case=should pass even without CSRF token/type=spa", func(t *testing.T) {
|
||||
|
|
@ -288,7 +290,7 @@ func TestSettings(t *testing.T) {
|
|||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken, json.RawMessage(gjson.Get(actual, "error").Raw), "%s", actual)
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken, json.RawMessage(gjson.Get(actual, "error").Raw), "%s", actual)
|
||||
})
|
||||
|
||||
t.Run("case=should pass even without CSRF token/type=api", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
|
@ -37,8 +39,8 @@ var (
|
|||
type registrationStrategyDependencies interface {
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.HTTPClientProvider
|
||||
x.TracingProvider
|
||||
jsonnetsecure.VMProvider
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/x/otelx"
|
||||
|
||||
"github.com/ory/jsonschema/v3"
|
||||
|
|
@ -38,8 +40,8 @@ var _ settings.Strategy = new(Strategy)
|
|||
|
||||
type (
|
||||
strategyDependencies interface {
|
||||
x.CSRFProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
x.WriterProvider
|
||||
x.LoggingProvider
|
||||
x.TracingProvider
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow/registration"
|
||||
"github.com/ory/kratos/selfservice/strategy/code"
|
||||
"github.com/ory/kratos/selfservice/strategy/oidc"
|
||||
|
|
@ -139,7 +141,7 @@ func TestStrategyTraits(t *testing.T) {
|
|||
actual, res := testhelpers.SettingsMakeRequest(t, false, false, f, browserUser1,
|
||||
url.Values{"traits.booly": {"true"}, "csrf_token": {"invalid"}, "method": {"profile"}}.Encode())
|
||||
assert.EqualValues(t, http.StatusOK, res.StatusCode, "should return a 400 error because CSRF token is not set\n\t%s", actual)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken, json.RawMessage(actual), "%s", actual)
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken, json.RawMessage(actual), "%s", actual)
|
||||
})
|
||||
|
||||
t.Run("description=should fail to post data if CSRF is invalid/type=spa", func(t *testing.T) {
|
||||
|
|
@ -150,7 +152,7 @@ func TestStrategyTraits(t *testing.T) {
|
|||
actual, res := testhelpers.SettingsMakeRequest(t, false, true, f, browserUser1,
|
||||
testhelpers.EncodeFormAsJSON(t, true, url.Values{"traits.booly": {"true"}, "csrf_token": {"invalid"}, "method": {"profile"}}))
|
||||
assert.EqualValues(t, http.StatusForbidden, res.StatusCode, "should return a 400 error because CSRF token is not set\n\t%s", actual)
|
||||
assertx.EqualAsJSON(t, x.ErrInvalidCSRFToken, json.RawMessage(gjson.Get(actual, "error").Raw), "%s", actual)
|
||||
assertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFToken, json.RawMessage(gjson.Get(actual, "error").Raw), "%s", actual)
|
||||
})
|
||||
|
||||
t.Run("description=should not fail because of CSRF token but because of unprivileged/type=api", func(t *testing.T) {
|
||||
|
|
@ -158,7 +160,7 @@ func TestStrategyTraits(t *testing.T) {
|
|||
|
||||
f := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)
|
||||
|
||||
actual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiUser1, `{"traits.booly":true,"method":"profile","csrf_token":"`+x.FakeCSRFToken+`"}`)
|
||||
actual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiUser1, `{"traits.booly":true,"method":"profile","csrf_token":"`+nosurfx.FakeCSRFToken+`"}`)
|
||||
require.Len(t, res.Cookies(), 1)
|
||||
assert.Equal(t, "ory_kratos_continuity", res.Cookies()[0].Name)
|
||||
assert.EqualValues(t, http.StatusForbidden, res.StatusCode)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
|
||||
"github.com/ory/x/assertx"
|
||||
|
|
@ -410,7 +412,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
}, id, "")
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), errTS.URL)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
})
|
||||
|
||||
t.Run("type=spa", func(t *testing.T) {
|
||||
|
|
@ -420,7 +422,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
}, id, "")
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -435,7 +437,7 @@ func TestCompleteLogin(t *testing.T) {
|
|||
cred, ok := id.GetCredentials(identity.CredentialsTypePassword)
|
||||
require.True(t, ok)
|
||||
values := url.Values{"method": {"password"}, "password_identifier": {cred.Identifiers[0]},
|
||||
"password": {pwd}, "csrf_token": {x.FakeCSRFToken}}.Encode()
|
||||
"password": {pwd}, "csrf_token": {nosurfx.FakeCSRFToken}}.Encode()
|
||||
|
||||
body, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)
|
||||
require.Contains(t, res.Request.URL.Path, "login", "%s", res.Request.URL.String())
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
|
|
@ -127,7 +129,7 @@ func TestCompleteSettings(t *testing.T) {
|
|||
}, id)
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), errTS.URL)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
})
|
||||
|
||||
t.Run("type=spa", func(t *testing.T) {
|
||||
|
|
@ -137,7 +139,7 @@ func TestCompleteSettings(t *testing.T) {
|
|||
}, id)
|
||||
|
||||
assert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pquerna/otp"
|
||||
|
||||
|
|
@ -33,8 +35,8 @@ var (
|
|||
type totpStrategyDependencies interface {
|
||||
x.LoggingProvider
|
||||
x.WriterProvider
|
||||
x.CSRFTokenGeneratorProvider
|
||||
x.CSRFProvider
|
||||
nosurfx.CSRFTokenGeneratorProvider
|
||||
nosurfx.CSRFProvider
|
||||
x.TracingProvider
|
||||
|
||||
config.Provider
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
|
||||
"github.com/ory/x/snapshotx"
|
||||
|
|
@ -240,10 +242,10 @@ func TestCompleteSettings(t *testing.T) {
|
|||
}, id)
|
||||
if spa {
|
||||
assert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "error.reason").String(), body)
|
||||
} else {
|
||||
assert.Contains(t, res.Request.URL.String(), errTS.URL)
|
||||
assert.Equal(t, x.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
assert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, "reason").String(), body)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue