Skip to content

Commit 3b8cfcf

Browse files
committed
feat: allow disabling verification hook auto-inject
1 parent c445e40 commit 3b8cfcf

7 files changed

Lines changed: 111 additions & 3 deletions

File tree

driver/config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ const (
106106
ViperKeyUseLegacyShowVerificationUI = "feature_flags.legacy_continue_with_verification_ui"
107107
ViperKeyLegacyOIDCRegistrationGroup = "feature_flags.legacy_oidc_registration_node_group"
108108
ViperKeyUseLegacyRequireVerifiedLoginError = "feature_flags.legacy_require_verified_login_error"
109+
ViperKeyDisableVerificationHookAutoInjection = "feature_flags.disable_verification_hook_auto_injection"
109110
ViperKeySessionRefreshMinTimeLeft = "session.earliest_possible_extend"
110111
ViperKeyCookieSameSite = "cookies.same_site"
111112
ViperKeyCookieDomain = "cookies.domain"
@@ -737,6 +738,10 @@ func (p *Config) UseLegacyRequireVerifiedLoginError(ctx context.Context) bool {
737738
return p.GetProvider(ctx).Bool(ViperKeyUseLegacyRequireVerifiedLoginError)
738739
}
739740

741+
func (p *Config) SelfServiceVerificationHookAutoInjectionDisabled(ctx context.Context) bool {
742+
return p.GetProvider(ctx).Bool(ViperKeyDisableVerificationHookAutoInjection)
743+
}
744+
740745
func (p *Config) SelfServiceFlowRecoveryEnabled(ctx context.Context) bool {
741746
return p.GetProvider(ctx).Bool(ViperKeySelfServiceRecoveryEnabled)
742747
}

driver/config/config_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,10 @@ func TestSession(t *testing.T) {
637637
assert.Equal(t, false, p.SessionWhoAmICaching(ctx))
638638
p.MustSet(ctx, config.ViperKeySessionWhoAmICaching, true)
639639
assert.Equal(t, true, p.SessionWhoAmICaching(ctx))
640+
641+
assert.Equal(t, false, p.SelfServiceVerificationHookAutoInjectionDisabled(ctx))
642+
p.MustSet(ctx, config.ViperKeyDisableVerificationHookAutoInjection, true)
643+
assert.Equal(t, true, p.SelfServiceVerificationHookAutoInjectionDisabled(ctx))
640644
}
641645

642646
func TestCookies(t *testing.T) {

driver/registry_default_registration.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ func (m *RegistryDefault) PostRegistrationPostPersistHooks(ctx context.Context,
3535
}
3636
}
3737

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

driver/registry_default_settings.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func (m *RegistryDefault) PostSettingsPostPersistHooks(ctx context.Context, sett
3333
}
3434
}
3535

36-
if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
36+
if m.Config().SelfServiceFlowVerificationEnabled(ctx) && !m.Config().SelfServiceVerificationHookAutoInjectionDisabled(ctx) {
3737
hooks = slices.Insert(hooks, 0, settings.PostHookPostPersistExecutor(m.HookVerifier()))
3838
}
3939

driver/registry_default_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,38 @@ func TestDriverDefault_Hooks(t *testing.T) {
334334
}
335335
},
336336
},
337+
{
338+
uc: "Verification enabled but auto-injection disabled",
339+
config: map[string]any{
340+
config.ViperKeySelfServiceVerificationEnabled: true,
341+
config.ViperKeyDisableVerificationHookAutoInjection: true,
342+
config.ViperKeySelfServiceRegistrationAfter + ".password.hooks": []map[string]any{
343+
{"hook": "session"},
344+
},
345+
},
346+
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
347+
return []registration.PostHookPostPersistExecutor{
348+
hook.NewSessionIssuer(reg),
349+
}
350+
},
351+
},
352+
{
353+
uc: "Auto-injection disabled but verification hook explicitly configured",
354+
config: map[string]any{
355+
config.ViperKeySelfServiceVerificationEnabled: true,
356+
config.ViperKeyDisableVerificationHookAutoInjection: true,
357+
config.ViperKeySelfServiceRegistrationAfter + ".password.hooks": []map[string]any{
358+
{"hook": "verification"},
359+
{"hook": "session"},
360+
},
361+
},
362+
expect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {
363+
return []registration.PostHookPostPersistExecutor{
364+
hook.NewVerifier(reg),
365+
hook.NewSessionIssuer(reg),
366+
}
367+
},
368+
},
337369
} {
338370
t.Run(fmt.Sprintf("after/uc=%s", tc.uc), func(t *testing.T) {
339371
t.Parallel()
@@ -608,6 +640,38 @@ func TestDriverDefault_Hooks(t *testing.T) {
608640
}
609641
},
610642
},
643+
{
644+
uc: "Verification enabled but auto-injection disabled",
645+
config: map[string]any{
646+
config.ViperKeySelfServiceVerificationEnabled: true,
647+
config.ViperKeyDisableVerificationHookAutoInjection: true,
648+
config.ViperKeySelfServiceSettingsAfter + ".profile.hooks": []map[string]any{
649+
{"hook": "web_hook", "config": map[string]any{"url": "foo", "method": "POST", "headers": map[string]string{"X-Custom-Header": "test"}}},
650+
},
651+
},
652+
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
653+
return []settings.PostHookPostPersistExecutor{
654+
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
655+
}
656+
},
657+
},
658+
{
659+
uc: "Auto-injection disabled but verification hook explicitly configured",
660+
config: map[string]any{
661+
config.ViperKeySelfServiceVerificationEnabled: true,
662+
config.ViperKeyDisableVerificationHookAutoInjection: true,
663+
config.ViperKeySelfServiceSettingsAfter + ".profile.hooks": []map[string]any{
664+
{"hook": "verification"},
665+
{"hook": "web_hook", "config": map[string]any{"url": "foo", "method": "POST", "headers": map[string]string{"X-Custom-Header": "test"}}},
666+
},
667+
},
668+
expect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {
669+
return []settings.PostHookPostPersistExecutor{
670+
hook.NewVerifier(reg),
671+
hook.NewWebHook(reg, &request.Config{Method: "POST", URL: "foo", Headers: map[string]string{"X-Custom-Header": "test"}}),
672+
}
673+
},
674+
},
611675
} {
612676
t.Run(fmt.Sprintf("after/uc=%s", tc.uc), func(t *testing.T) {
613677
t.Parallel()

embedx/config.schema.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,9 @@
800800
{
801801
"$ref": "#/definitions/selfServiceShowVerificationUIHook"
802802
},
803+
{
804+
"$ref": "#/definitions/selfServiceVerificationHook"
805+
},
803806
{
804807
"$ref": "#/definitions/b2bSSOHook"
805808
},
@@ -929,6 +932,9 @@
929932
{
930933
"$ref": "#/definitions/selfServiceShowVerificationUIHook"
931934
},
935+
{
936+
"$ref": "#/definitions/selfServiceVerificationHook"
937+
},
932938
{
933939
"$ref": "#/definitions/b2bSSOHook"
934940
}
@@ -3247,6 +3253,12 @@
32473253
"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.",
32483254
"default": false,
32493255
"type": "boolean"
3256+
},
3257+
"disable_verification_hook_auto_injection": {
3258+
"type": "boolean",
3259+
"title": "Disable automatic injection of the verification hook",
3260+
"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.",
3261+
"default": false
32503262
}
32513263
},
32523264
"additionalProperties": false

selfservice/hook/verification_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,30 @@ func TestVerifier(t *testing.T) {
149149
require.NoError(t, tc.execHook(h, i, originalFlow))
150150
assert.Empty(t, originalFlow.ContinueWith(), "%#ßv", originalFlow.ContinueWith())
151151
})
152+
153+
t.Run("case=should not send verification emails when auto-injection is disabled", func(t *testing.T) {
154+
t.Parallel()
155+
conf, reg := pkg.NewFastRegistryWithMocks(t)
156+
testhelpers.SetDefaultIdentitySchema(conf, "file://./stub/verify.schema.json")
157+
conf.MustSet(ctx, config.ViperKeyPublicBaseURL, "https://www.ory.com/")
158+
conf.MustSet(ctx, config.ViperKeyCourierSMTPURL, "smtp://foo@bar@dev.null/")
159+
conf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)
160+
conf.MustSet(ctx, config.ViperKeyDisableVerificationHookAutoInjection, true)
161+
162+
i := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)
163+
i.Traits = identity.Traits(`{"emails":["foo@ory.sh","bar@ory.sh"]}`)
164+
require.NoError(t, reg.IdentityManager().Create(context.Background(), i))
165+
166+
hooks, err := reg.PostRegistrationPostPersistHooks(ctx, identity.CredentialsTypePassword)
167+
require.NoError(t, err)
168+
for _, h := range hooks {
169+
assert.IsNotType(t, &hook.Verifier{}, h, "verifier hook should not be auto-injected when flag is set")
170+
}
171+
172+
messages, err := reg.CourierPersister().NextMessages(context.Background(), 12)
173+
require.EqualError(t, err, courier.ErrQueueEmpty.Error())
174+
require.Len(t, messages, 0)
175+
})
152176
})
153177
}
154178

0 commit comments

Comments
 (0)