diff --git a/selfservice/flow/error.go b/selfservice/flow/error.go index ef3da83be767..14388d909b68 100644 --- a/selfservice/flow/error.go +++ b/selfservice/flow/error.go @@ -3,9 +3,12 @@ package flow import ( "fmt" "net/http" + "net/url" "time" + "github.com/ory/kratos/driver/config" "github.com/ory/kratos/x" + "github.com/ory/x/urlx" "github.com/gofrs/uuid" "github.com/pkg/errors" @@ -86,3 +89,12 @@ func NewBrowserLocationChangeRequiredError(redirectTo string) *BrowserLocationCh }, } } + +func GetFlowExpiredRedirectURL(config *config.Config, route, returnTo string) *url.URL { + redirectURL := urlx.AppendPaths(config.SelfPublicURL(), route) + if returnTo != "" { + redirectURL = urlx.CopyWithQuery(redirectURL, url.Values{"return_to": {returnTo}}) + } + + return redirectURL +} diff --git a/selfservice/flow/login/flow.go b/selfservice/flow/login/flow.go index 98963f59f1ec..13eb1ae86104 100644 --- a/selfservice/flow/login/flow.go +++ b/selfservice/flow/login/flow.go @@ -9,6 +9,8 @@ import ( "strings" "time" + "github.com/gobuffalo/pop/v6" + "github.com/tidwall/gjson" "github.com/ory/x/sqlxx" @@ -185,8 +187,22 @@ func (f *Flow) EnsureInternalContext() { func (f Flow) MarshalJSON() ([]byte, error) { type local Flow + f.SetReturnTo() + return json.Marshal(local(f)) +} + +func (f *Flow) SetReturnTo() { if u, err := url.Parse(f.RequestURL); err == nil { f.ReturnTo = u.Query().Get("return_to") } - return json.Marshal(local(f)) +} + +func (f *Flow) AfterFind(*pop.Connection) error { + f.SetReturnTo() + return nil +} + +func (f *Flow) AfterSave(*pop.Connection) error { + f.SetReturnTo() + return nil } diff --git a/selfservice/flow/login/handler.go b/selfservice/flow/login/handler.go index a196c6732446..1fa96cd34c6c 100644 --- a/selfservice/flow/login/handler.go +++ b/selfservice/flow/login/handler.go @@ -425,9 +425,12 @@ func (h *Handler) fetchFlow(w http.ResponseWriter, r *http.Request, _ httprouter if ar.ExpiresAt.Before(time.Now()) { if ar.Type == flow.TypeBrowser { + redirectURL := flow.GetFlowExpiredRedirectURL(h.d.Config(r.Context()), RouteInitBrowserFlow, ar.ReturnTo) + h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired). WithReason("The login flow has expired. Redirect the user to the login flow init endpoint to initialize a new login flow."). - WithDetail("redirect_to", urlx.AppendPaths(h.d.Config(r.Context()).SelfPublicURL(), RouteInitBrowserFlow).String()))) + WithDetail("redirect_to", redirectURL.String()). + WithDetail("return_to", ar.ReturnTo))) return } h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired). diff --git a/selfservice/flow/login/handler_test.go b/selfservice/flow/login/handler_test.go index 462f73ee225c..66c8f670b328 100644 --- a/selfservice/flow/login/handler_test.go +++ b/selfservice/flow/login/handler_test.go @@ -3,6 +3,7 @@ package login_test import ( "context" "encoding/json" + "fmt" "io/ioutil" "net/http" "net/url" @@ -553,11 +554,12 @@ func TestGetFlow(t *testing.T) { }) t.Run("case=expired with return_to", func(t *testing.T) { - conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{"https://www.ory.sh/"}) + returnTo := "https://www.ory.sh" + conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo}) client := testhelpers.NewClientWithCookies(t) setupLoginUI(t, client) - body := x.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow+"?return_to=https://www.ory.sh") + body := x.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow+"?return_to="+returnTo) // Expire the flow f, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String())) @@ -565,6 +567,11 @@ func TestGetFlow(t *testing.T) { f.ExpiresAt = time.Now().Add(-time.Second) require.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), f)) + // Retrieve the flow and verify that return_to is in the response + getURL := fmt.Sprintf("%s%s?id=%s&return_to=%s", public.URL, login.RouteGetFlow, f.ID, returnTo) + getBody := x.EasyGetBody(t, client, getURL) + assert.Equal(t, gjson.GetBytes(getBody, "error.details.return_to").String(), returnTo) + // 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"}}) @@ -574,7 +581,7 @@ func TestGetFlow(t *testing.T) { 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) + assert.Equal(t, public.URL+login.RouteInitBrowserFlow+"?return_to="+returnTo, f.RequestURL) }) t.Run("case=not found", func(t *testing.T) { diff --git a/selfservice/flow/recovery/flow.go b/selfservice/flow/recovery/flow.go index ba242732c5ed..cc13b7064b8e 100644 --- a/selfservice/flow/recovery/flow.go +++ b/selfservice/flow/recovery/flow.go @@ -7,6 +7,8 @@ import ( "net/url" "time" + "github.com/gobuffalo/pop/v6" + "github.com/gofrs/uuid" "github.com/pkg/errors" @@ -182,8 +184,22 @@ func (f *Flow) SetCSRFToken(token string) { func (f Flow) MarshalJSON() ([]byte, error) { type local Flow + f.SetReturnTo() + return json.Marshal(local(f)) +} + +func (f *Flow) SetReturnTo() { if u, err := url.Parse(f.RequestURL); err == nil { f.ReturnTo = u.Query().Get("return_to") } - return json.Marshal(local(f)) +} + +func (f *Flow) AfterFind(*pop.Connection) error { + f.SetReturnTo() + return nil +} + +func (f *Flow) AfterSave(*pop.Connection) error { + f.SetReturnTo() + return nil } diff --git a/selfservice/flow/recovery/handler.go b/selfservice/flow/recovery/handler.go index bf03e8015671..d3da91a411c8 100644 --- a/selfservice/flow/recovery/handler.go +++ b/selfservice/flow/recovery/handler.go @@ -266,9 +266,12 @@ func (h *Handler) fetch(w http.ResponseWriter, r *http.Request, _ httprouter.Par if f.ExpiresAt.Before(time.Now().UTC()) { if f.Type == flow.TypeBrowser { + redirectURL := flow.GetFlowExpiredRedirectURL(h.d.Config(r.Context()), RouteInitBrowserFlow, f.ReturnTo) + h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. WithReason("The recovery flow has expired. Redirect the user to the recovery flow init endpoint to initialize a new recovery flow."). - WithDetail("redirect_to", urlx.AppendPaths(h.d.Config(r.Context()).SelfPublicURL(), RouteInitBrowserFlow).String()))) + WithDetail("redirect_to", redirectURL.String()). + WithDetail("return_to", f.ReturnTo))) return } h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. diff --git a/selfservice/flow/recovery/handler_test.go b/selfservice/flow/recovery/handler_test.go index f859ae11ce46..16a98c55864c 100644 --- a/selfservice/flow/recovery/handler_test.go +++ b/selfservice/flow/recovery/handler_test.go @@ -3,6 +3,7 @@ package recovery_test import ( "context" "encoding/json" + "fmt" "io/ioutil" "net/http" "net/http/httptest" @@ -246,10 +247,11 @@ func TestGetFlow(t *testing.T) { }) t.Run("case=expired with return_to", func(t *testing.T) { - conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{"https://www.ory.sh/"}) + returnTo := "https://www.ory.sh" + conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo}) client := testhelpers.NewClientWithCookies(t) setupRecoveryTS(t, client) - body := x.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow+"?return_to=https://www.ory.sh") + body := x.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow+"?return_to="+returnTo) // Expire the flow f, err := reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String())) @@ -257,6 +259,11 @@ func TestGetFlow(t *testing.T) { f.ExpiresAt = time.Now().Add(-time.Second) require.NoError(t, reg.RecoveryFlowPersister().UpdateRecoveryFlow(context.Background(), f)) + // Retrieve the flow and verify that return_to is in the response + getURL := fmt.Sprintf("%s%s?id=%s&return_to=%s", public.URL, recovery.RouteGetFlow, f.ID, returnTo) + getBody := x.EasyGetBody(t, client, getURL) + assert.Equal(t, gjson.GetBytes(getBody, "error.details.return_to").String(), returnTo) + // 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"}}) @@ -266,7 +273,7 @@ func TestGetFlow(t *testing.T) { 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) + assert.Equal(t, public.URL+recovery.RouteInitBrowserFlow+"?return_to="+returnTo, f.RequestURL) }) t.Run("case=not found", func(t *testing.T) { diff --git a/selfservice/flow/registration/flow.go b/selfservice/flow/registration/flow.go index f5027d81f474..c442c0208a6a 100644 --- a/selfservice/flow/registration/flow.go +++ b/selfservice/flow/registration/flow.go @@ -7,6 +7,8 @@ import ( "net/url" "time" + "github.com/gobuffalo/pop/v6" + "github.com/tidwall/gjson" "github.com/ory/x/sqlxx" @@ -150,8 +152,22 @@ func (f *Flow) EnsureInternalContext() { func (f Flow) MarshalJSON() ([]byte, error) { type local Flow + f.SetReturnTo() + return json.Marshal(local(f)) +} + +func (f *Flow) SetReturnTo() { if u, err := url.Parse(f.RequestURL); err == nil { f.ReturnTo = u.Query().Get("return_to") } - return json.Marshal(local(f)) +} + +func (f *Flow) AfterFind(*pop.Connection) error { + f.SetReturnTo() + return nil +} + +func (f *Flow) AfterSave(*pop.Connection) error { + f.SetReturnTo() + return nil } diff --git a/selfservice/flow/registration/handler.go b/selfservice/flow/registration/handler.go index 5554dd217a96..89f03bfc1e70 100644 --- a/selfservice/flow/registration/handler.go +++ b/selfservice/flow/registration/handler.go @@ -331,9 +331,12 @@ func (h *Handler) fetchFlow(w http.ResponseWriter, r *http.Request, ps httproute if ar.ExpiresAt.Before(time.Now()) { if ar.Type == flow.TypeBrowser { + redirectURL := flow.GetFlowExpiredRedirectURL(h.d.Config(r.Context()), RouteInitBrowserFlow, ar.ReturnTo) + h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired). WithReason("The registration flow has expired. Redirect the user to the registration flow init endpoint to initialize a new registration flow."). - WithDetail("redirect_to", urlx.AppendPaths(h.d.Config(r.Context()).SelfPublicURL(), RouteInitBrowserFlow).String()))) + WithDetail("redirect_to", redirectURL.String()). + WithDetail("return_to", ar.ReturnTo))) return } h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired). diff --git a/selfservice/flow/registration/handler_test.go b/selfservice/flow/registration/handler_test.go index f00a55b04e0c..fd9ff858337b 100644 --- a/selfservice/flow/registration/handler_test.go +++ b/selfservice/flow/registration/handler_test.go @@ -3,6 +3,7 @@ package registration_test import ( "context" "encoding/json" + "fmt" "io/ioutil" "net/http" "net/http/httptest" @@ -303,10 +304,12 @@ func TestGetFlow(t *testing.T) { }) t.Run("case=expired with return_to", func(t *testing.T) { - conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{"https://www.ory.sh/"}) + returnTo := "https://www.ory.sh" + conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo}) + client := testhelpers.NewClientWithCookies(t) setupRegistrationUI(t, client) - body := x.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow+"?return_to=https://www.ory.sh") + body := x.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow+"?return_to="+returnTo) // Expire the flow f, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String())) @@ -314,6 +317,11 @@ func TestGetFlow(t *testing.T) { f.ExpiresAt = time.Now().Add(-time.Second) require.NoError(t, reg.RegistrationFlowPersister().UpdateRegistrationFlow(context.Background(), f)) + // Retrieve the flow and verify that return_to is in the response + getURL := fmt.Sprintf("%s%s?id=%s&return_to=%s", public.URL, registration.RouteGetFlow, f.ID, returnTo) + getBody := x.EasyGetBody(t, client, getURL) + assert.Equal(t, gjson.GetBytes(getBody, "error.details.return_to").String(), returnTo) + // 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"}}) @@ -323,7 +331,7 @@ func TestGetFlow(t *testing.T) { 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) + assert.Equal(t, public.URL+registration.RouteInitBrowserFlow+"?return_to="+returnTo, f.RequestURL) }) t.Run("case=not found", func(t *testing.T) { diff --git a/selfservice/flow/settings/flow.go b/selfservice/flow/settings/flow.go index d65568019bc2..496df80782b6 100644 --- a/selfservice/flow/settings/flow.go +++ b/selfservice/flow/settings/flow.go @@ -7,6 +7,8 @@ import ( "net/url" "time" + "github.com/gobuffalo/pop/v6" + "github.com/ory/kratos/text" "github.com/tidwall/gjson" @@ -194,8 +196,22 @@ func (f *Flow) EnsureInternalContext() { func (f Flow) MarshalJSON() ([]byte, error) { type local Flow + f.SetReturnTo() + return json.Marshal(local(f)) +} + +func (f *Flow) SetReturnTo() { if u, err := url.Parse(f.RequestURL); err == nil { f.ReturnTo = u.Query().Get("return_to") } - return json.Marshal(local(f)) +} + +func (f *Flow) AfterFind(*pop.Connection) error { + f.SetReturnTo() + return nil +} + +func (f *Flow) AfterSave(*pop.Connection) error { + f.SetReturnTo() + return nil } diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go index 9b47d35b0385..36a4a6111ddb 100644 --- a/selfservice/flow/settings/handler.go +++ b/selfservice/flow/settings/handler.go @@ -377,9 +377,12 @@ func (h *Handler) fetchFlow(w http.ResponseWriter, r *http.Request) error { if pr.ExpiresAt.Before(time.Now().UTC()) { if pr.Type == flow.TypeBrowser { + redirectURL := flow.GetFlowExpiredRedirectURL(h.d.Config(r.Context()), RouteInitBrowserFlow, pr.ReturnTo) + h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. WithReason("The settings flow has expired. Redirect the user to the settings flow init endpoint to initialize a new settings flow."). - WithDetail("redirect_to", urlx.AppendPaths(h.d.Config(r.Context()).SelfPublicURL(), RouteInitBrowserFlow).String()))) + WithDetail("redirect_to", redirectURL.String()). + WithDetail("return_to", pr.ReturnTo))) return nil } h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. diff --git a/selfservice/flow/settings/handler_test.go b/selfservice/flow/settings/handler_test.go index 0aab2fb9d52c..1cf590d76b6e 100644 --- a/selfservice/flow/settings/handler_test.go +++ b/selfservice/flow/settings/handler_test.go @@ -3,6 +3,7 @@ package settings_test import ( "context" "encoding/json" + "fmt" "io/ioutil" "net/http" "net/url" @@ -223,9 +224,11 @@ func TestHandler(t *testing.T) { }) t.Run("case=expired with return_to", func(t *testing.T) { - conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{"https://www.ory.sh/"}) + returnTo := "https://www.ory.sh" + conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo}) + client := testhelpers.NewHTTPClientWithArbitrarySessionToken(t, reg) - body := x.EasyGetBody(t, client, publicTS.URL+settings.RouteInitBrowserFlow+"?return_to=https://www.ory.sh") + body := x.EasyGetBody(t, client, publicTS.URL+settings.RouteInitBrowserFlow+"?return_to="+returnTo) // Expire the flow f, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String())) @@ -233,6 +236,11 @@ func TestHandler(t *testing.T) { f.ExpiresAt = time.Now().Add(-time.Second) require.NoError(t, reg.SettingsFlowPersister().UpdateSettingsFlow(context.Background(), f)) + // Retrieve the flow and verify that return_to is in the response + getURL := fmt.Sprintf("%s%s?id=%s&return_to=%s", publicTS.URL, settings.RouteGetFlow, f.ID, returnTo) + getBody := x.EasyGetBody(t, client, getURL) + assert.Equal(t, gjson.GetBytes(getBody, "error.details.return_to").String(), returnTo) + // 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"}}) @@ -242,7 +250,7 @@ func TestHandler(t *testing.T) { 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) + assert.Equal(t, publicTS.URL+settings.RouteInitBrowserFlow+"?return_to="+returnTo, f.RequestURL) }) t.Run("description=should fail to fetch request if identity changed", func(t *testing.T) { diff --git a/selfservice/flow/verification/flow.go b/selfservice/flow/verification/flow.go index e2697fcfa6a8..a5d190c97f39 100644 --- a/selfservice/flow/verification/flow.go +++ b/selfservice/flow/verification/flow.go @@ -7,6 +7,8 @@ import ( "net/url" "time" + "github.com/gobuffalo/pop/v6" + "github.com/gofrs/uuid" "github.com/pkg/errors" @@ -190,8 +192,22 @@ func (f *Flow) SetCSRFToken(token string) { func (f Flow) MarshalJSON() ([]byte, error) { type local Flow + f.SetReturnTo() + return json.Marshal(local(f)) +} + +func (f *Flow) SetReturnTo() { if u, err := url.Parse(f.RequestURL); err == nil { f.ReturnTo = u.Query().Get("return_to") } - return json.Marshal(local(f)) +} + +func (f *Flow) AfterFind(*pop.Connection) error { + f.SetReturnTo() + return nil +} + +func (f *Flow) AfterSave(*pop.Connection) error { + f.SetReturnTo() + return nil } diff --git a/selfservice/flow/verification/handler.go b/selfservice/flow/verification/handler.go index d2d1b8a3e4a1..c62ede047c03 100644 --- a/selfservice/flow/verification/handler.go +++ b/selfservice/flow/verification/handler.go @@ -248,9 +248,12 @@ func (h *Handler) fetch(w http.ResponseWriter, r *http.Request, _ httprouter.Par if req.ExpiresAt.Before(time.Now().UTC()) { if req.Type == flow.TypeBrowser { + redirectURL := flow.GetFlowExpiredRedirectURL(h.d.Config(r.Context()), RouteInitBrowserFlow, req.ReturnTo) + h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. WithReason("The verification flow has expired. Redirect the user to the verification flow init endpoint to initialize a new verification flow."). - WithDetail("redirect_to", urlx.AppendPaths(h.d.Config(r.Context()).SelfPublicURL(), RouteInitBrowserFlow).String()))) + WithDetail("redirect_to", redirectURL.String()). + WithDetail("return_to", req.ReturnTo))) return } h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. diff --git a/selfservice/flow/verification/handler_test.go b/selfservice/flow/verification/handler_test.go index 24bf899fd6c9..fa70fff5097e 100644 --- a/selfservice/flow/verification/handler_test.go +++ b/selfservice/flow/verification/handler_test.go @@ -2,6 +2,7 @@ package verification_test import ( "context" + "fmt" "io/ioutil" "net/http" "net/url" @@ -109,11 +110,12 @@ func TestGetFlow(t *testing.T) { }) t.Run("case=expired with return_to", func(t *testing.T) { - conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{"https://www.ory.sh/"}) + returnTo := "https://www.ory.sh" + conf.MustSet(config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo}) client := testhelpers.NewClientWithCookies(t) _ = setupVerificationUI(t, client) - body := x.EasyGetBody(t, client, public.URL+verification.RouteInitBrowserFlow+"?return_to=https://www.ory.sh") + body := x.EasyGetBody(t, client, public.URL+verification.RouteInitBrowserFlow+"?return_to="+returnTo) // Expire the flow f, err := reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, "id").String())) @@ -121,6 +123,11 @@ func TestGetFlow(t *testing.T) { f.ExpiresAt = time.Now().Add(-time.Second) require.NoError(t, reg.VerificationFlowPersister().UpdateVerificationFlow(context.Background(), f)) + // Retrieve the flow and verify that return_to is in the response + getURL := fmt.Sprintf("%s%s?id=%s&return_to=%s", public.URL, verification.RouteGetFlow, f.ID, returnTo) + getBody := x.EasyGetBody(t, client, getURL) + assert.Equal(t, gjson.GetBytes(getBody, "error.details.return_to").String(), returnTo) + // 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"}}) @@ -130,7 +137,7 @@ func TestGetFlow(t *testing.T) { 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) + assert.Equal(t, public.URL+verification.RouteInitBrowserFlow+"?return_to="+returnTo, f.RequestURL) }) t.Run("case=relative redirect when self-service verification ui is a relative URL", func(t *testing.T) {