Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions driver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const (
ViperKeyUseLegacyShowVerificationUI = "feature_flags.legacy_continue_with_verification_ui"
ViperKeyLegacyOIDCRegistrationGroup = "feature_flags.legacy_oidc_registration_node_group"
ViperKeyUseLegacyRequireVerifiedLoginError = "feature_flags.legacy_require_verified_login_error"
ViperKeyDisableVerificationHookAutoInjection = "feature_flags.disable_verification_hook_auto_injection"
ViperKeySessionRefreshMinTimeLeft = "session.earliest_possible_extend"
ViperKeyCookieSameSite = "cookies.same_site"
ViperKeyCookieDomain = "cookies.domain"
Expand Down Expand Up @@ -737,6 +738,10 @@ func (p *Config) UseLegacyRequireVerifiedLoginError(ctx context.Context) bool {
return p.GetProvider(ctx).Bool(ViperKeyUseLegacyRequireVerifiedLoginError)
}

func (p *Config) SelfServiceVerificationHookAutoInjectionDisabled(ctx context.Context) bool {
return p.GetProvider(ctx).Bool(ViperKeyDisableVerificationHookAutoInjection)
}

func (p *Config) SelfServiceFlowRecoveryEnabled(ctx context.Context) bool {
return p.GetProvider(ctx).Bool(ViperKeySelfServiceRecoveryEnabled)
}
Expand Down
11 changes: 11 additions & 0 deletions driver/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,17 @@ func TestSession(t *testing.T) {
assert.Equal(t, true, p.SessionWhoAmICaching(ctx))
}

func TestVerificationHookAutoInjection(t *testing.T) {
t.Parallel()
ctx := context.Background()
l := logrusx.New("", "")
p := config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())

assert.Equal(t, false, p.SelfServiceVerificationHookAutoInjectionDisabled(ctx))
p.MustSet(ctx, config.ViperKeyDisableVerificationHookAutoInjection, true)
assert.Equal(t, true, p.SelfServiceVerificationHookAutoInjectionDisabled(ctx))
}

func TestCookies(t *testing.T) {
t.Parallel()
ctx := context.Background()
Expand Down
3 changes: 1 addition & 2 deletions driver/registry_default_registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ func (m *RegistryDefault) PostRegistrationPostPersistHooks(ctx context.Context,
}
}

// WARNING - If you remove this, no verification emails / sms will be sent post-registration.
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
if m.Config().SelfServiceFlowVerificationEnabled(ctx) && !m.Config().SelfServiceVerificationHookAutoInjectionDisabled(ctx) {
hooks = slices.Insert(hooks, 0, registration.PostHookPostPersistExecutor(m.HookVerifier()))
}

Expand Down
2 changes: 1 addition & 1 deletion driver/registry_default_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (m *RegistryDefault) PostSettingsPostPersistHooks(ctx context.Context, sett
}
}

if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
if m.Config().SelfServiceFlowVerificationEnabled(ctx) && !m.Config().SelfServiceVerificationHookAutoInjectionDisabled(ctx) {
hooks = slices.Insert(hooks, 0, settings.PostHookPostPersistExecutor(m.HookVerifier()))
}

Expand Down
64 changes: 64 additions & 0 deletions driver/registry_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,38 @@ func TestDriverDefault_Hooks(t *testing.T) {
}
},
},
{
uc: "Verification enabled but auto-injection disabled",
config: map[string]any{
config.ViperKeySelfServiceVerificationEnabled: true,
config.ViperKeyDisableVerificationHookAutoInjection: true,
config.ViperKeySelfServiceRegistrationAfter + ".password.hooks": []map[string]any{
{"hook": "session"},
},
},
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
return []registration.PostHookPostPersistExecutor{
hook.NewSessionIssuer(reg),
}
},
},
{
uc: "Auto-injection disabled but verification hook explicitly configured",
config: map[string]any{
config.ViperKeySelfServiceVerificationEnabled: true,
config.ViperKeyDisableVerificationHookAutoInjection: true,
config.ViperKeySelfServiceRegistrationAfter + ".password.hooks": []map[string]any{
{"hook": "verification"},
{"hook": "session"},
},
},
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
return []registration.PostHookPostPersistExecutor{
hook.NewVerifier(reg),
hook.NewSessionIssuer(reg),
}
},
},
} {
t.Run(fmt.Sprintf("after/uc=%s", tc.uc), func(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -608,6 +640,38 @@ func TestDriverDefault_Hooks(t *testing.T) {
}
},
},
{
uc: "Verification enabled but auto-injection disabled",
config: map[string]any{
config.ViperKeySelfServiceVerificationEnabled: true,
config.ViperKeyDisableVerificationHookAutoInjection: true,
config.ViperKeySelfServiceSettingsAfter + ".profile.hooks": []map[string]any{
{"hook": "web_hook", "config": map[string]any{"url": "foo", "method": "POST", "headers": map[string]string{"X-Custom-Header": "test"}}},
},
},
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
return []settings.PostHookPostPersistExecutor{
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
}
},
},
{
uc: "Auto-injection disabled but verification hook explicitly configured",
config: map[string]any{
config.ViperKeySelfServiceVerificationEnabled: true,
config.ViperKeyDisableVerificationHookAutoInjection: true,
config.ViperKeySelfServiceSettingsAfter + ".profile.hooks": []map[string]any{
{"hook": "verification"},
{"hook": "web_hook", "config": map[string]any{"url": "foo", "method": "POST", "headers": map[string]string{"X-Custom-Header": "test"}}},
},
},
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
return []settings.PostHookPostPersistExecutor{
hook.NewVerifier(reg),
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
}
},
},
} {
t.Run(fmt.Sprintf("after/uc=%s", tc.uc), func(t *testing.T) {
t.Parallel()
Expand Down
12 changes: 12 additions & 0 deletions embedx/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,9 @@
{
"$ref": "#/definitions/selfServiceShowVerificationUIHook"
},
{
"$ref": "#/definitions/selfServiceVerificationHook"
},
{
"$ref": "#/definitions/b2bSSOHook"
},
Expand Down Expand Up @@ -929,6 +932,9 @@
{
"$ref": "#/definitions/selfServiceShowVerificationUIHook"
},
{
"$ref": "#/definitions/selfServiceVerificationHook"
},
{
"$ref": "#/definitions/b2bSSOHook"
}
Expand Down Expand Up @@ -3247,6 +3253,12 @@
"description": "The node group to use for registration flows. Previously, the node group for the oidc method's profile fields was `oidc`. Going forward, it will be `default`. This switch can toggle between those two for backwards compatibility and will be removed in the future.",
"default": false,
"type": "boolean"
},
"disable_verification_hook_auto_injection": {
"type": "boolean",
"title": "Disable automatic injection of the verification hook",
"description": "If true, the verification hook will not be automatically prepended to registration and settings post-persist hooks when verification is enabled. Operators can still explicitly add {\"hook\": \"verification\"} to their after-hooks configuration.",
"default": false
}
},
"additionalProperties": false
Expand Down
24 changes: 24 additions & 0 deletions selfservice/hook/verification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,30 @@ func TestVerifier(t *testing.T) {
})
}

t.Run("case=should not send verification emails when auto-injection is disabled", func(t *testing.T) {
t.Parallel()
conf, reg := pkg.NewFastRegistryWithMocks(t)
testhelpers.SetDefaultIdentitySchema(conf, "file://./stub/verify.schema.json")
conf.MustSet(ctx, config.ViperKeyPublicBaseURL, "https://www.ory.com/")
conf.MustSet(ctx, config.ViperKeyCourierSMTPURL, "smtp://foo@bar@dev.null/")
conf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)
conf.MustSet(ctx, config.ViperKeyDisableVerificationHookAutoInjection, true)

i := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)
i.Traits = identity.Traits(`{"emails":["foo@ory.sh","bar@ory.sh"]}`)
require.NoError(t, reg.IdentityManager().Create(context.Background(), i))

hooks, err := reg.PostRegistrationPostPersistHooks(ctx, identity.CredentialsTypePassword)
require.NoError(t, err)
for _, h := range hooks {
assert.IsNotType(t, &hook.Verifier{}, h, "verifier hook should not be auto-injected when flag is set")
}

messages, err := reg.CourierPersister().NextMessages(context.Background(), 12)
require.EqualError(t, err, courier.ErrQueueEmpty.Error())
require.Len(t, messages, 0)
})

t.Run("flow=login/case=does not run if aal is not 1", func(t *testing.T) {
t.Parallel()
_, reg := pkg.NewFastRegistryWithMocks(t)
Expand Down
Loading