Repository: ory/kratos
Pull request #1697 (2021-08-29 15:44:25)

fix: #1251 - fix return_to for expired flows
<!--
fix issue in #1251 - preserve return_to for expired flows
-->

## Related issue(s)

<!--
Please link the GitHub issue this pull request resolves in the format of `#1234`. If you discussed this change
with a maintainer, please mention her/him using the `@` syntax (e.g. `@aeneasr`).

If this change neither resolves an existing issue nor has sign-off from one of the maintainers, there is a
chance substantial changes will be requested or that the changes will be rejected.

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!
-->

- [x] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).
- [x] I am following the
      [contributing code guidelines](../blob/master/CONTRIBUTING.md#contributing-code).
- [x] I have read the [security policy](../security/policy).
- [x] 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.
- [x] I have added tests that prove my fix is effective or that my feature
      works.
- [x] I have added or changed [the documentation](docs/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...
-->


codecov[bot]: # [Codecov](https://codecov.io/gh/ory/kratos/pull/1697?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory) Report
> Merging [#1697](https://codecov.io/gh/ory/kratos/pull/1697?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory) (70e5a87) into [master](https://codecov.io/gh/ory/kratos/commit/e7a237afb209201a4934473f0fdf48cc2a1bff8f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory) (e7a237a) will **decrease** coverage by `0.01%`.
> The diff coverage is `72.22%`.

[![Impacted file tree graph](https://codecov.io/gh/ory/kratos/pull/1697/graphs/tree.svg?width=650&height=150&src=pr&token=6t0QqOdurR&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory)](https://codecov.io/gh/ory/kratos/pull/1697?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory)

```diff
@@            Coverage Diff             @@
##           master    #1697      +/-   ##
==========================================
- Coverage   74.25%   74.24%   -0.02%     
==========================================
  Files         259      259              
  Lines       12618    12648      +30     
==========================================
+ Hits         9370     9390      +20     
- Misses       2627     2632       +5     
- Partials      621      626       +5     
```


| [Impacted Files](https://codecov.io/gh/ory/kratos/pull/1697?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory) | Coverage Δ | |
|---|---|---|
| [selfservice/flow/login/handler.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9sb2dpbi9oYW5kbGVyLmdv) | `68.88% <66.66%> (-0.11%)` | :arrow_down: |
| [selfservice/flow/recovery/flow.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9yZWNvdmVyeS9mbG93Lmdv) | `90.69% <66.66%> (-3.90%)` | :arrow_down: |
| [selfservice/flow/registration/handler.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9yZWdpc3RyYXRpb24vaGFuZGxlci5nbw==) | `71.92% <66.66%> (-0.30%)` | :arrow_down: |
| [selfservice/flow/settings/handler.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9zZXR0aW5ncy9oYW5kbGVyLmdv) | `63.43% <66.66%> (+0.15%)` | :arrow_up: |
| [selfservice/flow/verification/flow.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy92ZXJpZmljYXRpb24vZmxvdy5nbw==) | `85.71% <66.66%> (-2.29%)` | :arrow_down: |
| [selfservice/flow/login/error.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9sb2dpbi9lcnJvci5nbw==) | `77.41% <100.00%> (ø)` | |
| [selfservice/flow/recovery/error.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9yZWNvdmVyeS9lcnJvci5nbw==) | `73.84% <100.00%> (ø)` | |
| [selfservice/flow/registration/error.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9yZWdpc3RyYXRpb24vZXJyb3IuZ28=) | `77.41% <100.00%> (ø)` | |
| [selfservice/flow/settings/error.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy9zZXR0aW5ncy9lcnJvci5nbw==) | `75.60% <100.00%> (ø)` | |
| [selfservice/flow/verification/error.go](https://codecov.io/gh/ory/kratos/pull/1697/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory#diff-c2VsZnNlcnZpY2UvZmxvdy92ZXJpZmljYXRpb24vZXJyb3IuZ28=) | `81.66% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/ory/kratos/pull/1697?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/ory/kratos/pull/1697?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory). Last update [e7a237a...70e5a87](https://codecov.io/gh/ory/kratos/pull/1697?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ory).

aeneasr: Let me know when you want this reviewed again :)
dibyajyotibehera: > Let me know when you want this reviewed again :)

Please review it :) again .
I had a put a comment above actually to get it reviewed :)

> thanks . I made the changes added ui tests for login and registration flows . I believe other flows stay on the settings ui instead of redirecting . Please check.
aeneasr: Oh sorry, I missed that!
aeneasr: Awesome work!
aeneasr: The CI failure is unrelated to your changes!

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b7b75510..465469c23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,15 +2,16 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**
 
-- [ (2021-08-31)](#2021-08-31)
+- [ (2021-09-05)](#2021-09-05)
     - [Bug Fixes](#bug-fixes)
     - [Documentation](#documentation)
+    - [Features](#features)
     - [Tests](#tests)
 - [0.7.3-alpha.1 (2021-08-28)](#073-alpha1-2021-08-28)
     - [Bug Fixes](#bug-fixes-1)
     - [Code Generation](#code-generation)
     - [Documentation](#documentation-1)
-    - [Features](#features)
+    - [Features](#features-1)
 - [0.7.1-alpha.1 (2021-07-22)](#071-alpha1-2021-07-22)
     - [Bug Fixes](#bug-fixes-2)
     - [Code Generation](#code-generation-1)
@@ -22,7 +23,7 @@
     - [Code Generation](#code-generation-2)
     - [Code Refactoring](#code-refactoring)
     - [Documentation](#documentation-3)
-    - [Features](#features-1)
+    - [Features](#features-2)
     - [Tests](#tests-2)
     - [Unclassified](#unclassified)
 - [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17)
@@ -35,25 +36,25 @@
     - [Documentation](#documentation-4)
 - [0.6.1-alpha.1 (2021-05-11)](#061-alpha1-2021-05-11)
     - [Code Generation](#code-generation-5)
-    - [Features](#features-2)
+    - [Features](#features-3)
 - [0.6.0-alpha.2 (2021-05-07)](#060-alpha2-2021-05-07)
     - [Bug Fixes](#bug-fixes-5)
     - [Code Generation](#code-generation-6)
-    - [Features](#features-3)
+    - [Features](#features-4)
 - [0.6.0-alpha.1 (2021-05-05)](#060-alpha1-2021-05-05)
   - [Breaking Changes](#breaking-changes-2)
     - [Bug Fixes](#bug-fixes-6)
     - [Code Generation](#code-generation-7)
     - [Code Refactoring](#code-refactoring-2)
     - [Documentation](#documentation-5)
-    - [Features](#features-4)
+    - [Features](#features-5)
     - [Tests](#tests-3)
     - [Unclassified](#unclassified-1)
 - [0.5.5-alpha.1 (2020-12-09)](#055-alpha1-2020-12-09)
     - [Bug Fixes](#bug-fixes-7)
     - [Code Generation](#code-generation-8)
     - [Documentation](#documentation-6)
-    - [Features](#features-5)
+    - [Features](#features-6)
     - [Tests](#tests-4)
     - [Unclassified](#unclassified-2)
 - [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11)
@@ -61,12 +62,12 @@
     - [Code Generation](#code-generation-9)
     - [Code Refactoring](#code-refactoring-3)
     - [Documentation](#documentation-7)
-    - [Features](#features-6)
+    - [Features](#features-7)
 - [0.5.3-alpha.1 (2020-10-27)](#053-alpha1-2020-10-27)
     - [Bug Fixes](#bug-fixes-9)
     - [Code Generation](#code-generation-10)
     - [Documentation](#documentation-8)
-    - [Features](#features-7)
+    - [Features](#features-8)
     - [Tests](#tests-5)
 - [0.5.2-alpha.1 (2020-10-22)](#052-alpha1-2020-10-22)
     - [Bug Fixes](#bug-fixes-10)
@@ -77,7 +78,7 @@
     - [Bug Fixes](#bug-fixes-11)
     - [Code Generation](#code-generation-12)
     - [Documentation](#documentation-10)
-    - [Features](#features-8)
+    - [Features](#features-9)
     - [Tests](#tests-7)
     - [Unclassified](#unclassified-3)
 - [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15)
@@ -86,7 +87,7 @@
     - [Code Generation](#code-generation-13)
     - [Code Refactoring](#code-refactoring-4)
     - [Documentation](#documentation-11)
-    - [Features](#features-9)
+    - [Features](#features-10)
     - [Tests](#tests-8)
     - [Unclassified](#unclassified-4)
 - [0.4.6-alpha.1 (2020-07-13)](#046-alpha1-2020-07-13)
@@ -111,7 +112,7 @@
     - [Code Generation](#code-generation-19)
     - [Code Refactoring](#code-refactoring-5)
     - [Documentation](#documentation-13)
-    - [Features](#features-10)
+    - [Features](#features-11)
     - [Unclassified](#unclassified-5)
 - [0.3.0-alpha.1 (2020-05-15)](#030-alpha1-2020-05-15)
   - [Breaking Changes](#breaking-changes-5)
@@ -119,7 +120,7 @@
     - [Chores](#chores)
     - [Code Refactoring](#code-refactoring-6)
     - [Documentation](#documentation-14)
-    - [Features](#features-11)
+    - [Features](#features-12)
     - [Unclassified](#unclassified-6)
 - [0.2.1-alpha.1 (2020-05-05)](#021-alpha1-2020-05-05)
     - [Chores](#chores-1)
@@ -130,7 +131,7 @@
     - [Chores](#chores-2)
     - [Code Refactoring](#code-refactoring-7)
     - [Documentation](#documentation-16)
-    - [Features](#features-12)
+    - [Features](#features-13)
     - [Unclassified](#unclassified-7)
 - [0.1.1-alpha.1 (2020-02-18)](#011-alpha1-2020-02-18)
     - [Bug Fixes](#bug-fixes-21)
@@ -140,10 +141,10 @@
     - [Bug Fixes](#bug-fixes-22)
     - [Code Refactoring](#code-refactoring-9)
     - [Documentation](#documentation-18)
-    - [Features](#features-13)
+    - [Features](#features-14)
 - [0.1.0-alpha.5 (2020-02-06)](#010-alpha5-2020-02-06)
     - [Documentation](#documentation-19)
-    - [Features](#features-14)
+    - [Features](#features-15)
 - [0.1.0-alpha.4 (2020-02-06)](#010-alpha4-2020-02-06)
     - [Continuous Integration](#continuous-integration)
     - [Documentation](#documentation-20)
@@ -152,7 +153,7 @@
 - [0.1.0-alpha.2 (2020-02-03)](#010-alpha2-2020-02-03)
     - [Bug Fixes](#bug-fixes-23)
     - [Documentation](#documentation-21)
-    - [Features](#features-15)
+    - [Features](#features-16)
     - [Unclassified](#unclassified-8)
 - [0.1.0-alpha.1 (2020-01-31)](#010-alpha1-2020-01-31)
     - [Documentation](#documentation-22)
@@ -196,7 +197,7 @@
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
-# [](https://github.com/ory/kratos/compare/v0.7.3-alpha.1...v) (2021-08-31)
+# [](https://github.com/ory/kratos/compare/v0.7.3-alpha.1...v) (2021-09-05)
 
 
 ### Bug Fixes
@@ -207,8 +208,14 @@
 
 * Add e2e quickstart ([2b749d3](https://github.com/ory/kratos/commit/2b749d39fcb0d320d193290966a558ee2c5734d1))
 * Browser redirects ([#1700](https://github.com/ory/kratos/issues/1700)) ([a44089a](https://github.com/ory/kratos/commit/a44089a506f5ea9daa406fcb862ad707f569c2bb))
+* Mark logout_url always available ([9021805](https://github.com/ory/kratos/commit/9021805c4399beb73f234726f8f5f3bfd312482c))
 * Minor improvements ([#1707](https://github.com/ory/kratos/issues/1707)) ([79c132c](https://github.com/ory/kratos/commit/79c132c5a0737ea1632655d8aea0af63c4200d37))
 
+### Features
+
+* Making use of the updated instrumentedsql version ([#1723](https://github.com/ory/kratos/issues/1723)) ([9e6fbdd](https://github.com/ory/kratos/commit/9e6fbdd06a75d7207b4801d1148267b3a1a0a0c7))
+* **oidc:** Github-app provider ([#1711](https://github.com/ory/kratos/issues/1711)) ([fb1fe8c](https://github.com/ory/kratos/commit/fb1fe8c468bb6f8275618b84c5fa157a314c345f))
+
 ### Tests
 
 * **session:** Resolve incorrect assertion ([0531220](https://github.com/ory/kratos/commit/05312203ab12eec44e59dcd9210160f2781a69b4))
diff --git a/docs/docs/.static/api.json b/docs/docs/.static/api.json
index 8b0af40cb..e15cadbaf 100755
--- a/docs/docs/.static/api.json
+++ b/docs/docs/.static/api.json
@@ -1183,7 +1183,6 @@
       },
       "selfServiceRecoveryFlowState": {
         "description": "The state represents the state of the recovery flow.\n\nchoose_method: ask the user to choose a method (e.g. recover account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the recovery challenge was passed.",
-        "enum": ["choose_method", "sent_email", "passed_challenge"],
         "title": "Recovery Flow State",
         "type": "string"
       },
@@ -1454,17 +1453,7 @@
         "type": "object"
       },
       "submitSelfServiceRecoveryFlowBody": {
-        "discriminator": {
-          "mapping": {
-            "link": "#/components/schemas/submitSelfServiceRecoveryFlowWithLinkMethodBody"
-          },
-          "propertyName": "method"
-        },
-        "oneOf": [
-          {
-            "$ref": "#/components/schemas/submitSelfServiceRecoveryFlowWithLinkMethodBody"
-          }
-        ]
+        "type": "object"
       },
       "submitSelfServiceRecoveryFlowWithLinkMethodBody": {
         "properties": {
@@ -1478,7 +1467,6 @@
           },
           "method": {
             "description": "Method supports `link` only right now.",
-            "enum": ["link"],
             "type": "string"
           }
         },
@@ -1496,6 +1484,9 @@
         "oneOf": [
           {
             "$ref": "#/components/schemas/submitSelfServiceRegistrationFlowWithPasswordMethodBody"
+          },
+          {
+            "$ref": "#/components/schemas/submitSelfServiceRegistrationFlowWithOidcMethodBody"
           }
         ]
       },
diff --git a/driver/registry_default.go b/driver/registry_default.go
index ac03afb98..a5c85232d 100644
--- a/driver/registry_default.go
+++ b/driver/registry_default.go
@@ -478,7 +478,6 @@ func (m *RegistryDefault) Init(ctx context.Context, opts ...RegistryOption) erro
 			if m.Tracer(ctx).IsLoaded() {
 				opts = []instrumentedsql.Opt{
 					instrumentedsql.WithTracer(opentracing.NewTracer(true)),
-					instrumentedsql.WithOmitArgs(),
 				}
 			}
 
diff --git a/go.mod b/go.mod
index dee11801e..08a8c2991 100644
--- a/go.mod
+++ b/go.mod
@@ -2,21 +2,19 @@ module github.com/ory/kratos
 
 go 1.16
 
-replace gopkg.in/DataDog/dd-trace-go.v1 => gopkg.in/DataDog/dd-trace-go.v1 v1.27.1-0.20201005154917-54b73b3e126a
-
-// Use the internal httpclient which can be generated in this codebase but mark it as the
-// official SDK, allowing for the Ory CLI to consume Ory Kratos' CLI commands.
-replace github.com/ory/kratos-client-go => ./internal/httpclient
-
-replace go.mongodb.org/mongo-driver => go.mongodb.org/mongo-driver v1.4.6
-
-replace github.com/gobuffalo/pop/v5 => github.com/gobuffalo/pop/v5 v5.3.4-0.20210608105745-bb07a373cc0e
-
-replace github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.7-0.20210414154423-1157a4212dcb
-
-replace github.com/ory/x => github.com/ory/x v0.0.272
-
-replace github.com/oleiade/reflections => github.com/oleiade/reflections v1.0.1
+replace (
+	gopkg.in/DataDog/dd-trace-go.v1 => gopkg.in/DataDog/dd-trace-go.v1 v1.27.1-0.20201005154917-54b73b3e126a
+	// Use the internal httpclient which can be generated in this codebase but mark it as the
+	// official SDK, allowing for the Ory CLI to consume Ory Kratos' CLI commands.
+	github.com/ory/kratos-client-go => ./internal/httpclient
+	go.mongodb.org/mongo-driver => go.mongodb.org/mongo-driver v1.4.6
+	github.com/gobuffalo/pop/v5 => github.com/gobuffalo/pop/v5 v5.3.4-0.20210608105745-bb07a373cc0e
+	github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.7-0.20210414154423-1157a4212dcb
+	github.com/ory/x => github.com/ory/x v0.0.272
+	github.com/oleiade/reflections => github.com/oleiade/reflections v1.0.1
+	github.com/luna-duclos/instrumentedsql => github.com/ory/instrumentedsql v1.2.0
+	github.com/luna-duclos/instrumentedsql/opentracing => github.com/ory/instrumentedsql/opentracing v0.0.0-20210903114257-c8963b546c5c
+)
 
 require (
 	github.com/DataDog/datadog-go v4.7.0+incompatible // indirect
diff --git a/go.sum b/go.sum
index 409de5f9b..6ef537cde 100644
--- a/go.sum
+++ b/go.sum
@@ -1098,6 +1098,10 @@ github.com/ory/graceful v0.1.1 h1:zx+8tDObLPrG+7Tc8jKYlXsqWnLtOQA1IZ/FAAKHMXU=
 github.com/ory/graceful v0.1.1/go.mod h1:zqu70l95WrKHF4AZ6tXHvAqAvpY6M7g6ttaAVcMm7KU=
 github.com/ory/herodot v0.9.6 h1:S2VSmlWgyhyogB4gH3659B3+1Pv9lJCv2a1uxigw1eg=
 github.com/ory/herodot v0.9.6/go.mod h1:g3yAI/d6wPdGnOt3dbYUj5JGTZBNuUVLuuDqHnfc1lM=
+github.com/ory/instrumentedsql v1.2.0 h1:7VqI6sVRAEVmIp+mVbeICMTi27EJQuq5k9crlt3rs10=
+github.com/ory/instrumentedsql v1.2.0/go.mod h1:wT//5kSu12gfOhU8yDf6IC69yQ+Tc8MufpwzYliRRx8=
+github.com/ory/instrumentedsql/opentracing v0.0.0-20210903114257-c8963b546c5c h1:eek/2FXUZEnRXaRQtqw8MQMxcPX7Qr3tACKI5pe71n0=
+github.com/ory/instrumentedsql/opentracing v0.0.0-20210903114257-c8963b546c5c/go.mod h1:61Z4zNpOoshtEmLmp6T13G9adSk66/dYTfTBEnCLwVQ=
 github.com/ory/jsonschema/v3 v3.0.3 h1:Y7KT4n84ROq8pJ3IMf9JDAulXqYKSU5xUtHjdQFbCLI=
 github.com/ory/jsonschema/v3 v3.0.3/go.mod h1:JvXwbx7IxAkIAo7Qo5OSC1lea+w12DtYGV8h+MTAfnA=
 github.com/ory/mail v2.3.1+incompatible h1:vHntHDHtQXamt2T+iwTTlCoBkDvILUeujE9Ocwe9md4=
diff --git a/selfservice/flow/login/error.go b/selfservice/flow/login/error.go
index 1cdef1329..84168edcd 100644
--- a/selfservice/flow/login/error.go
+++ b/selfservice/flow/login/error.go
@@ -79,7 +79,7 @@ func (s *ErrorHandler) WriteFlowError(w http.ResponseWriter, r *http.Request, f
 
 	if e := new(FlowExpiredError); errors.As(err, &e) {
 		// create new flow because the old one is not valid
-		a, err := s.d.LoginHandler().NewLoginFlow(w, r, f.Type)
+		a, err := s.d.LoginHandler().FromOldFlow(w, r, *f)
 		if err != nil {
 			// failed to create a new session and redirect to it, handle that error as a new one
 			s.WriteFlowError(w, r, f, group, err)
diff --git a/selfservice/flow/login/handler.go b/selfservice/flow/login/handler.go
index a7c781a7b..eec0e6136 100644
--- a/selfservice/flow/login/handler.go
+++ b/selfservice/flow/login/handler.go
@@ -109,6 +109,16 @@ func (h *Handler) NewLoginFlow(w http.ResponseWriter, r *http.Request, flow flow
 	return f, nil
 }
 
+func (h *Handler) FromOldFlow(w http.ResponseWriter, r *http.Request, of Flow) (*Flow, error) {
+	nf, err := h.NewLoginFlow(w, r, of.Type)
+	if err != nil {
+		return nil, err
+	}
+
+	nf.RequestURL = of.RequestURL
+	return nf, nil
+}
+
 // nolint:deadcode,unused
 // swagger:parameters initializeSelfServiceLoginFlowForBrowsers initializeSelfServiceLoginFlowWithoutBrowser
 type initializeSelfServiceLoginFlowWithoutBrowser struct {
diff --git a/selfservice/flow/recovery/error.go b/selfservice/flow/recovery/error.go
index a945d8bd9..31fbebda8 100644
--- a/selfservice/flow/recovery/error.go
+++ b/selfservice/flow/recovery/error.go
@@ -96,7 +96,7 @@ func (s *ErrorHandler) WriteFlowError(
 
 	if e := new(FlowExpiredError); errors.As(err, &e) {
 		// create new flow because the old one is not valid
-		a, err := NewFlow(s.d.Config(r.Context()), s.d.Config(r.Context()).SelfServiceFlowRecoveryRequestLifespan(), s.d.GenerateCSRFToken(r), r, s.d.RecoveryStrategies(r.Context()), f.Type)
+		a, err := FromOldFlow(s.d.Config(r.Context()), s.d.Config(r.Context()).SelfServiceFlowRecoveryRequestLifespan(), s.d.GenerateCSRFToken(r), r, s.d.RecoveryStrategies(r.Context()), *f)
 		if err != nil {
 			// failed to create a new session and redirect to it, handle that error as a new one
 			s.WriteFlowError(w, r, f, group, err)
diff --git a/selfservice/flow/recovery/flow.go b/selfservice/flow/recovery/flow.go
index 241a9980d..ef9c0ef09 100644
--- a/selfservice/flow/recovery/flow.go
+++ b/selfservice/flow/recovery/flow.go
@@ -111,6 +111,16 @@ func NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Reques
 	return req, nil
 }
 
+func FromOldFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, of Flow) (*Flow, error) {
+	nf, err := NewFlow(conf, exp, csrf, r, strategies, of.Type)
+	if err != nil {
+		return nil, err
+	}
+
+	nf.RequestURL = of.RequestURL
+	return nf, nil
+}
+
 func (f *Flow) GetType() flow.Type {
 	return f.Type
 }
diff --git a/selfservice/flow/registration/error.go b/selfservice/flow/registration/error.go
index 247dddbbf..5b7ca1210 100644
--- a/selfservice/flow/registration/error.go
+++ b/selfservice/flow/registration/error.go
@@ -83,7 +83,7 @@ func (s *ErrorHandler) WriteFlowError(
 
 	if e := new(FlowExpiredError); errors.As(err, &e) {
 		// create new flow because the old one is not valid
-		a, err := s.d.RegistrationHandler().NewRegistrationFlow(w, r, f.Type)
+		a, err := s.d.RegistrationHandler().FromOldFlow(w, r, *f)
 		if err != nil {
 			// failed to create a new session and redirect to it, handle that error as a new one
 			s.WriteFlowError(w, r, f, group, err)
diff --git a/selfservice/flow/registration/handler.go b/selfservice/flow/registration/handler.go
index a89bf5848..54bdf9d84 100644
--- a/selfservice/flow/registration/handler.go
+++ b/selfservice/flow/registration/handler.go
@@ -112,6 +112,16 @@ func (h *Handler) NewRegistrationFlow(w http.ResponseWriter, r *http.Request, ft
 	return f, nil
 }
 
+func (h *Handler) FromOldFlow(w http.ResponseWriter, r *http.Request, of Flow) (*Flow, error) {
+	nf, err := h.NewRegistrationFlow(w, r, of.Type)
+	if err != nil {
+		return nil, err
+	}
+
+	nf.RequestURL = of.RequestURL
+	return nf, nil
+}
+
 // swagger:route GET /self-service/registration/api v0alpha1 initializeSelfServiceRegistrationFlowWithoutBrowser
 //
 // Initialize Registration Flow for APIs, Services, Apps, ...
diff --git a/selfservice/flow/settings/error.go b/selfservice/flow/settings/error.go
index 8fc2b9bf5..8ef398417 100644
--- a/selfservice/flow/settings/error.go
+++ b/selfservice/flow/settings/error.go
@@ -118,7 +118,7 @@ func (s *ErrorHandler) WriteFlowError(
 		}
 
 		// create new flow because the old one is not valid
-		a, err := s.d.SettingsHandler().NewFlow(w, r, id, f.Type)
+		a, err := s.d.SettingsHandler().FromOldFlow(w, r, id, *f)
 		if err != nil {
 			// failed to create a new session and redirect to it, handle that error as a new one
 			s.WriteFlowError(w, r, group, f, id, err)
diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go
index 7ef6b4416..16bd36d2d 100644
--- a/selfservice/flow/settings/handler.go
+++ b/selfservice/flow/settings/handler.go
@@ -128,6 +128,16 @@ func (h *Handler) NewFlow(w http.ResponseWriter, r *http.Request, i *identity.Id
 	return f, nil
 }
 
+func (h *Handler) FromOldFlow(w http.ResponseWriter, r *http.Request, i *identity.Identity, of Flow) (*Flow, error) {
+	nf, err := h.NewFlow(w, r, i, of.Type)
+	if err != nil {
+		return nil, err
+	}
+
+	nf.RequestURL = of.RequestURL
+	return nf, nil
+}
+
 // swagger:parameters initializeSelfServiceSettingsFlowWithoutBrowser
 // nolint:deadcode,unused
 type initializeSelfServiceSettingsFlowWithoutBrowser struct {
diff --git a/selfservice/flow/verification/error.go b/selfservice/flow/verification/error.go
index e768a2e72..da7da9571 100644
--- a/selfservice/flow/verification/error.go
+++ b/selfservice/flow/verification/error.go
@@ -85,8 +85,8 @@ func (s *ErrorHandler) WriteFlowError(
 
 	if e := new(FlowExpiredError); errors.As(err, &e) {
 		// create new flow because the old one is not valid
-		a, err := NewFlow(s.d.Config(r.Context()), s.d.Config(r.Context()).SelfServiceFlowVerificationRequestLifespan(),
-			s.d.GenerateCSRFToken(r), r, s.d.VerificationStrategies(r.Context()), f.Type)
+		a, err := FromOldFlow(s.d.Config(r.Context()), s.d.Config(r.Context()).SelfServiceFlowVerificationRequestLifespan(),
+			s.d.GenerateCSRFToken(r), r, s.d.VerificationStrategies(r.Context()), f)
 		if err != nil {
 			// failed to create a new session and redirect to it, handle that error as a new one
 			s.WriteFlowError(w, r, f, group, err)
diff --git a/selfservice/flow/verification/flow.go b/selfservice/flow/verification/flow.go
index ff3ce1cd4..841d0dd02 100644
--- a/selfservice/flow/verification/flow.go
+++ b/selfservice/flow/verification/flow.go
@@ -115,6 +115,16 @@ func NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Reques
 	return f, nil
 }
 
+func FromOldFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, of *Flow) (*Flow, error) {
+	nf, err := NewFlow(conf, exp, csrf, r, strategies, of.Type)
+	if err != nil {
+		return nil, err
+	}
+
+	nf.RequestURL = of.RequestURL
+	return nf, nil
+}
+
 func NewPostHookFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, original flow.Flow) (*Flow, error) {
 	f, err := NewFlow(conf, exp, csrf, r, strategies, original.GetType())
 	if err != nil {
diff --git a/spec/api.json b/spec/api.json
index b7dddb870..460183eaf 100755
--- a/spec/api.json
+++ b/spec/api.json
@@ -1279,11 +1279,6 @@
       },
       "selfServiceRecoveryFlowState": {
         "description": "The state represents the state of the recovery flow.\n\nchoose_method: ask the user to choose a method (e.g. recover account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the recovery challenge was passed.",
-        "enum": [
-          "choose_method",
-          "sent_email",
-          "passed_challenge"
-        ],
         "title": "Recovery Flow State",
         "type": "string"
       },
@@ -1593,17 +1588,7 @@
         "type": "object"
       },
       "submitSelfServiceRecoveryFlowBody": {
-        "discriminator": {
-          "mapping": {
-            "link": "#/components/schemas/submitSelfServiceRecoveryFlowWithLinkMethodBody"
-          },
-          "propertyName": "method"
-        },
-        "oneOf": [
-          {
-            "$ref": "#/components/schemas/submitSelfServiceRecoveryFlowWithLinkMethodBody"
-          }
-        ]
+        "type": "object"
       },
       "submitSelfServiceRecoveryFlowWithLinkMethodBody": {
         "properties": {
@@ -1617,9 +1602,6 @@
           },
           "method": {
             "description": "Method supports `link` only right now.",
-            "enum": [
-              "link"
-            ],
             "type": "string"
           }
         },
@@ -1640,6 +1622,9 @@
         "oneOf": [
           {
             "$ref": "#/components/schemas/submitSelfServiceRegistrationFlowWithPasswordMethodBody"
+          },
+          {
+            "$ref": "#/components/schemas/submitSelfServiceRegistrationFlowWithOidcMethodBody"
           }
         ]
       },


Test patch:
diff --git a/selfservice/flow/login/handler_test.go b/selfservice/flow/login/handler_test.go
index 800683421..a2bde2591 100644
--- a/selfservice/flow/login/handler_test.go
+++ b/selfservice/flow/login/handler_test.go
@@ -185,6 +185,7 @@ func TestGetFlow(t *testing.T) {
 			require.NoError(t, err)
 		}))
 		conf.MustSet(config.ViperKeySelfServiceLoginUI, ts.URL)
+		conf.MustSet(config.ViperKeySelfServiceBrowserDefaultReturnTo, "https://www.ory.sh")
 		t.Cleanup(ts.Close)
 		return ts
 	}
@@ -229,4 +230,27 @@ func TestGetFlow(t *testing.T) {
 		assert.EqualValues(t, http.StatusGone, res.StatusCode)
 		assert.Equal(t, public.URL+login.RouteInitBrowserFlow, gjson.GetBytes(body, "error.details.redirect_to").String(), "%s", body)
 	})
+
+	t.Run("case=expired with return_to", func(t *testing.T) {
+		client := testhelpers.NewClientWithCookies(t)
+		setupLoginUI(t, client)
+		body := x.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow+"?return_to=https://www.ory.sh")
+
+		// Expire the flow
+		f, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String()))
+		require.NoError(t, err)
+		f.ExpiresAt = time.Now().Add(-time.Second)
+		require.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), f))
+
+		// submit the flow but it is expired
+		u := public.URL + login.RouteSubmitFlow + "?flow=" + f.ID.String()
+		res, err := client.PostForm(u, url.Values{"password_identifier": {"email@ory.sh"}, "csrf_token": {f.CSRFToken}, "password": {"password"}, "method": {"password"}})
+		resBody, err := ioutil.ReadAll(res.Body)
+		require.NoError(t, err)
+		require.NoError(t, res.Body.Close())
+
+		f, err = reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, "id").String()))
+		require.NoError(t, err)
+		assert.Equal(t, public.URL+login.RouteInitBrowserFlow+"?return_to=https://www.ory.sh", f.RequestURL)
+	})
 }
diff --git a/selfservice/flow/recovery/handler_test.go b/selfservice/flow/recovery/handler_test.go
index ef0fb38c1..3e5966c90 100644
--- a/selfservice/flow/recovery/handler_test.go
+++ b/selfservice/flow/recovery/handler_test.go
@@ -6,6 +6,7 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
+	"net/url"
 	"testing"
 	"time"
 
@@ -215,4 +216,27 @@ func TestGetFlow(t *testing.T) {
 		assert.EqualValues(t, http.StatusGone, res.StatusCode)
 		assert.Equal(t, public.URL+recovery.RouteInitBrowserFlow, gjson.GetBytes(body, "error.details.redirect_to").String(), "%s", body)
 	})
+
+	t.Run("case=expired with return_to", func(t *testing.T) {
+		client := testhelpers.NewClientWithCookies(t)
+		setupRecoveryTS(t, client)
+		body := x.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow+"?return_to=https://www.ory.sh")
+
+		// Expire the flow
+		f, err := reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String()))
+		require.NoError(t, err)
+		f.ExpiresAt = time.Now().Add(-time.Second)
+		require.NoError(t, reg.RecoveryFlowPersister().UpdateRecoveryFlow(context.Background(), f))
+
+		// submit the flow but it is expired
+		u := public.URL + recovery.RouteSubmitFlow + "?flow=" + f.ID.String()
+		res, err := client.PostForm(u, url.Values{"email": {"email@ory.sh"}, "csrf_token": {f.CSRFToken}, "method": {"link"}})
+		resBody, err := ioutil.ReadAll(res.Body)
+		require.NoError(t, err)
+		require.NoError(t, res.Body.Close())
+
+		f, err = reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, "id").String()))
+		require.NoError(t, err)
+		assert.Equal(t, public.URL+recovery.RouteInitBrowserFlow+"?return_to=https://www.ory.sh", f.RequestURL)
+	})
 }
diff --git a/selfservice/flow/registration/handler_test.go b/selfservice/flow/registration/handler_test.go
index 6e4dc9555..a78817da1 100644
--- a/selfservice/flow/registration/handler_test.go
+++ b/selfservice/flow/registration/handler_test.go
@@ -6,6 +6,7 @@ import (
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
+	"net/url"
 	"testing"
 	"time"
 
@@ -213,4 +214,27 @@ func TestGetFlow(t *testing.T) {
 		assert.EqualValues(t, http.StatusGone, res.StatusCode)
 		assert.Equal(t, public.URL+registration.RouteInitBrowserFlow, gjson.GetBytes(body, "error.details.redirect_to").String(), "%s", body)
 	})
+
+	t.Run("case=expired with return_to", func(t *testing.T) {
+		client := testhelpers.NewClientWithCookies(t)
+		setupRegistrationUI(t, client)
+		body := x.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow+"?return_to=https://www.ory.sh")
+
+		// Expire the flow
+		f, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String()))
+		require.NoError(t, err)
+		f.ExpiresAt = time.Now().Add(-time.Second)
+		require.NoError(t, reg.RegistrationFlowPersister().UpdateRegistrationFlow(context.Background(), f))
+
+		// submit the flow but it is expired
+		u := public.URL + registration.RouteSubmitFlow + "?flow=" + f.ID.String()
+		res, err := client.PostForm(u, url.Values{"method": {"password"}, "csrf_token": {f.CSRFToken}, "password": {"password"}, "traits.email": {"email@ory.sh"}})
+		resBody, err := ioutil.ReadAll(res.Body)
+		require.NoError(t, err)
+		require.NoError(t, res.Body.Close())
+
+		f, err = reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, "id").String()))
+		require.NoError(t, err)
+		assert.Equal(t, public.URL+registration.RouteInitBrowserFlow+"?return_to=https://www.ory.sh", f.RequestURL)
+	})
 }
diff --git a/selfservice/flow/settings/handler_test.go b/selfservice/flow/settings/handler_test.go
index b7e8d0dd9..16800352b 100644
--- a/selfservice/flow/settings/handler_test.go
+++ b/selfservice/flow/settings/handler_test.go
@@ -4,9 +4,12 @@ import (
 	"context"
 	"io/ioutil"
 	"net/http"
+	"net/url"
 	"testing"
 	"time"
 
+	"github.com/gofrs/uuid"
+
 	kratos "github.com/ory/kratos-client-go"
 
 	"github.com/ory/kratos/corpx"
@@ -131,6 +134,28 @@ func TestHandler(t *testing.T) {
 		assert.Equal(t, int64(http.StatusGone), gjson.GetBytes(err.(*kratos.GenericOpenAPIError).Body(), "error.code").Int())
 	})
 
+	t.Run("case=expired with return_to", func(t *testing.T) {
+		client := testhelpers.NewHTTPClientWithArbitrarySessionToken(t, reg)
+		body := x.EasyGetBody(t, client, publicTS.URL+settings.RouteInitBrowserFlow+"?return_to=https://www.ory.sh")
+
+		// Expire the flow
+		f, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String()))
+		require.NoError(t, err)
+		f.ExpiresAt = time.Now().Add(-time.Second)
+		require.NoError(t, reg.SettingsFlowPersister().UpdateSettingsFlow(context.Background(), f))
+
+		// submit the flow but it is expired
+		u := publicTS.URL + settings.RouteSubmitFlow + "?flow=" + f.ID.String()
+		res, err := client.PostForm(u, url.Values{"method": {"password"}, "csrf_token": {"csrf"}, "password": {"password"}})
+		resBody, err := ioutil.ReadAll(res.Body)
+		require.NoError(t, err)
+		require.NoError(t, res.Body.Close())
+
+		f, err = reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, "id").String()))
+		require.NoError(t, err)
+		assert.Equal(t, publicTS.URL+settings.RouteInitBrowserFlow+"?return_to=https://www.ory.sh", f.RequestURL)
+	})
+
 	t.Run("description=should fail to fetch request if identity changed", func(t *testing.T) {
 		t.Run("type=api", func(t *testing.T) {
 			user1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(t, reg)
diff --git a/selfservice/flow/verification/handler_test.go b/selfservice/flow/verification/handler_test.go
index 0765b16ad..34949ab9c 100644
--- a/selfservice/flow/verification/handler_test.go
+++ b/selfservice/flow/verification/handler_test.go
@@ -2,7 +2,9 @@ package verification_test
 
 import (
 	"context"
+	"io/ioutil"
 	"net/http"
+	"net/url"
 	"testing"
 	"time"
 
@@ -105,4 +107,27 @@ func TestGetFlow(t *testing.T) {
 		assert.EqualValues(t, http.StatusGone, res.StatusCode)
 		assert.Equal(t, public.URL+verification.RouteInitBrowserFlow, gjson.GetBytes(body, "error.details.redirect_to").String(), "%s", body)
 	})
+
+	t.Run("case=expired with return_to", func(t *testing.T) {
+		client := testhelpers.NewClientWithCookies(t)
+		_ = setupVerificationUI(t, client)
+		body := x.EasyGetBody(t, client, public.URL+verification.RouteInitBrowserFlow+"?return_to=https://www.ory.sh")
+
+		// Expire the flow
+		f, err := reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String()))
+		require.NoError(t, err)
+		f.ExpiresAt = time.Now().Add(-time.Second)
+		require.NoError(t, reg.VerificationFlowPersister().UpdateVerificationFlow(context.Background(), f))
+
+		// submit the flow but it is expired
+		u := public.URL + verification.RouteSubmitFlow + "?flow=" + f.ID.String()
+		res, err := client.PostForm(u, url.Values{"method": {"link"}, "csrf_token": {f.CSRFToken}, "email": {"email@ory.sh"}})
+		resBody, err := ioutil.ReadAll(res.Body)
+		require.NoError(t, err)
+		require.NoError(t, res.Body.Close())
+
+		f, err = reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, "id").String()))
+		require.NoError(t, err)
+		assert.Equal(t, public.URL+verification.RouteInitBrowserFlow+"?return_to=https://www.ory.sh", f.RequestURL)
+	})
 }
diff --git a/test/e2e/cypress/integration/profiles/email/login/success.spec.js b/test/e2e/cypress/integration/profiles/email/login/success.spec.js
index 19f76820b..8fc7fc8f4 100644
--- a/test/e2e/cypress/integration/profiles/email/login/success.spec.js
+++ b/test/e2e/cypress/integration/profiles/email/login/success.spec.js
@@ -1,4 +1,4 @@
-import { APP_URL, gen, password, website } from '../../../../helpers'
+import { APP_URL, gen, website } from '../../../../helpers'
 
 context('Email Profile', () => {
   describe('Login Flow Success', () => {
@@ -48,4 +48,46 @@ context('Email Profile', () => {
       })
     })
   })
+  describe('Login Flow Success with return_to url after flow expires', () => {
+    before(() => {
+      cy.useConfigProfile('email')
+    })
+
+    const email = gen.email()
+    const password = gen.password()
+
+    before(() => {
+      cy.registerApi({ email, password, fields: { 'traits.website': website } })
+    })
+
+    beforeEach(() => {
+      cy.shortLoginLifespan()
+      cy.browserReturnUrlOry()
+      cy.clearCookies()
+      cy.visit(
+        APP_URL + '/self-service/login/browser?return_to=https://www.ory.sh/'
+      )
+    })
+
+    it('should redirect to return_to after flow expires', () => {
+      cy.wait(105)
+      cy.get('input[name="password_identifier"]').type(email.toUpperCase())
+      cy.get('input[name="password"]').type(password)
+
+      cy.longLoginLifespan()
+      cy.get('button[type="submit"]').click()
+      cy.get('.messages .message').should(
+        'contain.text',
+        'The login flow expired'
+      )
+
+      // try again with long lifespan set
+      cy.get('input[name="password_identifier"]').type(email.toUpperCase())
+      cy.get('input[name="password"]').type(password)
+      cy.get('button[type="submit"]').click()
+
+      // check that redirection has happened
+      cy.url().should('eq', 'https://www.ory.sh/')
+    })
+  })
 })
diff --git a/test/e2e/cypress/integration/profiles/email/registration/success.spec.js b/test/e2e/cypress/integration/profiles/email/registration/success.spec.js
index 664162427..7be3bedd4 100644
--- a/test/e2e/cypress/integration/profiles/email/registration/success.spec.js
+++ b/test/e2e/cypress/integration/profiles/email/registration/success.spec.js
@@ -34,4 +34,46 @@ context('Email Profile', () => {
       })
     })
   })
+  describe('Registration Flow Success with return_to url', () => {
+    before(() => {
+      cy.useConfigProfile('email')
+    })
+
+    beforeEach(() => {
+      cy.shortRegisterLifespan()
+      cy.browserReturnUrlOry()
+      cy.visit(
+        APP_URL +
+          '/self-service/registration/browser?return_to=https://www.ory.sh/'
+      )
+    })
+
+    it('should redirect to return_to after flow expires', () => {
+      //wait for flow to expire
+      cy.wait(105)
+      const email = gen.email()
+      const password = gen.password()
+      const website = 'https://www.ory.sh/'
+      cy.get('input[name="traits"]').should('not.exist')
+      cy.get('input[name="traits.email"]').type(email)
+      cy.get('input[name="traits.website').type(website)
+      cy.get('input[name="password"]').type(password)
+
+      cy.longRegisterLifespan()
+      cy.get('button[type="submit"]').click()
+      cy.get('.messages .message').should(
+        'contain.text',
+        'The registration flow expired'
+      )
+
+      // try again with long lifespan set
+      cy.get('input[name="traits"]').should('not.exist')
+      cy.get('input[name="traits.email"]').type(email)
+      cy.get('input[name="traits.website').type(website)
+      cy.get('input[name="password"]').type(password)
+      cy.get('button[type="submit"]').click()
+
+      cy.url().should('eq', 'https://www.ory.sh/')
+    })
+  })
 })
diff --git a/test/e2e/cypress/support/commands.js b/test/e2e/cypress/support/commands.js
index b6f5f8572..fd321d761 100644
--- a/test/e2e/cypress/support/commands.js
+++ b/test/e2e/cypress/support/commands.js
@@ -121,6 +121,19 @@ Cypress.Commands.add('enableLoginForVerifiedAddressOnly', () => {
   })
 })
 
+Cypress.Commands.add('shortLoginLifespan', ({} = {}) => {
+  updateConfigFile((config) => {
+    config.selfservice.flows.login.lifespan = '100ms'
+    return config
+  })
+})
+Cypress.Commands.add('longLoginLifespan', ({} = {}) => {
+  updateConfigFile((config) => {
+    config.selfservice.flows.login.lifespan = '1h'
+    return config
+  })
+})
+
 Cypress.Commands.add('shortRecoveryLifespan', ({} = {}) => {
   updateConfigFile((config) => {
     config.selfservice.flows.recovery.lifespan = '1ms'
@@ -260,6 +273,26 @@ Cypress.Commands.add(
   }
 )
 
+Cypress.Commands.add('shortRegisterLifespan', ({} = {}) => {
+  updateConfigFile((config) => {
+    config.selfservice.flows.registration.lifespan = '100ms'
+    return config
+  })
+})
+
+Cypress.Commands.add('longRegisterLifespan', ({} = {}) => {
+  updateConfigFile((config) => {
+    config.selfservice.flows.registration.lifespan = '1h'
+    return config
+  })
+})
+
+Cypress.Commands.add('browserReturnUrlOry', ({} = {}) => {
+  updateConfigFile((config) => {
+    config.selfservice.whitelisted_return_urls = ['https://www.ory.sh/']
+    return config
+  })
+})
 Cypress.Commands.add('loginOidc', ({ expectSession = true }) => {
   cy.visit(APP_URL + '/auth/login')
   cy.get('button[value="hydra"]').click()