Repository: ory/kratos
Pull request #2695 (2022-08-25 12:49:34)

feat: add flow id check to use verification token
<!--
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.

This text will be included in the changelog. If applicable, include links to documentation or pieces of code.
If your change includes breaking changes please add a codeblock documenting the breaking change:

```
BREAKING CHANGES: This patch changes the behavior of configuration item `foo` to do bar. To keep the existing
behavior please do baz.
```
-->

## Related issue(s)
#2679 

<!--
If this pull request

1. is a fix for a known bug, link the issue where the bug was reported in the format of `#1234`;
2. is a fix for a previously unknown bug, explain the bug and how to reproduce it in this pull request;
3. implements a new feature, link the issue containing the design document in the format of `#1234`;
4. improves the documentation, no issue reference is required.

Pull requests introducing new features, which do not have a design document linked are more likely to be rejected and take on average 2-8 weeks longer to
get merged.

You can discuss changes with maintainers either in the Github Discusssions in this repository or
join the [Ory Chat](https://www.ory.sh/chat).
-->

## Checklist

<!--
Put an `x` in the boxes that apply. You can also fill these out after creating the PR.

Please be aware that pull requests must have all boxes ticked in order to be merged.

If you're unsure about any of them, don't hesitate to ask. We're here to help!
-->

- [ ] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).
- [ ] I have referenced an issue containing the design document if my change introduces a new feature.
- [ ] I am following the [contributing code guidelines](../blob/master/CONTRIBUTING.md#contributing-code).
- [ ] I have read the [security policy](../security/policy).
- [ ] I confirm that this pull request does not address a security vulnerability. 
      If this pull request addresses a security. vulnerability, 
      I confirm that I got green light (please contact [security@ory.sh](mailto:security@ory.sh)) from the maintainers to push the changes.
- [ ] I have added tests that prove my fix is effective or that my feature works.
- [ ] I have added or changed [the documentation](https://github.com/ory/docs).

## Further Comments

<!--
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution
you did and what alternatives you considered, etc...
-->



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f2cd8b81..b19f75abc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**
 
-- [ (2022-08-24)](#2022-08-24)
+- [ (2022-09-07)](#2022-09-07)
   - [Breaking Changes](#breaking-changes)
     - [Bug Fixes](#bug-fixes)
     - [Code Refactoring](#code-refactoring)
@@ -264,10 +264,13 @@
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
-# [](https://github.com/ory/kratos/compare/v0.10.1...v) (2022-08-24)
+# [](https://github.com/ory/kratos/compare/v0.10.1...v) (2022-09-07)
 
 ## Breaking Changes
 
+This patch invalidates recovery flows initiated using the Admin API. Please
+re-generate any admin-generated recovery flows and tokens.
+
 This is a breaking change, as it removes the `courier.message_ttl` config key
 and replaces it with a counter `courier.message_retries`.
 
@@ -307,6 +310,9 @@ SDK Method `getJsonSchema` was renamed to `getIdentitySchema`.
   ([#2613](https://github.com/ory/kratos/issues/2613))
   ([29aa3b6](https://github.com/ory/kratos/commit/29aa3b6c37b3a173dcfeb02fdad4abc83774bc0b)),
   closes [#2591](https://github.com/ory/kratos/issues/2591)
+- Do not invalidate recovery addr on update
+  ([#2699](https://github.com/ory/kratos/issues/2699))
+  ([1689bb9](https://github.com/ory/kratos/commit/1689bb9f0a52387f699568da6bc773929b1201ae))
 - **docker:** Add missing dependencies
   ([#2643](https://github.com/ory/kratos/issues/2643))
   ([c589520](https://github.com/ory/kratos/commit/c589520ff865cefdb287e597b9e858851a778755))
@@ -323,6 +329,8 @@ SDK Method `getJsonSchema` was renamed to `getIdentitySchema`.
   ([#2517](https://github.com/ory/kratos/issues/2517))
   ([c058e23](https://github.com/ory/kratos/commit/c058e23599d994e12b676e87f7282c1f2b2e089c)),
   closes [#2426](https://github.com/ory/kratos/issues/2426)
+- Ignore commata in HIBP response
+  ([0856bd7](https://github.com/ory/kratos/commit/0856bd719b7e06a6d2163bf428ff6513d86376db))
 - Ignore CSRF for session extension on public route
   ([866b472](https://github.com/ory/kratos/commit/866b472750fba7bf498d359796f24867af7270ad))
 - Ignore error explicitly
@@ -330,6 +338,16 @@ SDK Method `getJsonSchema` was renamed to `getIdentitySchema`.
 - Improve migration status speed
   ([#2637](https://github.com/ory/kratos/issues/2637))
   ([a2e3c41](https://github.com/ory/kratos/commit/a2e3c41f9e513e1de47f6320f6a10acd1fed5eea))
+- Include flow id in use recovery token query
+  ([#2679](https://github.com/ory/kratos/issues/2679))
+  ([d56586b](https://github.com/ory/kratos/commit/d56586b028d79387886f880c1455edb5e4df2209)):
+
+  This PR adds the `selfservice_recovery_flow_id` to the query used when "using"
+  a token in the recovery flow.
+
+  This PR also adds a new enum field for `identity_recovery_tokens` to
+  distinguish the two flows: admin versus self-service recovery.
+
 - Make hydra consistently localhost
   ([70211a1](https://github.com/ory/kratos/commit/70211a17a452d5ced8317822afda3f8e6185cc71))
 - Make ID field in VerifiableAddress struct optional
@@ -338,6 +356,8 @@ SDK Method `getJsonSchema` was renamed to `getIdentitySchema`.
   closes [#2506](https://github.com/ory/kratos/issues/2506)
 - Make servicelocator explicit
   ([4f841da](https://github.com/ory/kratos/commit/4f841dae5423acf3514d50add9e99d28bc339fbb))
+- Make swagger/openapi go 1.19 compatible
+  ([fec6772](https://github.com/ory/kratos/commit/fec6772739129e0d5bb4103c717b1ac60df45aa8))
 - Mark gosec false positives
   ([13eaddb](https://github.com/ory/kratos/commit/13eaddb7babe630750361c6d8f3ffc736898ddec))
 - Metadata should not be required
@@ -439,10 +459,14 @@ SDK Method `getJsonSchema` was renamed to `getIdentitySchema`.
 - Add identity id to "account disabled" error
   ([#2557](https://github.com/ory/kratos/issues/2557))
   ([f09b1b3](https://github.com/ory/kratos/commit/f09b1b3701c6deda4d25cebb7ccf2e97089be32a))
+- Add missing config entry
+  ([8fe9de6](https://github.com/ory/kratos/commit/8fe9de6d60a381611e07226614241a83b0010126))
 - Add PATCH to adminUpdateIdentity
   ([#2380](https://github.com/ory/kratos/issues/2380))
   ([#2471](https://github.com/ory/kratos/issues/2471))
   ([94a3741](https://github.com/ory/kratos/commit/94a37416011086582e309f62dc2c45ca84083a33))
+- Add pre-hooks to settings, verification, recovery
+  ([c0ceaf3](https://github.com/ory/kratos/commit/c0ceaf31f9327cca903c19b77597cae4587737e6))
 - Allow setting public and admin metadata with the jsonnet data mapper
   ([#2569](https://github.com/ory/kratos/issues/2569))
   ([aa6eb13](https://github.com/ory/kratos/commit/aa6eb13c1c42c11354074553fac9c90ee0a8999e)),
@@ -470,6 +494,9 @@ SDK Method `getJsonSchema` was renamed to `getIdentitySchema`.
   `courier.message_retries` option to limit how often the sending of a message
   is retried before it is marked as `abandoned`.
 
+- Support ip exceptions
+  ([de46c08](https://github.com/ory/kratos/commit/de46c08534dfae6165f6a570cc59829f367c0b57))
+
 ### Reverts
 
 - Revert "autogen(openapi): regenerate swagger spec and internal client"
diff --git a/driver/config/config.go b/driver/config/config.go
index d5e7d9455..ab0449a5c 100644
--- a/driver/config/config.go
+++ b/driver/config/config.go
@@ -129,10 +129,12 @@ const (
 	ViperKeySelfServiceLogoutBrowserDefaultReturnTo          = "selfservice.flows.logout.after." + DefaultBrowserReturnURL
 	ViperKeySelfServiceSettingsURL                           = "selfservice.flows.settings.ui_url"
 	ViperKeySelfServiceSettingsAfter                         = "selfservice.flows.settings.after"
+	ViperKeySelfServiceSettingsBeforeHooks                   = "selfservice.flows.settings.before.hooks"
 	ViperKeySelfServiceSettingsRequestLifespan               = "selfservice.flows.settings.lifespan"
 	ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter = "selfservice.flows.settings.privileged_session_max_age"
 	ViperKeySelfServiceSettingsRequiredAAL                   = "selfservice.flows.settings.required_aal"
 	ViperKeySelfServiceRecoveryAfter                         = "selfservice.flows.recovery.after"
+	ViperKeySelfServiceRecoveryBeforeHooks                   = "selfservice.flows.recovery.before.hooks"
 	ViperKeySelfServiceRecoveryEnabled                       = "selfservice.flows.recovery.enabled"
 	ViperKeySelfServiceRecoveryUI                            = "selfservice.flows.recovery.ui_url"
 	ViperKeySelfServiceRecoveryRequestLifespan               = "selfservice.flows.recovery.lifespan"
@@ -142,6 +144,7 @@ const (
 	ViperKeySelfServiceVerificationRequestLifespan           = "selfservice.flows.verification.lifespan"
 	ViperKeySelfServiceVerificationBrowserDefaultReturnTo    = "selfservice.flows.verification.after." + DefaultBrowserReturnURL
 	ViperKeySelfServiceVerificationAfter                     = "selfservice.flows.verification.after"
+	ViperKeySelfServiceVerificationBeforeHooks               = "selfservice.flows.verification.before.hooks"
 	ViperKeyDefaultIdentitySchemaID                          = "identity.default_schema_id"
 	ViperKeyIdentitySchemas                                  = "identity.schemas"
 	ViperKeyHasherAlgorithm                                  = "hashers.algorithm"
@@ -623,6 +626,18 @@ func (p *Config) SelfServiceFlowLoginBeforeHooks(ctx context.Context) []SelfServ
 	return p.selfServiceHooks(ctx, ViperKeySelfServiceLoginBeforeHooks)
 }
 
+func (p *Config) SelfServiceFlowRecoveryBeforeHooks(ctx context.Context) []SelfServiceHook {
+	return p.selfServiceHooks(ctx, ViperKeySelfServiceRecoveryBeforeHooks)
+}
+
+func (p *Config) SelfServiceFlowVerificationBeforeHooks(ctx context.Context) []SelfServiceHook {
+	return p.selfServiceHooks(ctx, ViperKeySelfServiceVerificationBeforeHooks)
+}
+
+func (p *Config) SelfServiceFlowSettingsBeforeHooks(ctx context.Context) []SelfServiceHook {
+	return p.selfServiceHooks(ctx, ViperKeySelfServiceSettingsBeforeHooks)
+}
+
 func (p *Config) SelfServiceFlowRegistrationBeforeHooks(ctx context.Context) []SelfServiceHook {
 	return p.selfServiceHooks(ctx, ViperKeySelfServiceRegistrationBeforeHooks)
 }
diff --git a/driver/registry_default_recovery.go b/driver/registry_default_recovery.go
index 6981edfbd..eddd170e8 100644
--- a/driver/registry_default_recovery.go
+++ b/driver/registry_default_recovery.go
@@ -50,6 +50,15 @@ 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) 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 {
diff --git a/driver/registry_default_settings.go b/driver/registry_default_settings.go
index 567c06959..22e6ad509 100644
--- a/driver/registry_default_settings.go
+++ b/driver/registry_default_settings.go
@@ -16,6 +16,15 @@ func (m *RegistryDefault) PostSettingsPrePersistHooks(ctx context.Context, setti
 	return
 }
 
+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) PostSettingsPostPersistHooks(ctx context.Context, settingsType string) (b []settings.PostHookPostPersistExecutor) {
 	initialHookCount := 0
 	if m.Config().SelfServiceFlowVerificationEnabled(ctx) {
diff --git a/driver/registry_default_verify.go b/driver/registry_default_verify.go
index 7fdca0f71..0cb1a8ab2 100644
--- a/driver/registry_default_verify.go
+++ b/driver/registry_default_verify.go
@@ -73,6 +73,15 @@ 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) 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 {
diff --git a/embedx/config.schema.json b/embedx/config.schema.json
index 0fb733ca9..423482e97 100644
--- a/embedx/config.schema.json
+++ b/embedx/config.schema.json
@@ -790,6 +790,33 @@
         }
       }
     },
+    "selfServiceBeforeSettings": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "hooks": {
+          "$ref": "#/definitions/selfServiceHooks"
+        }
+      }
+    },
+    "selfServiceBeforeRecovery": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "hooks": {
+          "$ref": "#/definitions/selfServiceHooks"
+        }
+      }
+    },
+    "selfServiceBeforeVerification": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "hooks": {
+          "$ref": "#/definitions/selfServiceHooks"
+        }
+      }
+    },
     "selfServiceAfterRegistration": {
       "type": "object",
       "additionalProperties": false,
@@ -1023,6 +1050,9 @@
                 },
                 "after": {
                   "$ref": "#/definitions/selfServiceAfterSettings"
+                },
+                "before": {
+                  "$ref": "#/definitions/selfServiceBeforeSettings"
                 }
               }
             },
@@ -1146,6 +1176,9 @@
                     "1m",
                     "1s"
                   ]
+                },
+                "before": {
+                  "$ref": "#/definitions/selfServiceBeforeVerification"
                 }
               }
             },
@@ -1184,6 +1217,9 @@
                     "1m",
                     "1s"
                   ]
+                },
+                "before": {
+                  "$ref": "#/definitions/selfServiceBeforeRecovery"
                 }
               }
             },
diff --git a/package-lock.json b/package-lock.json
index 6429c6d85..de6603327 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,7 +18,8 @@
         "got": "^11.8.2",
         "ory-prettier-styles": "1.1.2",
         "otplib": "^12.0.1",
-        "prettier": "2.3.2",
+        "prettier": "^2.7.1",
+        "prettier-plugin-packagejson": "^2.2.18",
         "typescript": "^4.4.3",
         "wait-on": "5.3.0"
       }
@@ -167,6 +168,41 @@
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
       "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
     },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
     "node_modules/@nuxtjs/opencollective": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz",
@@ -339,6 +375,16 @@
         "@types/responselike": "*"
       }
     },
+    "node_modules/@types/glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+      "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+      "dev": true,
+      "dependencies": {
+        "@types/minimatch": "*",
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/http-cache-semantics": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
@@ -354,6 +400,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/minimatch": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
+      "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
+      "dev": true
+    },
     "node_modules/@types/node": {
       "version": "16.11.24",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.24.tgz",
@@ -483,6 +535,15 @@
         "sprintf-js": "~1.0.2"
       }
     },
+    "node_modules/array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/asn1": {
       "version": "0.2.6",
       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
@@ -618,6 +679,18 @@
         "concat-map": "0.0.1"
       }
     },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/buffer": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -1203,6 +1276,36 @@
         "node": ">=0.4.0"
       }
     },
+    "node_modules/detect-indent": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
+      "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/detect-newline": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "dependencies": {
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/easy-table": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz",
@@ -1363,11 +1466,36 @@
         "node >=0.6.0"
       ]
     },
+    "node_modules/fast-glob": {
+      "version": "3.2.11",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
+      "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
     "node_modules/fast-safe-stringify": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
       "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
     },
+    "node_modules/fastq": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+      "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+      "dev": true,
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
     "node_modules/fd-slicer": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
@@ -1391,6 +1519,18 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/follow-redirects": {
       "version": "1.14.8",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
@@ -1492,6 +1632,15 @@
         "assert-plus": "^1.0.0"
       }
     },
+    "node_modules/git-hooks-list": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-1.0.3.tgz",
+      "integrity": "sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/fisker/git-hooks-list?sponsor=1"
+      }
+    },
     "node_modules/glob": {
       "version": "7.1.6",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -1511,6 +1660,18 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/global-dirs": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz",
@@ -1526,6 +1687,25 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/globby": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.0.tgz",
+      "integrity": "sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw==",
+      "dev": true,
+      "dependencies": {
+        "@types/glob": "^7.1.1",
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.0.3",
+        "glob": "^7.1.3",
+        "ignore": "^5.1.1",
+        "merge2": "^1.2.3",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/got": {
       "version": "11.8.3",
       "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
@@ -1636,6 +1816,15 @@
         }
       ]
     },
+    "node_modules/ignore": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+      "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
     "node_modules/indent-string": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@@ -1704,6 +1893,15 @@
         "is-ci": "bin.js"
       }
     },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/is-fullwidth-code-point": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -1712,6 +1910,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/is-installed-globally": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
@@ -1736,6 +1946,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
     "node_modules/is-path-inside": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
@@ -1745,6 +1964,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/is-stream": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -2004,6 +2232,28 @@
       "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
       "dev": true
     },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dev": true,
+      "dependencies": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
     "node_modules/mime-db": {
       "version": "1.51.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
@@ -2242,6 +2492,15 @@
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
       "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA=="
     },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/pend": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@@ -2254,6 +2513,18 @@
       "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
       "dev": true
     },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
     "node_modules/pify": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -2264,15 +2535,30 @@
       }
     },
     "node_modules/prettier": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
-      "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+      "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
       "dev": true,
       "bin": {
         "prettier": "bin-prettier.js"
       },
       "engines": {
         "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/prettier-plugin-packagejson": {
+      "version": "2.2.18",
+      "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.2.18.tgz",
+      "integrity": "sha512-iBjQ3IY6IayFrQHhXvg+YvKprPUUiIJ04Vr9+EbeQPfwGajznArIqrN33c5bi4JcIvmLHGROIMOm9aYakJj/CA==",
+      "dev": true,
+      "dependencies": {
+        "sort-package-json": "1.57.0"
+      },
+      "peerDependencies": {
+        "prettier": ">= 1.16.0"
       }
     },
     "node_modules/pretty-bytes": {
@@ -2327,6 +2613,26 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
     "node_modules/quick-lru": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@@ -2401,6 +2707,16 @@
         "node": ">=8"
       }
     },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true,
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/rfdc": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
@@ -2430,6 +2746,29 @@
         "node": ">=0.12.0"
       }
     },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
     "node_modules/rxjs": {
       "version": "7.5.2",
       "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz",
@@ -2508,6 +2847,15 @@
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
     },
+    "node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/slice-ansi": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
@@ -2522,6 +2870,29 @@
         "node": ">=8"
       }
     },
+    "node_modules/sort-object-keys": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz",
+      "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==",
+      "dev": true
+    },
+    "node_modules/sort-package-json": {
+      "version": "1.57.0",
+      "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-1.57.0.tgz",
+      "integrity": "sha512-FYsjYn2dHTRb41wqnv+uEqCUvBpK3jZcTp9rbz2qDTmel7Pmdtf+i2rLaaPMRZeSVM60V3Se31GyWFpmKs4Q5Q==",
+      "dev": true,
+      "dependencies": {
+        "detect-indent": "^6.0.0",
+        "detect-newline": "3.1.0",
+        "git-hooks-list": "1.0.3",
+        "globby": "10.0.0",
+        "is-plain-obj": "2.1.0",
+        "sort-object-keys": "^1.1.3"
+      },
+      "bin": {
+        "sort-package-json": "cli.js"
+      }
+    },
     "node_modules/spawn-command": {
       "version": "0.0.2-1",
       "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
@@ -2641,6 +3012,18 @@
         "node": ">=8.17.0"
       }
     },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
     "node_modules/tough-cookie": {
       "version": "2.5.0",
       "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -3048,6 +3431,32 @@
         }
       }
     },
+    "@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      }
+    },
+    "@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true
+    },
+    "@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      }
+    },
     "@nuxtjs/opencollective": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz",
@@ -3195,6 +3604,16 @@
         "@types/responselike": "*"
       }
     },
+    "@types/glob": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+      "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+      "dev": true,
+      "requires": {
+        "@types/minimatch": "*",
+        "@types/node": "*"
+      }
+    },
     "@types/http-cache-semantics": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
@@ -3210,6 +3629,12 @@
         "@types/node": "*"
       }
     },
+    "@types/minimatch": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
+      "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
+      "dev": true
+    },
     "@types/node": {
       "version": "16.11.24",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.24.tgz",
@@ -3304,6 +3729,12 @@
         "sprintf-js": "~1.0.2"
       }
     },
+    "array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true
+    },
     "asn1": {
       "version": "0.2.6",
       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
@@ -3413,6 +3844,15 @@
         "concat-map": "0.0.1"
       }
     },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
     "buffer": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -3848,6 +4288,27 @@
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
       "dev": true
     },
+    "detect-indent": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
+      "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
+      "dev": true
+    },
+    "detect-newline": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+      "dev": true
+    },
+    "dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "requires": {
+        "path-type": "^4.0.0"
+      }
+    },
     "easy-table": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz",
@@ -3975,11 +4436,33 @@
       "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
       "dev": true
     },
+    "fast-glob": {
+      "version": "3.2.11",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
+      "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      }
+    },
     "fast-safe-stringify": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
       "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
     },
+    "fastq": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+      "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
     "fd-slicer": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
@@ -3997,6 +4480,15 @@
         "escape-string-regexp": "^1.0.5"
       }
     },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
     "follow-redirects": {
       "version": "1.14.8",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
@@ -4066,6 +4558,12 @@
         "assert-plus": "^1.0.0"
       }
     },
+    "git-hooks-list": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-1.0.3.tgz",
+      "integrity": "sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==",
+      "dev": true
+    },
     "glob": {
       "version": "7.1.6",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -4079,6 +4577,15 @@
         "path-is-absolute": "^1.0.0"
       }
     },
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
     "global-dirs": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz",
@@ -4088,6 +4595,22 @@
         "ini": "2.0.0"
       }
     },
+    "globby": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.0.tgz",
+      "integrity": "sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw==",
+      "dev": true,
+      "requires": {
+        "@types/glob": "^7.1.1",
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.0.3",
+        "glob": "^7.1.3",
+        "ignore": "^5.1.1",
+        "merge2": "^1.2.3",
+        "slash": "^3.0.0"
+      }
+    },
     "got": {
       "version": "11.8.3",
       "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
@@ -4163,6 +4686,12 @@
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
       "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
     },
+    "ignore": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+      "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+      "dev": true
+    },
     "indent-string": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@@ -4219,11 +4748,26 @@
         "ci-info": "^3.2.0"
       }
     },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true
+    },
     "is-fullwidth-code-point": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
       "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
     },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
     "is-installed-globally": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
@@ -4239,12 +4783,24 @@
       "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
       "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="
     },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
     "is-path-inside": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
       "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
       "dev": true
     },
+    "is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true
+    },
     "is-stream": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -4445,6 +5001,22 @@
       "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
       "dev": true
     },
+    "merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dev": true,
+      "requires": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      }
+    },
     "mime-db": {
       "version": "1.51.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
@@ -4615,6 +5187,12 @@
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
       "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA=="
     },
+    "path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true
+    },
     "pend": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@@ -4627,6 +5205,12 @@
       "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
       "dev": true
     },
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true
+    },
     "pify": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -4634,11 +5218,20 @@
       "dev": true
     },
     "prettier": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
-      "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+      "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
       "dev": true
     },
+    "prettier-plugin-packagejson": {
+      "version": "2.2.18",
+      "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.2.18.tgz",
+      "integrity": "sha512-iBjQ3IY6IayFrQHhXvg+YvKprPUUiIJ04Vr9+EbeQPfwGajznArIqrN33c5bi4JcIvmLHGROIMOm9aYakJj/CA==",
+      "dev": true,
+      "requires": {
+        "sort-package-json": "1.57.0"
+      }
+    },
     "pretty-bytes": {
       "version": "5.6.0",
       "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@@ -4679,6 +5272,12 @@
       "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
       "dev": true
     },
+    "queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true
+    },
     "quick-lru": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@@ -4738,6 +5337,12 @@
         "signal-exit": "^3.0.2"
       }
     },
+    "reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true
+    },
     "rfdc": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
@@ -4758,6 +5363,15 @@
       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
       "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="
     },
+    "run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "requires": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
     "rxjs": {
       "version": "7.5.2",
       "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz",
@@ -4812,6 +5426,12 @@
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
     },
+    "slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true
+    },
     "slice-ansi": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
@@ -4823,6 +5443,26 @@
         "is-fullwidth-code-point": "^3.0.0"
       }
     },
+    "sort-object-keys": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz",
+      "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==",
+      "dev": true
+    },
+    "sort-package-json": {
+      "version": "1.57.0",
+      "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-1.57.0.tgz",
+      "integrity": "sha512-FYsjYn2dHTRb41wqnv+uEqCUvBpK3jZcTp9rbz2qDTmel7Pmdtf+i2rLaaPMRZeSVM60V3Se31GyWFpmKs4Q5Q==",
+      "dev": true,
+      "requires": {
+        "detect-indent": "^6.0.0",
+        "detect-newline": "3.1.0",
+        "git-hooks-list": "1.0.3",
+        "globby": "10.0.0",
+        "is-plain-obj": "2.1.0",
+        "sort-object-keys": "^1.1.3"
+      }
+    },
     "spawn-command": {
       "version": "0.0.2-1",
       "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
@@ -4916,6 +5556,15 @@
         "rimraf": "^3.0.0"
       }
     },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
     "tough-cookie": {
       "version": "2.5.0",
       "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
diff --git a/package.json b/package.json
index 4d931468c..c87c3561e 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,21 @@
 {
   "private": true,
-  "prettier": "ory-prettier-styles",
-  "config": {
-    "prettierTarget": "test/e2e/**/*{.ts,.js}"
-  },
   "scripts": {
     "format": "prettier --write ${npm_package_config_prettierTarget}",
     "format:check": "prettier --check ${npm_package_config_prettierTarget}",
+    "openapi-generator-cli": "openapi-generator-cli",
     "test": "cypress run --browser chrome",
     "test:watch": "cypress open --browser chrome",
-    "openapi-generator-cli": "openapi-generator-cli",
-    "wait-on": "wait-on",
-    "text-run": "exit 0"
+    "text-run": "exit 0",
+    "wait-on": "wait-on"
+  },
+  "config": {
+    "prettierTarget": "test/e2e/**/*{.ts,.js}"
+  },
+  "prettier": "ory-prettier-styles",
+  "dependencies": {
+    "@openapitools/openapi-generator-cli": "^2.1.18",
+    "yamljs": "^0.3.0"
   },
   "devDependencies": {
     "@ory/kratos-client": "0.0.0-next.8d3b018594f7",
@@ -23,12 +27,9 @@
     "got": "^11.8.2",
     "ory-prettier-styles": "1.1.2",
     "otplib": "^12.0.1",
-    "prettier": "2.3.2",
+    "prettier": "^2.7.1",
+    "prettier-plugin-packagejson": "^2.2.18",
     "typescript": "^4.4.3",
     "wait-on": "5.3.0"
-  },
-  "dependencies": {
-    "@openapitools/openapi-generator-cli": "^2.1.18",
-    "yamljs": "^0.3.0"
   }
 }
diff --git a/persistence/sql/migrations/sql/20220825134336000000_delete_verification_token_without_flow_id.down.sql b/persistence/sql/migrations/sql/20220825134336000000_delete_verification_token_without_flow_id.down.sql
new file mode 100644
index 000000000..e69de29bb
diff --git a/persistence/sql/migrations/sql/20220825134336000000_delete_verification_token_without_flow_id.up.sql b/persistence/sql/migrations/sql/20220825134336000000_delete_verification_token_without_flow_id.up.sql
new file mode 100644
index 000000000..4e7eba1ac
--- /dev/null
+++ b/persistence/sql/migrations/sql/20220825134336000000_delete_verification_token_without_flow_id.up.sql
@@ -0,0 +1,2 @@
+DELETE FROM identity_verification_tokens
+WHERE selfservice_verification_flow_id IS NULL;
diff --git a/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.down.sql b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.down.sql
new file mode 100644
index 000000000..5e755e406
--- /dev/null
+++ b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.down.sql
@@ -0,0 +1,5 @@
+ALTER TABLE identity_verification_tokens
+ALTER selfservice_verification_flow_id DROP NOT NULL;
+
+DROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx;
+CREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);
diff --git a/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.down.sql b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.down.sql
new file mode 100644
index 000000000..726a40c24
--- /dev/null
+++ b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.down.sql
@@ -0,0 +1,5 @@
+ALTER TABLE identity_verification_tokens
+MODIFY selfservice_verification_flow_id CHAR(36) NULL;
+
+DROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens;
+CREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);
diff --git a/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.up.sql b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.up.sql
new file mode 100644
index 000000000..53d22bee7
--- /dev/null
+++ b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.up.sql
@@ -0,0 +1,5 @@
+ALTER TABLE identity_verification_tokens
+MODIFY selfservice_verification_flow_id CHAR(36) NOT NULL;
+
+DROP INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens;
+CREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid, token, used, selfservice_verification_flow_id);
diff --git a/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.down.sql b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.down.sql
new file mode 100644
index 000000000..325516f79
--- /dev/null
+++ b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.down.sql
@@ -0,0 +1,37 @@
+ALTER TABLE identity_verification_tokens
+RENAME TO identity_verification_tokens_;
+
+CREATE TABLE "identity_verification_tokens" (
+"id" TEXT PRIMARY KEY,
+"token" TEXT NOT NULL,
+"used" bool NOT NULL DEFAULT 'false',
+"used_at" DATETIME,
+"expires_at" DATETIME NOT NULL,
+"issued_at" DATETIME NOT NULL,
+"identity_verifiable_address_id" char(36) NOT NULL,
+"selfservice_verification_flow_id" char(36),
+"created_at" DATETIME NOT NULL,
+"updated_at" DATETIME NOT NULL,
+"nid" char(36),
+FOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,
+FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE
+);
+
+DROP INDEX identity_verification_tokens_id_nid_idx;
+DROP INDEX identity_verification_tokens_nid_id_idx;
+DROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx;
+DROP INDEX identity_verification_tokens_token_uq_idx;
+DROP INDEX identity_verification_tokens_verifiable_address_id_idx;
+DROP INDEX identity_verification_tokens_verification_flow_id_idx;
+
+CREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id, nid);
+CREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid, id);
+CREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);
+CREATE UNIQUE INDEX "identity_verification_tokens_token_uq_idx" ON "identity_verification_tokens" (token);
+CREATE INDEX "identity_verification_tokens_verifiable_address_id_idx" ON "identity_verification_tokens" (identity_verifiable_address_id);
+CREATE INDEX "identity_verification_tokens_verification_flow_id_idx" ON "identity_verification_tokens" (selfservice_verification_flow_id);
+
+INSERT INTO identity_verification_tokens
+SELECT * FROM identity_verification_tokens_;
+
+DROP TABLE identity_verification_tokens_;
diff --git a/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.up.sql b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.up.sql
new file mode 100644
index 000000000..b417f6e54
--- /dev/null
+++ b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.up.sql
@@ -0,0 +1,37 @@
+ALTER TABLE identity_verification_tokens
+RENAME TO identity_verification_tokens_;
+
+CREATE TABLE "identity_verification_tokens" (
+"id" TEXT PRIMARY KEY,
+"token" TEXT NOT NULL,
+"used" bool NOT NULL DEFAULT 'false',
+"used_at" DATETIME,
+"expires_at" DATETIME NOT NULL,
+"issued_at" DATETIME NOT NULL,
+"identity_verifiable_address_id" char(36) NOT NULL,
+"selfservice_verification_flow_id" char(36) NOT NULL,
+"created_at" DATETIME NOT NULL,
+"updated_at" DATETIME NOT NULL,
+"nid" char(36),
+FOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,
+FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE
+);
+
+DROP INDEX identity_verification_tokens_id_nid_idx;
+DROP INDEX identity_verification_tokens_nid_id_idx;
+DROP INDEX identity_verification_tokens_token_nid_used_idx;
+DROP INDEX identity_verification_tokens_token_uq_idx;
+DROP INDEX identity_verification_tokens_verifiable_address_id_idx;
+DROP INDEX identity_verification_tokens_verification_flow_id_idx;
+
+CREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id, nid);
+CREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid, id);
+CREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid, token, used, selfservice_verification_flow_id);
+CREATE UNIQUE INDEX "identity_verification_tokens_token_uq_idx" ON "identity_verification_tokens" (token);
+CREATE INDEX "identity_verification_tokens_verifiable_address_id_idx" ON "identity_verification_tokens" (identity_verifiable_address_id);
+CREATE INDEX "identity_verification_tokens_verification_flow_id_idx" ON "identity_verification_tokens" (selfservice_verification_flow_id);
+
+INSERT INTO identity_verification_tokens
+SELECT * FROM identity_verification_tokens_;
+
+DROP TABLE identity_verification_tokens_;
\ No newline at end of file
diff --git a/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.up.sql b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.up.sql
new file mode 100644
index 000000000..3d4173ce4
--- /dev/null
+++ b/persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.up.sql
@@ -0,0 +1,5 @@
+ALTER TABLE identity_verification_tokens
+ALTER selfservice_verification_flow_id SET NOT NULL;
+
+DROP INDEX identity_verification_tokens_token_nid_used_idx;
+CREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid, token, used, selfservice_verification_flow_id);
diff --git a/persistence/sql/persister_verification.go b/persistence/sql/persister_verification.go
index 77a0e22e2..8ea41836d 100644
--- a/persistence/sql/persister_verification.go
+++ b/persistence/sql/persister_verification.go
@@ -67,7 +67,7 @@ func (p *Persister) CreateVerificationToken(ctx context.Context, token *link.Ver
 	return nil
 }
 
-func (p *Persister) UseVerificationToken(ctx context.Context, token string) (*link.VerificationToken, error) {
+func (p *Persister) UseVerificationToken(ctx context.Context, fID uuid.UUID, token string) (*link.VerificationToken, error) {
 	ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseVerificationToken")
 	defer span.End()
 
@@ -76,7 +76,7 @@ func (p *Persister) UseVerificationToken(ctx context.Context, token string) (*li
 	nid := p.NetworkID(ctx)
 	if err := sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) (err error) {
 		for _, secret := range p.r.Config().SecretsSession(ctx) {
-			if err = tx.Where("token = ? AND nid = ? AND NOT used", p.hmacValueWithSecret(ctx, token, secret), nid).First(&rt); err != nil {
+			if err = tx.Where("token = ? AND nid = ? AND NOT used AND selfservice_verification_flow_id = ?", p.hmacValueWithSecret(ctx, token, secret), nid, fID).First(&rt); err != nil {
 				if !errors.Is(sqlcon.HandleError(err), sqlcon.ErrNoRows) {
 					return err
 				}
diff --git a/selfservice/flow/recovery/handler.go b/selfservice/flow/recovery/handler.go
index 1da77912a..664f2d9a7 100644
--- a/selfservice/flow/recovery/handler.go
+++ b/selfservice/flow/recovery/handler.go
@@ -51,6 +51,7 @@ type (
 		x.CSRFProvider
 		config.Provider
 		ErrorHandlerProvider
+		HookExecutorProvider
 	}
 	Handler struct {
 		d handlerDependencies
@@ -127,6 +128,11 @@ func (h *Handler) initAPIFlow(w http.ResponseWriter, r *http.Request, _ httprout
 		return
 	}
 
+	if err := h.d.RecoveryExecutor().PreRecoveryHook(w, r, req); err != nil {
+		h.d.Writer().WriteError(w, r, err)
+		return
+	}
+
 	if err := h.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), req); err != nil {
 		h.d.Writer().WriteError(w, r, err)
 		return
@@ -178,6 +184,11 @@ func (h *Handler) initBrowserFlow(w http.ResponseWriter, r *http.Request, _ http
 		return
 	}
 
+	if err := h.d.RecoveryExecutor().PreRecoveryHook(w, r, f); err != nil {
+		h.d.Writer().WriteError(w, r, err)
+		return
+	}
+
 	if err := h.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), f); err != nil {
 		h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)
 		return
diff --git a/selfservice/flow/recovery/hook.go b/selfservice/flow/recovery/hook.go
index 9fe1fb20d..7c75f2f4b 100644
--- a/selfservice/flow/recovery/hook.go
+++ b/selfservice/flow/recovery/hook.go
@@ -14,12 +14,18 @@ import (
 )
 
 type (
+	PreHookExecutor interface {
+		ExecuteRecoveryPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error
+	}
+	PreHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow) error
+
 	PostHookExecutor interface {
 		ExecutePostRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error
 	}
 	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
 	}
 )
@@ -32,6 +38,10 @@ func PostHookRecoveryExecutorNames(e []PostHookExecutor) []string {
 	return names
 }
 
+func (f PreHookExecutorFunc) ExecuteRecoveryPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
+	return f(w, r, a)
+}
+
 func (f PostHookExecutorFunc) ExecutePostRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error {
 	return f(w, r, a, s)
 }
@@ -92,3 +102,13 @@ func (e *HookExecutor) PostRecoveryHook(w http.ResponseWriter, r *http.Request,
 
 	return nil
 }
+
+func (e *HookExecutor) PreRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
+	for _, executor := range e.d.PreRecoveryHooks(r.Context()) {
+		if err := executor.ExecuteRecoveryPreHook(w, r, a); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go
index 2974f56fa..2c017c0ed 100644
--- a/selfservice/flow/registration/hook.go
+++ b/selfservice/flow/registration/hook.go
@@ -52,9 +52,11 @@ func PostHookPostPersistExecutorNames(e []PostHookPostPersistExecutor) []string
 func (f PreHookExecutorFunc) ExecuteRegistrationPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
 	return f(w, r, a)
 }
+
 func (f PostHookPostPersistExecutorFunc) ExecutePostRegistrationPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error {
 	return f(w, r, a, s)
 }
+
 func (f PostHookPrePersistExecutorFunc) ExecutePostRegistrationPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error {
 	return f(w, r, a, i)
 }
diff --git a/selfservice/flow/settings/error.go b/selfservice/flow/settings/error.go
index b749b5ca4..86b20be7f 100644
--- a/selfservice/flow/settings/error.go
+++ b/selfservice/flow/settings/error.go
@@ -25,7 +25,7 @@ import (
 )
 
 var (
-	ErrHookAbortRequest = errors.New("aborted settings hook execution")
+	ErrHookAbortFlow = errors.New("aborted settings hook execution")
 )
 
 type (
diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go
index 0ce280d38..0fd300707 100644
--- a/selfservice/flow/settings/handler.go
+++ b/selfservice/flow/settings/handler.go
@@ -111,6 +111,11 @@ func (h *Handler) NewFlow(w http.ResponseWriter, r *http.Request, i *identity.Id
 	if err != nil {
 		return nil, err
 	}
+
+	if err := h.d.SettingsHookExecutor().PreSettingsHook(w, r, f); err != nil {
+		return nil, err
+	}
+
 	for _, strategy := range h.d.SettingsStrategies(r.Context()) {
 		if err := h.d.ContinuityManager().Abort(r.Context(), w, r, ContinuityKey(strategy.SettingsStrategyID())); err != nil {
 			return nil, err
diff --git a/selfservice/flow/settings/hook.go b/selfservice/flow/settings/hook.go
index 6690891f8..02c3c3388 100644
--- a/selfservice/flow/settings/hook.go
+++ b/selfservice/flow/settings/hook.go
@@ -24,18 +24,27 @@ import (
 )
 
 type (
+	PreHookExecutor interface {
+		ExecuteSettingsPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error
+	}
+	PreHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow) error
+
 	PostHookPrePersistExecutor interface {
 		ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error
 	}
 	PostHookPrePersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error
-	PostHookPostPersistExecutor    interface {
+
+	PostHookPostPersistExecutor interface {
 		ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error
 	}
 	PostHookPostPersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error
-	HooksProvider                   interface {
+
+	HooksProvider interface {
+		PreSettingsHooks(ctx context.Context) []PreHookExecutor
 		PostSettingsPrePersistHooks(ctx context.Context, settingsType string) []PostHookPrePersistExecutor
 		PostSettingsPostPersistHooks(ctx context.Context, settingsType string) []PostHookPostPersistExecutor
 	}
+
 	executorDependencies interface {
 		identity.ManagementProvider
 		identity.ValidationProvider
@@ -57,6 +66,10 @@ type (
 	}
 )
 
+func (f PreHookExecutorFunc) ExecuteSettingsPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
+	return f(w, r, a)
+}
+
 func (f PostHookPrePersistExecutorFunc) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error {
 	return f(w, r, a, s)
 }
@@ -164,7 +177,7 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request,
 		}
 
 		if err := executor.ExecuteSettingsPrePersistHook(w, r, ctxUpdate.Flow, i); err != nil {
-			if errors.Is(err, ErrHookAbortRequest) {
+			if errors.Is(err, ErrHookAbortFlow) {
 				e.d.Logger().WithRequest(r).WithFields(logFields).
 					Debug("A ExecuteSettingsPrePersistHook hook aborted early.")
 				return nil
@@ -229,7 +242,7 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request,
 
 	for k, executor := range e.d.PostSettingsPostPersistHooks(r.Context(), settingsType) {
 		if err := executor.ExecuteSettingsPostPersistHook(w, r, ctxUpdate.Flow, i); err != nil {
-			if errors.Is(err, ErrHookAbortRequest) {
+			if errors.Is(err, ErrHookAbortFlow) {
 				e.d.Logger().
 					WithRequest(r).
 					WithField("executor", fmt.Sprintf("%T", executor)).
@@ -271,3 +284,13 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request,
 	x.ContentNegotiationRedirection(w, r, i.CopyWithoutCredentials(), e.d.Writer(), returnTo.String())
 	return nil
 }
+
+func (e *HookExecutor) PreSettingsHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
+	for _, executor := range e.d.PreSettingsHooks(r.Context()) {
+		if err := executor.ExecuteSettingsPreHook(w, r, a); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/selfservice/flow/verification/handler.go b/selfservice/flow/verification/handler.go
index 5a4de45c1..e9efc3a37 100644
--- a/selfservice/flow/verification/handler.go
+++ b/selfservice/flow/verification/handler.go
@@ -49,6 +49,7 @@ type (
 		FlowPersistenceProvider
 		ErrorHandlerProvider
 		StrategyProvider
+		HookExecutorProvider
 	}
 	Handler struct {
 		d handlerDependencies
@@ -80,6 +81,34 @@ func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
 	admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d))
 }
 
+type FlowOption func(f *Flow)
+
+func WithFlowReturnTo(returnTo string) FlowOption {
+	return func(f *Flow) {
+		f.ReturnTo = returnTo
+	}
+}
+
+func (h *Handler) NewVerificationFlow(w http.ResponseWriter, r *http.Request, ft flow.Type, opts ...FlowOption) (*Flow, error) {
+	f, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, h.d.AllVerificationStrategies(), ft)
+	if err != nil {
+		return nil, err
+	}
+	for _, o := range opts {
+		o(f)
+	}
+
+	if err := h.d.VerificationExecutor().PreVerificationHook(w, r, f); err != nil {
+		return nil, err
+	}
+
+	if err := h.d.VerificationFlowPersister().CreateVerificationFlow(r.Context(), f); err != nil {
+		return nil, err
+	}
+
+	return f, nil
+}
+
 // swagger:route GET /self-service/verification/api v0alpha2 initializeSelfServiceVerificationFlowWithoutBrowser
 //
 // Initialize Verification Flow for APIs, Services, Apps, ...
@@ -108,17 +137,12 @@ func (h *Handler) initAPIFlow(w http.ResponseWriter, r *http.Request, _ httprout
 		return
 	}
 
-	req, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, h.d.VerificationStrategies(r.Context()), flow.TypeAPI)
+	req, err := h.NewVerificationFlow(w, r, flow.TypeAPI)
 	if err != nil {
 		h.d.Writer().WriteError(w, r, err)
 		return
 	}
 
-	if err := h.d.VerificationFlowPersister().CreateVerificationFlow(r.Context(), req); err != nil {
-		h.d.Writer().WriteError(w, r, err)
-		return
-	}
-
 	h.d.Writer().Write(w, r, req)
 }
 
@@ -156,14 +180,9 @@ func (h *Handler) initBrowserFlow(w http.ResponseWriter, r *http.Request, ps htt
 		return
 	}
 
-	req, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, h.d.VerificationStrategies(r.Context()), flow.TypeBrowser)
+	req, err := h.NewVerificationFlow(w, r, flow.TypeBrowser)
 	if err != nil {
-		h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)
-		return
-	}
-
-	if err := h.d.VerificationFlowPersister().CreateVerificationFlow(r.Context(), req); err != nil {
-		h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)
+		h.d.Writer().WriteError(w, r, err)
 		return
 	}
 
diff --git a/selfservice/flow/verification/hook.go b/selfservice/flow/verification/hook.go
index b7e90e338..39eaa0395 100644
--- a/selfservice/flow/verification/hook.go
+++ b/selfservice/flow/verification/hook.go
@@ -14,6 +14,11 @@ import (
 )
 
 type (
+	PreHookExecutor interface {
+		ExecuteVerificationPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error
+	}
+	PreHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow) error
+
 	PostHookExecutor interface {
 		ExecutePostVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error
 	}
@@ -21,6 +26,7 @@ type (
 
 	HooksProvider interface {
 		PostVerificationHooks(ctx context.Context) []PostHookExecutor
+		PreVerificationHooks(ctx context.Context) []PreHookExecutor
 	}
 )
 
@@ -32,6 +38,10 @@ func PostHookVerificationExecutorNames(e []PostHookExecutor) []string {
 	return names
 }
 
+func (f PreHookExecutorFunc) ExecuteVerificationPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error {
+	return f(w, r, a)
+}
+
 func (f PostHookExecutorFunc) ExecutePostVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error {
 	return f(w, r, a, i)
 }
@@ -63,6 +73,16 @@ 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()) {
+		if err := executor.ExecuteVerificationPreHook(w, r, a); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 func (e *HookExecutor) PostVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error {
 	e.d.Logger().
 		WithRequest(r).
diff --git a/selfservice/hook/error.go b/selfservice/hook/error.go
index fc28d45b8..0f8c6046e 100644
--- a/selfservice/hook/error.go
+++ b/selfservice/hook/error.go
@@ -25,9 +25,14 @@ var (
 
 	_ login.PreHookExecutor  = new(Error)
 	_ login.PostHookExecutor = new(Error)
+	_ login.PreHookExecutor  = new(Error)
 
 	_ settings.PostHookPostPersistExecutor = new(Error)
 	_ settings.PostHookPrePersistExecutor  = new(Error)
+	_ settings.PreHookExecutor             = new(Error)
+
+	_ verification.PreHookExecutor = new(Error)
+	_ recovery.PreHookExecutor     = new(Error)
 )
 
 type Error struct {
@@ -44,12 +49,16 @@ func (e Error) err(path string, abort error) error {
 	return nil
 }
 
+func (e Error) ExecuteSettingsPreHook(w http.ResponseWriter, r *http.Request, a *settings.Flow) error {
+	return e.err("ExecuteSettingsPreHook", settings.ErrHookAbortFlow)
+}
+
 func (e Error) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, s *identity.Identity) error {
-	return e.err("ExecuteSettingsPrePersistHook", settings.ErrHookAbortRequest)
+	return e.err("ExecuteSettingsPrePersistHook", settings.ErrHookAbortFlow)
 }
 
 func (e Error) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, s *identity.Identity) error {
-	return e.err("ExecuteSettingsPostPersistHook", settings.ErrHookAbortRequest)
+	return e.err("ExecuteSettingsPostPersistHook", settings.ErrHookAbortFlow)
 }
 
 func (e Error) ExecuteLoginPostHook(w http.ResponseWriter, r *http.Request, g node.UiNodeGroup, a *login.Flow, s *session.Session) error {
@@ -72,10 +81,18 @@ func (e Error) ExecutePostRegistrationPrePersistHook(w http.ResponseWriter, r *h
 	return e.err("ExecutePostRegistrationPrePersistHook", registration.ErrHookAbortFlow)
 }
 
+func (e Error) ExecuteRecoveryPreHook(w http.ResponseWriter, r *http.Request, a *recovery.Flow) error {
+	return e.err("ExecuteRecoveryPreHook", recovery.ErrHookAbortFlow)
+}
+
 func (e Error) ExecutePostRecoveryHook(w http.ResponseWriter, r *http.Request, a *recovery.Flow, s *session.Session) error {
 	return e.err("ExecutePostRecoveryHook", recovery.ErrHookAbortFlow)
 }
 
+func (e Error) ExecuteVerificationPreHook(w http.ResponseWriter, r *http.Request, a *verification.Flow) error {
+	return e.err("ExecuteVerificationPreHook", verification.ErrHookAbortFlow)
+}
+
 func (e Error) ExecutePostVerificationHook(w http.ResponseWriter, r *http.Request, a *verification.Flow, i *identity.Identity) error {
 	return e.err("ExecutePostVerificationHook", verification.ErrHookAbortFlow)
 }
diff --git a/selfservice/hook/web_hook.go b/selfservice/hook/web_hook.go
index 0d90fe512..df53cb0a6 100644
--- a/selfservice/hook/web_hook.go
+++ b/selfservice/hook/web_hook.go
@@ -96,6 +96,16 @@ func (e *WebHook) ExecuteLoginPostHook(_ http.ResponseWriter, req *http.Request,
 	})
 }
 
+func (e *WebHook) ExecuteVerificationPreHook(_ http.ResponseWriter, req *http.Request, flow *verification.Flow) error {
+	ctx, _ := e.deps.Tracer(req.Context()).Tracer().Start(req.Context(), "selfservice.hook.ExecutePreVerificationHook")
+	return e.execute(ctx, &templateContext{
+		Flow:           flow,
+		RequestHeaders: req.Header,
+		RequestMethod:  req.Method,
+		RequestURL:     x.RequestURL(req).String(),
+	})
+}
+
 func (e *WebHook) ExecutePostVerificationHook(_ http.ResponseWriter, req *http.Request, flow *verification.Flow, id *identity.Identity) error {
 	ctx, _ := e.deps.Tracer(req.Context()).Tracer().Start(req.Context(), "selfservice.hook.ExecutePostVerificationHook")
 	return e.execute(ctx, &templateContext{
@@ -107,6 +117,16 @@ func (e *WebHook) ExecutePostVerificationHook(_ http.ResponseWriter, req *http.R
 	})
 }
 
+func (e *WebHook) ExecuteRecoveryPreHook(_ http.ResponseWriter, req *http.Request, flow *recovery.Flow) error {
+	ctx, _ := e.deps.Tracer(req.Context()).Tracer().Start(req.Context(), "selfservice.hook.ExecutePreRecoveryHook")
+	return e.execute(ctx, &templateContext{
+		Flow:           flow,
+		RequestHeaders: req.Header,
+		RequestMethod:  req.Method,
+		RequestURL:     x.RequestURL(req).String(),
+	})
+}
+
 func (e *WebHook) ExecutePostRecoveryHook(_ http.ResponseWriter, req *http.Request, flow *recovery.Flow, session *session.Session) error {
 	ctx, _ := e.deps.Tracer(req.Context()).Tracer().Start(req.Context(), "selfservice.hook.ExecutePostRecoveryHook")
 	return e.execute(ctx, &templateContext{
@@ -150,6 +170,16 @@ func (e *WebHook) ExecutePostRegistrationPostPersistHook(_ http.ResponseWriter,
 	})
 }
 
+func (e *WebHook) ExecuteSettingsPreHook(_ http.ResponseWriter, req *http.Request, flow *settings.Flow) error {
+	ctx, _ := e.deps.Tracer(req.Context()).Tracer().Start(req.Context(), "selfservice.hook.ExecutePreSettingsHook")
+	return e.execute(ctx, &templateContext{
+		Flow:           flow,
+		RequestHeaders: req.Header,
+		RequestMethod:  req.Method,
+		RequestURL:     x.RequestURL(req).String(),
+	})
+}
+
 func (e *WebHook) ExecuteSettingsPostPersistHook(_ http.ResponseWriter, req *http.Request, flow *settings.Flow, id *identity.Identity) error {
 	ctx, _ := e.deps.Tracer(req.Context()).Tracer().Start(req.Context(), "selfservice.hook.ExecuteSettingsPostPersistHook")
 	return e.execute(ctx, &templateContext{
diff --git a/selfservice/strategy/link/persistence.go b/selfservice/strategy/link/persistence.go
index 1caff3221..930b6750d 100644
--- a/selfservice/strategy/link/persistence.go
+++ b/selfservice/strategy/link/persistence.go
@@ -19,7 +19,7 @@ type (
 
 	VerificationTokenPersister interface {
 		CreateVerificationToken(ctx context.Context, token *VerificationToken) error
-		UseVerificationToken(ctx context.Context, token string) (*VerificationToken, error)
+		UseVerificationToken(ctx context.Context, fID uuid.UUID, token string) (*VerificationToken, error)
 		DeleteVerificationToken(ctx context.Context, token string) error
 	}
 
diff --git a/selfservice/strategy/link/strategy.go b/selfservice/strategy/link/strategy.go
index e9b02cd88..9514e80a8 100644
--- a/selfservice/strategy/link/strategy.go
+++ b/selfservice/strategy/link/strategy.go
@@ -61,6 +61,7 @@ type (
 		verification.FlowPersistenceProvider
 		verification.StrategyProvider
 		verification.HookExecutorProvider
+		verification.HandlerProvider
 
 		RecoveryTokenPersistenceProvider
 		VerificationTokenPersistenceProvider
diff --git a/selfservice/strategy/link/strategy_verification.go b/selfservice/strategy/link/strategy_verification.go
index 22f005ecb..9815abcaf 100644
--- a/selfservice/strategy/link/strategy_verification.go
+++ b/selfservice/strategy/link/strategy_verification.go
@@ -117,7 +117,7 @@ func (s *Strategy) Verify(w http.ResponseWriter, r *http.Request, f *verificatio
 			return s.handleVerificationError(w, r, nil, body, err)
 		}
 
-		return s.verificationUseToken(w, r, body)
+		return s.verificationUseToken(w, r, body, f)
 	}
 
 	if err := flow.MethodEnabledAndAllowed(r.Context(), s.VerificationStrategyID(), body.Method, s.d); err != nil {
@@ -187,8 +187,8 @@ type selfServiceBrowserVerifyParameters struct {
 	Token string `json:"token"`
 }
 
-func (s *Strategy) verificationUseToken(w http.ResponseWriter, r *http.Request, body *verificationSubmitPayload) error {
-	token, err := s.d.VerificationTokenPersister().UseVerificationToken(r.Context(), body.Token)
+func (s *Strategy) verificationUseToken(w http.ResponseWriter, r *http.Request, body *verificationSubmitPayload, f *verification.Flow) error {
+	token, err := s.d.VerificationTokenPersister().UseVerificationToken(r.Context(), f.ID, body.Token)
 	if err != nil {
 		if errors.Is(err, sqlcon.ErrNoRows) {
 			return s.retryVerificationFlowWithMessage(w, r, flow.TypeBrowser, text.NewErrorValidationVerificationTokenInvalidOrAlreadyUsed())
@@ -197,23 +197,6 @@ func (s *Strategy) verificationUseToken(w http.ResponseWriter, r *http.Request,
 		return s.retryVerificationFlowWithError(w, r, flow.TypeBrowser, err)
 	}
 
-	var f *verification.Flow
-	if !token.FlowID.Valid {
-		f, err = verification.NewFlow(s.d.Config(), s.d.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), s.d.GenerateCSRFToken(r), r, s.d.VerificationStrategies(r.Context()), flow.TypeBrowser)
-		if err != nil {
-			return s.retryVerificationFlowWithError(w, r, flow.TypeBrowser, err)
-		}
-
-		if err := s.d.VerificationFlowPersister().CreateVerificationFlow(r.Context(), f); err != nil {
-			return s.retryVerificationFlowWithError(w, r, flow.TypeBrowser, err)
-		}
-	} else {
-		f, err = s.d.VerificationFlowPersister().GetVerificationFlow(r.Context(), token.FlowID.UUID)
-		if err != nil {
-			return s.retryVerificationFlowWithError(w, r, flow.TypeBrowser, err)
-		}
-	}
-
 	if err := token.Valid(); err != nil {
 		return s.retryVerificationFlowWithError(w, r, flow.TypeBrowser, err)
 	}
diff --git a/selfservice/strategy/link/token_verification.go b/selfservice/strategy/link/token_verification.go
index 93454725b..b7c8fe3bd 100644
--- a/selfservice/strategy/link/token_verification.go
+++ b/selfservice/strategy/link/token_verification.go
@@ -44,8 +44,8 @@ type VerificationToken struct {
 	// VerifiableAddressID is a helper struct field for gobuffalo.pop.
 	VerifiableAddressID uuid.UUID `json:"-" faker:"-" db:"identity_verifiable_address_id"`
 	// FlowID is a helper struct field for gobuffalo.pop.
-	FlowID uuid.NullUUID `json:"-" faker:"-" db:"selfservice_verification_flow_id"`
-	NID    uuid.UUID     `json:"-"  faker:"-" db:"nid"`
+	FlowID uuid.UUID `json:"-" faker:"-" db:"selfservice_verification_flow_id"`
+	NID    uuid.UUID `json:"-" faker:"-" db:"nid"`
 }
 
 func (VerificationToken) TableName(ctx context.Context) string {
@@ -60,7 +60,8 @@ func NewSelfServiceVerificationToken(address *identity.VerifiableAddress, f *ver
 		VerifiableAddress: address,
 		ExpiresAt:         now.Add(expiresIn),
 		IssuedAt:          now,
-		FlowID:            uuid.NullUUID{UUID: f.ID, Valid: true}}
+		FlowID:            f.ID,
+	}
 }
 
 func (f *VerificationToken) Valid() error {


Test patch:
diff --git a/driver/registry_default_test.go b/driver/registry_default_test.go
index 96ac14a79..356d81b67 100644
--- a/driver/registry_default_test.go
+++ b/driver/registry_default_test.go
@@ -31,6 +31,45 @@ func TestDriverDefault_Hooks(t *testing.T) {
 	ctx := context.Background()
 
 	t.Run("type=verification", func(t *testing.T) {
+		// BEFORE hooks
+		for _, tc := range []struct {
+			uc     string
+			prep   func(conf *config.Config)
+			expect func(reg *driver.RegistryDefault) []verification.PreHookExecutor
+		}{
+			{
+				uc:     "No hooks configured",
+				prep:   func(conf *config.Config) {},
+				expect: func(reg *driver.RegistryDefault) []verification.PreHookExecutor { return nil },
+			},
+			{
+				uc: "Two web_hooks are configured",
+				prep: func(conf *config.Config) {
+					conf.MustSet(ctx, config.ViperKeySelfServiceVerificationBeforeHooks, []map[string]interface{}{
+						{"hook": "web_hook", "config": map[string]interface{}{"url": "foo", "method": "POST"}},
+						{"hook": "web_hook", "config": map[string]interface{}{"url": "bar", "method": "GET"}},
+					})
+				},
+				expect: func(reg *driver.RegistryDefault) []verification.PreHookExecutor {
+					return []verification.PreHookExecutor{
+						hook.NewWebHook(reg, json.RawMessage(`{"method":"POST","url":"foo"}`)),
+						hook.NewWebHook(reg, json.RawMessage(`{"method":"GET","url":"bar"}`)),
+					}
+				},
+			},
+		} {
+			t.Run(fmt.Sprintf("before/uc=%s", tc.uc), func(t *testing.T) {
+				conf, reg := internal.NewFastRegistryWithMocks(t)
+				tc.prep(conf)
+
+				h := reg.PreVerificationHooks(ctx)
+
+				expectedExecutors := tc.expect(reg)
+				require.Len(t, h, len(expectedExecutors))
+				assert.Equal(t, expectedExecutors, h)
+			})
+		}
+
 		// AFTER hooks
 		for _, tc := range []struct {
 			uc     string
@@ -72,6 +111,45 @@ func TestDriverDefault_Hooks(t *testing.T) {
 	})
 
 	t.Run("type=recovery", func(t *testing.T) {
+		// BEFORE hooks
+		for _, tc := range []struct {
+			uc     string
+			prep   func(conf *config.Config)
+			expect func(reg *driver.RegistryDefault) []recovery.PreHookExecutor
+		}{
+			{
+				uc:     "No hooks configured",
+				prep:   func(conf *config.Config) {},
+				expect: func(reg *driver.RegistryDefault) []recovery.PreHookExecutor { return nil },
+			},
+			{
+				uc: "Two web_hooks are configured",
+				prep: func(conf *config.Config) {
+					conf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBeforeHooks, []map[string]interface{}{
+						{"hook": "web_hook", "config": map[string]interface{}{"url": "foo", "method": "POST"}},
+						{"hook": "web_hook", "config": map[string]interface{}{"url": "bar", "method": "GET"}},
+					})
+				},
+				expect: func(reg *driver.RegistryDefault) []recovery.PreHookExecutor {
+					return []recovery.PreHookExecutor{
+						hook.NewWebHook(reg, json.RawMessage(`{"method":"POST","url":"foo"}`)),
+						hook.NewWebHook(reg, json.RawMessage(`{"method":"GET","url":"bar"}`)),
+					}
+				},
+			},
+		} {
+			t.Run(fmt.Sprintf("before/uc=%s", tc.uc), func(t *testing.T) {
+				conf, reg := internal.NewFastRegistryWithMocks(t)
+				tc.prep(conf)
+
+				h := reg.PreRecoveryHooks(ctx)
+
+				expectedExecutors := tc.expect(reg)
+				require.Len(t, h, len(expectedExecutors))
+				assert.Equal(t, expectedExecutors, h)
+			})
+		}
+
 		// AFTER hooks
 		for _, tc := range []struct {
 			uc     string
@@ -388,6 +466,45 @@ func TestDriverDefault_Hooks(t *testing.T) {
 	})
 
 	t.Run("type=settings", func(t *testing.T) {
+		// BEFORE hooks
+		for _, tc := range []struct {
+			uc     string
+			prep   func(conf *config.Config)
+			expect func(reg *driver.RegistryDefault) []settings.PreHookExecutor
+		}{
+			{
+				uc:     "No hooks configured",
+				prep:   func(conf *config.Config) {},
+				expect: func(reg *driver.RegistryDefault) []settings.PreHookExecutor { return nil },
+			},
+			{
+				uc: "Two web_hooks are configured",
+				prep: func(conf *config.Config) {
+					conf.MustSet(ctx, config.ViperKeySelfServiceSettingsBeforeHooks, []map[string]interface{}{
+						{"hook": "web_hook", "config": map[string]interface{}{"url": "foo", "method": "POST"}},
+						{"hook": "web_hook", "config": map[string]interface{}{"url": "bar", "method": "GET"}},
+					})
+				},
+				expect: func(reg *driver.RegistryDefault) []settings.PreHookExecutor {
+					return []settings.PreHookExecutor{
+						hook.NewWebHook(reg, json.RawMessage(`{"method":"POST","url":"foo"}`)),
+						hook.NewWebHook(reg, json.RawMessage(`{"method":"GET","url":"bar"}`)),
+					}
+				},
+			},
+		} {
+			t.Run(fmt.Sprintf("before/uc=%s", tc.uc), func(t *testing.T) {
+				conf, reg := internal.NewFastRegistryWithMocks(t)
+				tc.prep(conf)
+
+				h := reg.PreSettingsHooks(ctx)
+
+				expectedExecutors := tc.expect(reg)
+				require.Len(t, h, len(expectedExecutors))
+				assert.Equal(t, expectedExecutors, h)
+			})
+		}
+
 		// AFTER hooks
 		for _, tc := range []struct {
 			uc     string
diff --git a/internal/testhelpers/selfservice.go b/internal/testhelpers/selfservice.go
index 8a21cb0aa..b55be5557 100644
--- a/internal/testhelpers/selfservice.go
+++ b/internal/testhelpers/selfservice.go
@@ -46,7 +46,7 @@ func TestSelfServicePreHook(
 
 		t.Run("case=err if hooks err", func(t *testing.T) {
 			t.Cleanup(SelfServiceHookConfigReset(t, conf))
-			conf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: "err", Config: []byte(`{"ExecuteLoginPreHook": "err","ExecuteRegistrationPreHook": "err"}`)}})
+			conf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: "err", Config: []byte(`{"ExecuteLoginPreHook": "err","ExecuteRegistrationPreHook": "err","ExecuteSettingsPreHook": "err","ExecuteVerificationPreHook": "err","ExecuteRecoveryPreHook": "err"}`)}})
 
 			res, body := makeRequestPre(t, newServer(t))
 			assert.EqualValues(t, http.StatusInternalServerError, res.StatusCode, "%s", body)
@@ -55,7 +55,7 @@ func TestSelfServicePreHook(
 
 		t.Run("case=abort if hooks aborts", func(t *testing.T) {
 			t.Cleanup(SelfServiceHookConfigReset(t, conf))
-			conf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: "err", Config: []byte(`{"ExecuteLoginPreHook": "abort","ExecuteRegistrationPreHook": "abort"}`)}})
+			conf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: "err", Config: []byte(`{"ExecuteLoginPreHook": "abort","ExecuteRegistrationPreHook": "abort","ExecuteSettingsPreHook": "abort","ExecuteVerificationPreHook": "abort","ExecuteRecoveryPreHook": "abort"}`)}})
 
 			res, body := makeRequestPre(t, newServer(t))
 			assert.EqualValues(t, http.StatusOK, res.StatusCode)
@@ -154,7 +154,7 @@ func SelfServiceHookRegistrationErrorHandler(t *testing.T, w http.ResponseWriter
 }
 
 func SelfServiceHookSettingsErrorHandler(t *testing.T, w http.ResponseWriter, r *http.Request, err error) bool {
-	return SelfServiceHookErrorHandler(t, w, r, settings.ErrHookAbortRequest, err)
+	return SelfServiceHookErrorHandler(t, w, r, settings.ErrHookAbortFlow, err)
 }
 
 func SelfServiceHookErrorHandler(t *testing.T, w http.ResponseWriter, r *http.Request, abortErr error, actualErr error) bool {
@@ -182,6 +182,18 @@ func SelfServiceMakeRegistrationPreHookRequest(t *testing.T, ts *httptest.Server
 	return SelfServiceMakeHookRequest(t, ts, "/registration/pre", false, url.Values{})
 }
 
+func SelfServiceMakeSettingsPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {
+	return SelfServiceMakeHookRequest(t, ts, "/settings/pre", false, url.Values{})
+}
+
+func SelfServiceMakeRecoveryPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {
+	return SelfServiceMakeHookRequest(t, ts, "/recovery/pre", false, url.Values{})
+}
+
+func SelfServiceMakeVerificationPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {
+	return SelfServiceMakeHookRequest(t, ts, "/verification/pre", false, url.Values{})
+}
+
 func SelfServiceMakeRegistrationPostHookRequest(t *testing.T, ts *httptest.Server, asAPI bool, query url.Values) (*http.Response, string) {
 	return SelfServiceMakeHookRequest(t, ts, "/registration/post", asAPI, query)
 }
diff --git a/persistence/sql/migratest/fixtures/verification_token/ee56574d-2f1c-43f6-8d26-0062938ae330.json b/persistence/sql/migratest/fixtures/verification_token/ee56574d-2f1c-43f6-8d26-0062938ae330.json
new file mode 100644
index 000000000..93b643ac0
--- /dev/null
+++ b/persistence/sql/migratest/fixtures/verification_token/ee56574d-2f1c-43f6-8d26-0062938ae330.json
@@ -0,0 +1,6 @@
+{
+  "id": "ee56574d-2f1c-43f6-8d26-0062938ae330",
+  "verification_address": null,
+  "expires_at": "2013-10-07T08:23:19Z",
+  "issued_at": "2013-10-07T08:23:19Z"
+}
diff --git a/persistence/sql/migratest/testdata/20220902141902_testdata.sql b/persistence/sql/migratest/testdata/20220902141902_testdata.sql
new file mode 100644
index 000000000..50181c40c
--- /dev/null
+++ b/persistence/sql/migratest/testdata/20220902141902_testdata.sql
@@ -0,0 +1,2 @@
+INSERT INTO identity_verification_tokens (id, token, used, used_at, identity_verifiable_address_id, selfservice_verification_flow_id, created_at, updated_at, expires_at, issued_at, nid)
+VALUES ('ee56574d-2f1c-43f6-8d26-0062938ae330', '1001ba7ddd644cb68478e8947e4jfhe', false, null, '45e867e9-2745-4f16-8dd4-84334a252b61', '5385c962-0295-4575-9b1b-d7eef13c0eda', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '884f556e-eb3a-4b9f-bee3-11345642c6c0');
diff --git a/selfservice/flow/recovery/hook_test.go b/selfservice/flow/recovery/hook_test.go
index 32e164009..44bdba6ef 100644
--- a/selfservice/flow/recovery/hook_test.go
+++ b/selfservice/flow/recovery/hook_test.go
@@ -30,6 +30,13 @@ 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, reg.RecoveryStrategies(context.Background()), ft)
+			require.NoError(t, err)
+			if testhelpers.SelfServiceHookErrorHandler(t, w, r, recovery.ErrHookAbortFlow, reg.RecoveryExecutor().PreRecoveryHook(w, r, a)) {
+				_, _ = w.Write([]byte("ok"))
+			}
+		})
 
 		router.GET("/recovery/post", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 			a, err := recovery.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, reg.RecoveryStrategies(context.Background()), ft)
@@ -89,4 +96,15 @@ func TestRecoveryExecutor(t *testing.T) {
 			assert.Equal(t, "", body)
 		})
 	})
+
+	for _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {
+		t.Run("type="+string(kind)+"/method=PreRecoveryHook", testhelpers.TestSelfServicePreHook(
+			config.ViperKeySelfServiceRecoveryBeforeHooks,
+			testhelpers.SelfServiceMakeRecoveryPreHookRequest,
+			func(t *testing.T) *httptest.Server {
+				return newServer(t, nil, kind)
+			},
+			conf,
+		))
+	}
 }
diff --git a/selfservice/flow/registration/hook_test.go b/selfservice/flow/registration/hook_test.go
index 8eb3490df..cbe439df4 100644
--- a/selfservice/flow/registration/hook_test.go
+++ b/selfservice/flow/registration/hook_test.go
@@ -158,23 +158,16 @@ func TestRegistrationExecutor(t *testing.T) {
 				})
 			})
 
-			t.Run("type=browser/method=PreRegistrationHook", testhelpers.TestSelfServicePreHook(
-				config.ViperKeySelfServiceRegistrationBeforeHooks,
-				testhelpers.SelfServiceMakeRegistrationPreHookRequest,
-				func(t *testing.T) *httptest.Server {
-					return newServer(t, nil, flow.TypeBrowser)
-				},
-				conf,
-			))
-
-			t.Run("type=api/method=PreRegistrationHook", testhelpers.TestSelfServicePreHook(
-				config.ViperKeySelfServiceRegistrationBeforeHooks,
-				testhelpers.SelfServiceMakeRegistrationPreHookRequest,
-				func(t *testing.T) *httptest.Server {
-					return newServer(t, nil, flow.TypeAPI)
-				},
-				conf,
-			))
+			for _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {
+				t.Run("type="+string(kind)+"/method=PreRegistrationHook", testhelpers.TestSelfServicePreHook(
+					config.ViperKeySelfServiceRegistrationBeforeHooks,
+					testhelpers.SelfServiceMakeRegistrationPreHookRequest,
+					func(t *testing.T) *httptest.Server {
+						return newServer(t, nil, kind)
+					},
+					conf,
+				))
+			}
 		})
 	}
 }
diff --git a/selfservice/flow/settings/hook_test.go b/selfservice/flow/settings/hook_test.go
index 4ed6ba035..f5fa15bbf 100644
--- a/selfservice/flow/settings/hook_test.go
+++ b/selfservice/flow/settings/hook_test.go
@@ -46,6 +46,17 @@ func TestSettingsExecutor(t *testing.T) {
 			newServer := func(t *testing.T, ft flow.Type) *httptest.Server {
 				router := httprouter.New()
 				handleErr := testhelpers.SelfServiceHookSettingsErrorHandler
+				router.GET("/settings/pre", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+					i := testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)
+					sess, _ := session.NewActiveSession(ctx, i, conf, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)
+
+					f, err := settings.NewFlow(conf, time.Minute, r, sess.Identity, ft)
+					require.NoError(t, err)
+					if handleErr(t, w, r, reg.SettingsHookExecutor().PreSettingsHook(w, r, f)) {
+						_, _ = w.Write([]byte("ok"))
+					}
+				})
+
 				router.GET("/settings/post", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 					i := testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)
 					sess, _ := session.NewActiveSession(ctx, i, conf, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)
@@ -74,7 +85,6 @@ func TestSettingsExecutor(t *testing.T) {
 			conf.MustSet(ctx, config.ViperKeySelfServiceSettingsURL, uiURL)
 
 			t.Run("method=PostSettingsHook", func(t *testing.T) {
-
 				t.Run("case=pass without hooks", func(t *testing.T) {
 					t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))
 
@@ -154,6 +164,17 @@ func TestSettingsExecutor(t *testing.T) {
 					assert.NotEmpty(t, gjson.Get(body, "identity.id"))
 				})
 			})
+
+			for _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {
+				t.Run("type="+string(kind)+"/method=PreSettingsHook", testhelpers.TestSelfServicePreHook(
+					config.ViperKeySelfServiceSettingsBeforeHooks,
+					testhelpers.SelfServiceMakeSettingsPreHookRequest,
+					func(t *testing.T) *httptest.Server {
+						return newServer(t, kind)
+					},
+					conf,
+				))
+			}
 		})
 	}
 }
diff --git a/selfservice/flow/verification/hook_test.go b/selfservice/flow/verification/hook_test.go
index 75b9bafc3..d84bfb14d 100644
--- a/selfservice/flow/verification/hook_test.go
+++ b/selfservice/flow/verification/hook_test.go
@@ -28,6 +28,13 @@ func TestVerificationExecutor(t *testing.T) {
 
 	newServer := func(t *testing.T, i *identity.Identity, ft flow.Type) *httptest.Server {
 		router := httprouter.New()
+		router.GET("/verification/pre", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+			a, err := verification.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, reg.VerificationStrategies(context.Background()), ft)
+			require.NoError(t, err)
+			if testhelpers.SelfServiceHookErrorHandler(t, w, r, verification.ErrHookAbortFlow, reg.VerificationExecutor().PreVerificationHook(w, r, a)) {
+				_, _ = w.Write([]byte("ok"))
+			}
+		})
 
 		router.GET("/verification/post", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 			a, err := verification.NewFlow(conf, time.Minute, x.FakeCSRFToken, r, reg.VerificationStrategies(context.Background()), ft)
@@ -79,5 +86,17 @@ func TestVerificationExecutor(t *testing.T) {
 			assert.EqualValues(t, http.StatusOK, res.StatusCode)
 			assert.Equal(t, "", body)
 		})
+
+		for _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {
+			t.Run("type="+string(kind)+"/method=PreVerificationHook", testhelpers.TestSelfServicePreHook(
+				config.ViperKeySelfServiceVerificationBeforeHooks,
+				testhelpers.SelfServiceMakeVerificationPreHookRequest,
+				func(t *testing.T) *httptest.Server {
+					i := testhelpers.SelfServiceHookFakeIdentity(t)
+					return newServer(t, i, kind)
+				},
+				conf,
+			))
+		}
 	})
 }
diff --git a/selfservice/strategy/link/strategy_verification_test.go b/selfservice/strategy/link/strategy_verification_test.go
index e3edb984c..420f68866 100644
--- a/selfservice/strategy/link/strategy_verification_test.go
+++ b/selfservice/strategy/link/strategy_verification_test.go
@@ -383,4 +383,23 @@ func TestVerification(t *testing.T) {
 		require.NoError(t, err)
 		assert.Equal(t, returnToURL+"?flow="+flow.ID.String(), redirectURL.String())
 	})
+
+	t.Run("case=should not be able to use code from different flow", func(t *testing.T) {
+
+		f1, _ := newValidFlow(t, public.URL+verification.RouteInitBrowserFlow)
+
+		_, t2 := newValidFlow(t, public.URL+verification.RouteInitBrowserFlow)
+
+		formValues := url.Values{
+			"flow":  {f1.ID.String()},
+			"token": {t2.Token},
+		}
+		submitUrl := public.URL + verification.RouteSubmitFlow + "?" + formValues.Encode()
+
+		res, err := public.Client().Get(submitUrl)
+		require.NoError(t, err)
+		body := ioutilx.MustReadAll(res.Body)
+
+		assert.Equal(t, "The verification token is invalid or has already been used. Please retry the flow.", gjson.GetBytes(body, "ui.messages.0.text").String())
+	})
 }
diff --git a/selfservice/strategy/link/test/persistence.go b/selfservice/strategy/link/test/persistence.go
index f8648bf82..a2d3375ee 100644
--- a/selfservice/strategy/link/test/persistence.go
+++ b/selfservice/strategy/link/test/persistence.go
@@ -123,15 +123,10 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface {
 		})
 
 		t.Run("token=verification", func(t *testing.T) {
-			t.Run("case=should error when the verification token does not exist", func(t *testing.T) {
-				_, err := p.UseVerificationToken(ctx, "i-do-not-exist")
-				require.Error(t, err)
-			})
-
-			newVerificationToken := func(t *testing.T, email string) *link.VerificationToken {
-				var req verification.Flow
-				require.NoError(t, faker.FakeData(&req))
-				require.NoError(t, p.CreateVerificationFlow(ctx, &req))
+			newVerificationToken := func(t *testing.T, email string) (*verification.Flow, *link.VerificationToken) {
+				var f verification.Flow
+				require.NoError(t, faker.FakeData(&f))
+				require.NoError(t, p.CreateVerificationFlow(ctx, &f))
 
 				var i identity.Identity
 				require.NoError(t, faker.FakeData(&i))
@@ -140,9 +135,9 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface {
 				i.VerifiableAddresses = append(i.VerifiableAddresses, *address)
 
 				require.NoError(t, p.CreateIdentity(ctx, &i))
-				return &link.VerificationToken{
+				return &f, &link.VerificationToken{
 					Token:             x.NewUUID().String(),
-					FlowID:            uuid.NullUUID{UUID: req.ID, Valid: true},
+					FlowID:            f.ID,
 					VerifiableAddress: &i.VerifiableAddresses[0],
 					ExpiresAt:         time.Now(),
 					IssuedAt:          time.Now(),
@@ -150,26 +145,33 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface {
 			}
 
 			t.Run("case=should error when the verification token does not exist", func(t *testing.T) {
-				_, err := p.UseVerificationToken(ctx, "i-do-not-exist")
+				_, err := p.UseVerificationToken(ctx, x.NewUUID(), "i-do-not-exist")
+				require.Error(t, err)
+			})
+
+			t.Run("case=should error when the verification token does exist but the flow does not", func(t *testing.T) {
+				_, token := newVerificationToken(t, x.NewUUID().String()+"@ory.sh")
+				require.NoError(t, p.CreateVerificationToken(ctx, token))
+				_, err := p.UseVerificationToken(ctx, x.NewUUID(), token.Token)
 				require.Error(t, err)
 			})
 
 			t.Run("case=should create a new verification token", func(t *testing.T) {
-				token := newVerificationToken(t, "foo-user@ory.sh")
+				_, token := newVerificationToken(t, "foo-user@ory.sh")
 				require.NoError(t, p.CreateVerificationToken(ctx, token))
 			})
 
 			t.Run("case=should create a verification token and use it", func(t *testing.T) {
-				expected := newVerificationToken(t, "other-user@ory.sh")
+				f, expected := newVerificationToken(t, "other-user@ory.sh")
 				require.NoError(t, p.CreateVerificationToken(ctx, expected))
 
 				t.Run("not work on another network", func(t *testing.T) {
 					_, p := testhelpers.NewNetwork(t, ctx, p)
-					_, err := p.UseVerificationToken(ctx, expected.Token)
+					_, err := p.UseVerificationToken(ctx, f.ID, expected.Token)
 					require.ErrorIs(t, err, sqlcon.ErrNoRows)
 				})
 
-				actual, err := p.UseVerificationToken(ctx, expected.Token)
+				actual, err := p.UseVerificationToken(ctx, f.ID, expected.Token)
 				require.NoError(t, err)
 				assertx.EqualAsJSONExcept(t, expected.VerifiableAddress, actual.VerifiableAddress, []string{"created_at", "updated_at"})
 				assert.Equal(t, nid, actual.NID)
@@ -177,7 +179,7 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface {
 				assert.NotEqual(t, expected.Token, actual.Token)
 				assert.EqualValues(t, expected.FlowID, actual.FlowID)
 
-				_, err = p.UseVerificationToken(ctx, expected.Token)
+				_, err = p.UseVerificationToken(ctx, f.ID, expected.Token)
 				require.Error(t, err)
 			})
 		})