diff --git a/.schema/api.swagger.json b/.schema/api.swagger.json index 715e8ac84ae..7ba4f7750a0 100755 --- a/.schema/api.swagger.json +++ b/.schema/api.swagger.json @@ -624,66 +624,6 @@ } } }, - "/self-service/browser/flows/requests/settings": { - "get": { - "description": "When accessing this endpoint through ORY Kratos' Public API, ensure that cookies are set as they are required\nfor checking the auth session. To prevent scanning attacks, the public endpoint does not return 404 status codes\nbut instead 403 or 500.\n\nMore information can be found at [ORY Kratos User Settings \u0026 Profile Management Documentation](../self-service/flows/user-settings).", - "produces": [ - "application/json" - ], - "schemes": [ - "http", - "https" - ], - "tags": [ - "common", - "public", - "admin" - ], - "summary": "Get the request context of browser-based settings flows", - "operationId": "getSelfServiceBrowserSettingsRequest", - "parameters": [ - { - "type": "string", - "description": "Request is the Login Request ID\n\nThe value for this parameter comes from `request` URL Query parameter sent to your\napplication (e.g. `/settingss?request=abcde`).", - "name": "request", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "settingsRequest", - "schema": { - "$ref": "#/definitions/settingsRequest" - } - }, - "403": { - "description": "genericError", - "schema": { - "$ref": "#/definitions/genericError" - } - }, - "404": { - "description": "genericError", - "schema": { - "$ref": "#/definitions/genericError" - } - }, - "410": { - "description": "genericError", - "schema": { - "$ref": "#/definitions/genericError" - } - }, - "500": { - "description": "genericError", - "schema": { - "$ref": "#/definitions/genericError" - } - } - } - } - }, "/self-service/browser/flows/requests/verification": { "get": { "description": "When accessing this endpoint through ORY Kratos' Public API, ensure that cookies are set as they are required\nfor checking the auth session. To prevent scanning attacks, the public endpoint does not return 404 status codes\nbut instead 403 or 500.\n\nMore information can be found at [ORY Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/selfservice/flows/verify-email-account-activation).", @@ -738,31 +678,6 @@ } } }, - "/self-service/browser/flows/settings": { - "get": { - "description": "This endpoint initializes a browser-based settings flow. Once initialized, the browser will be redirected to\n`selfservice.flows.settings.ui_url` with the request ID set as a query parameter. If no valid user session exists, a login\nflow will be initialized.\n\n\u003e This endpoint is NOT INTENDED for API clients and only works\nwith browsers (Chrome, Firefox, ...).\n\nMore information can be found at [ORY Kratos User Settings \u0026 Profile Management Documentation](../self-service/flows/user-settings).", - "schemes": [ - "http", - "https" - ], - "tags": [ - "public" - ], - "summary": "Initialize browser-based settings flow", - "operationId": "initializeSelfServiceSettingsFlow", - "responses": { - "302": { - "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." - }, - "500": { - "description": "genericError", - "schema": { - "$ref": "#/definitions/genericError" - } - } - } - } - }, "/self-service/browser/flows/settings/strategies/password": { "post": { "description": "This endpoint completes a browser-based settings flow. This is usually achieved by POSTing data to this\nendpoint.\n\n\u003e This endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...) and HTML Forms.\n\nMore information can be found at [ORY Kratos User Settings \u0026 Profile Management Documentation](../self-service/flows/user-settings).", @@ -826,6 +741,12 @@ } ], "responses": { + "200": { + "description": "settingsViaApiResponse", + "schema": { + "$ref": "#/definitions/settingsViaApiResponse" + } + }, "302": { "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." }, @@ -1017,7 +938,7 @@ }, "/self-service/login/api": { "get": { - "description": "This endpoint initiates a login flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing login flow call `/self-service/login/flows?flow=\u003cflow_id\u003e`.\n\n:::note\n\nThis endpoint is NOT INTENDED for browser applications (Chrome, Firefox, ...). We recommend using this endpoint\nfor server-side browser applications and single page apps (SPA).\n\n:::\n\nMore information can be found at [ORY Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).", + "description": "This endpoint initiates a login flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing login flow call `/self-service/login/flows?flow=\u003cflow_id\u003e`.\n\n:::warning\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n:::\n\nMore information can be found at [ORY Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).", "schemes": [ "http", "https" @@ -1061,7 +982,7 @@ }, "/self-service/login/browser": { "get": { - "description": "This endpoint initializes a browser-based user login flow. Once initialized, the browser will be redirected to\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\n`?refresh=true` was set.\n\n:::note\n\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n\n:::\n\nMore information can be found at [ORY Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).", + "description": "This endpoint initializes a browser-based user login flow. Once initialized, the browser will be redirected to\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\n`?refresh=true` was set.\n\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n\nMore information can be found at [ORY Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).", "schemes": [ "http", "https" @@ -1166,14 +1087,22 @@ "parameters": [ { "type": "string", + "description": "The user's password.", "name": "password", "in": "query" }, { "type": "string", + "description": "Identifier is the email or username of the user trying to log in.", "name": "identifier", "in": "query" }, + { + "type": "string", + "description": "Sending the anti-csrf token is only required for browser login flows.", + "name": "csrf_token", + "in": "query" + }, { "type": "string", "description": "The Flow ID", @@ -1209,7 +1138,7 @@ }, "/self-service/registration/api": { "get": { - "description": "This endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=\u003cflow_id\u003e`.\n\n:::note\n\nThis endpoint is NOT INTENDED for browser applications (Chrome, Firefox, ...). We recommend using this endpoint\nfor server-side browser applications and single page apps (SPA).\n\n:::\n\nMore information can be found at [ORY Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).", + "description": "This endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=\u003cflow_id\u003e`.\n\n:::warning\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n:::\n\nMore information can be found at [ORY Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).", "schemes": [ "http", "https" @@ -1372,6 +1301,127 @@ } } }, + "/self-service/settings/api": { + "get": { + "description": "This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\nYou must provide a valid ORY Kratos Session Token for this endpoint to respond with HTTP 200 OK.\n\nTo fetch an existing settings flow call `/self-service/settings/flows?flow=\u003cflow_id\u003e`.\n\n:::warning\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n:::\n\nMore information can be found at [ORY Kratos User Settings \u0026 Profile Management Documentation](../self-service/flows/user-settings).", + "schemes": [ + "http", + "https" + ], + "tags": [ + "common", + "public", + "admin" + ], + "summary": "Initialize Settings Flow for API Clients", + "operationId": "initializeSelfServiceSettingsViaAPIFlow", + "responses": { + "200": { + "description": "settingsFlow", + "schema": { + "$ref": "#/definitions/settingsFlow" + } + }, + "400": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } + }, + "500": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } + } + } + } + }, + "/self-service/settings/browser/flows": { + "get": { + "description": "This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\nORY Kratos Session Cookie is included in the request, a login flow will be initialized.\n\n:::note\n\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n\n:::\n\nMore information can be found at [ORY Kratos User Settings \u0026 Profile Management Documentation](../self-service/flows/user-settings).", + "schemes": [ + "http", + "https" + ], + "tags": [ + "public" + ], + "summary": "Initialize Settings Flow for Browsers", + "operationId": "initializeSelfServiceSettingsViaBrowserFlow", + "responses": { + "302": { + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." + }, + "500": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } + } + } + } + }, + "/self-service/settings/flows": { + "get": { + "description": "When accessing this endpoint through ORY Kratos' Public API you must ensure that either the ORY Kratos Session Cookie\nor the ORY Kratos Session Token are set. The public endpoint does not return 404 status codes\nbut instead 403 or 500 to improve data privacy.\n\nYou can access this endpoint without credentials when using ORY Kratos' Admin API.\n\nMore information can be found at [ORY Kratos User Settings \u0026 Profile Management Documentation](../self-service/flows/user-settings).", + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "common", + "public", + "admin" + ], + "summary": "Get Information About a Settings Flow", + "operationId": "getSelfServiceSettingsFlow", + "parameters": [ + { + "type": "string", + "description": "ID is the Settings Flow ID\n\nThe value for this parameter comes from `flow` URL Query parameter sent to your\napplication (e.g. `/settings?flow=abcde`).", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "settingsFlow", + "schema": { + "$ref": "#/definitions/settingsFlow" + } + }, + "403": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } + }, + "404": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } + }, + "410": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } + }, + "500": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } + } + } + } + }, "/sessions": { "delete": { "description": "Use this endpoint to revoke a session using its token. This endpoint is particularly useful for API clients\nsuch as mobile apps to log the user out of the system and invalidate the session.\n\nThis endpoint does not remove any HTTP Cookies - use the Self-Service Logout Flow instead.", @@ -1485,6 +1535,30 @@ "type": "string", "title": "CredentialsType represents several different credential types, like password credentials, passwordless credentials," }, + "FlowMethodConfig": { + "type": "object", + "required": [ + "action", + "method", + "fields" + ], + "properties": { + "action": { + "description": "Action should be used as the form action URL `\u003cform action=\"{{ .Action }}\" method=\"post\"\u003e`.", + "type": "string" + }, + "fields": { + "$ref": "#/definitions/formFields" + }, + "messages": { + "$ref": "#/definitions/Messages" + }, + "method": { + "description": "Method is the form method (e.g. POST)", + "type": "string" + } + } + }, "ID": { "type": "integer", "format": "int64" @@ -2153,10 +2227,10 @@ } } }, - "settingsRequest": { - "description": "This request is used when an identity wants to update settings\n(e.g. profile data, passwords, ...) in a selfservice manner.\n\nWe recommend reading the [User Settings Documentation](../self-service/flows/user-settings)", + "settingsFlow": { + "description": "This flow is used when an identity wants to update settings\n(e.g. profile data, passwords, ...) in a selfservice manner.\n\nWe recommend reading the [User Settings Documentation](../self-service/flows/user-settings)", "type": "object", - "title": "Request presents a settings request", + "title": "Flow represents a Settings Flow", "required": [ "id", "expires_at", @@ -2172,7 +2246,7 @@ "type": "string" }, "expires_at": { - "description": "ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting,\na new request has to be initiated.", + "description": "ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting,\na new flow has to be initiated.", "type": "string", "format": "date-time" }, @@ -2183,7 +2257,7 @@ "$ref": "#/definitions/Identity" }, "issued_at": { - "description": "IssuedAt is the time (UTC) when the request occurred.", + "description": "IssuedAt is the time (UTC) when the flow occurred.", "type": "string", "format": "date-time" }, @@ -2191,10 +2265,10 @@ "$ref": "#/definitions/Messages" }, "methods": { - "description": "Methods contains context for all enabled registration methods. If a registration request has been\nprocessed, but for example the password is incorrect, this will contain error messages.", + "description": "Methods contains context for all enabled registration methods. If a settings flow has been\nprocessed, but for example the first name is empty, this will contain error messages.", "type": "object", "additionalProperties": { - "$ref": "#/definitions/settingsRequestMethod" + "$ref": "#/definitions/settingsFlowMethod" } }, "request_url": { @@ -2209,18 +2283,34 @@ } } }, - "settingsRequestMethod": { + "settingsFlowMethod": { "type": "object", "properties": { "config": { - "$ref": "#/definitions/RequestMethodConfig" + "$ref": "#/definitions/FlowMethodConfig" }, "method": { - "description": "Method contains the request credentials type.", + "description": "Method is the name of this flow method.", "type": "string" } } }, + "settingsViaApiResponse": { + "description": "The Response for Settings Flows via API", + "type": "object", + "required": [ + "flow", + "identity" + ], + "properties": { + "flow": { + "$ref": "#/definitions/settingsFlow" + }, + "identity": { + "$ref": "#/definitions/Identity" + } + } + }, "verificationRequest": { "description": "This request is used when an identity wants to verify an out-of-band communication\nchannel such as an email address or a phone number.\n\nFor more information head over to: https://www.ory.sh/docs/kratos/selfservice/flows/verify-email-account-activation", "type": "object", @@ -2277,4 +2367,4 @@ }, "x-forwarded-proto": "string", "x-request-id": "string" -} \ No newline at end of file +} diff --git a/cmd/daemon/serve.go b/cmd/daemon/serve.go index 31b2d4a923b..8318253d2ff 100644 --- a/cmd/daemon/serve.go +++ b/cmd/daemon/serve.go @@ -136,8 +136,8 @@ func sqa(cmd *cobra.Command, d driver.Driver) *metricsx.Service { session.RouteWhoami, identity.IdentitiesPath, profile.PublicSettingsProfilePath, - settings.PublicPath, - settings.PublicRequestPath, + settings.RouteInitBrowserFlow, + settings.RouteGetFlow, profile.PublicSettingsProfilePath, verification.PublicVerificationCompletePath, strings.ReplaceAll(strings.ReplaceAll(verification.PublicVerificationConfirmPath, ":via", "email"), ":code", ""), diff --git a/driver/configuration/provider.go b/driver/configuration/provider.go index f448ca3e5a6..6467c720a81 100644 --- a/driver/configuration/provider.go +++ b/driver/configuration/provider.go @@ -88,7 +88,7 @@ type Provider interface { SelfServiceFlowSettingsPrivilegedSessionMaxAge() time.Duration SelfServiceFlowSettingsAfterHooks(strategy string) []SelfServiceHook SelfServiceFlowSettingsReturnTo(strategy string, defaultReturnTo *url.URL) *url.URL - SelfServiceFlowSettingsRequestLifespan() time.Duration + SelfServiceFlowSettingsFlowLifespan() time.Duration SelfServiceFlowVerificationEnabled() bool SelfServiceFlowVerificationUI() *url.URL diff --git a/driver/configuration/provider_viper.go b/driver/configuration/provider_viper.go index c0744985602..6d5d852acb3 100644 --- a/driver/configuration/provider_viper.go +++ b/driver/configuration/provider_viper.go @@ -408,7 +408,7 @@ func (p *ViperProvider) SelfServiceFlowLoginRequestLifespan() time.Duration { return viperx.GetDuration(p.l, ViperKeySelfServiceLoginRequestLifespan, time.Hour) } -func (p *ViperProvider) SelfServiceFlowSettingsRequestLifespan() time.Duration { +func (p *ViperProvider) SelfServiceFlowSettingsFlowLifespan() time.Duration { return viperx.GetDuration(p.l, ViperKeySelfServiceSettingsRequestLifespan, time.Hour) } diff --git a/driver/configuration/provider_viper_test.go b/driver/configuration/provider_viper_test.go index 1aa545e569e..bf0643d5eaf 100644 --- a/driver/configuration/provider_viper_test.go +++ b/driver/configuration/provider_viper_test.go @@ -199,7 +199,7 @@ func TestViperProvider(t *testing.T) { }) t.Run("method=settings", func(t *testing.T) { - assert.Equal(t, time.Minute*99, p.SelfServiceFlowSettingsRequestLifespan()) + assert.Equal(t, time.Minute*99, p.SelfServiceFlowSettingsFlowLifespan()) assert.Equal(t, time.Minute*5, p.SelfServiceFlowSettingsPrivilegedSessionMaxAge()) t.Run("hook=before", func(t *testing.T) { diff --git a/driver/registry.go b/driver/registry.go index dff755dbf3e..ff932aa74a6 100644 --- a/driver/registry.go +++ b/driver/registry.go @@ -92,7 +92,7 @@ type Registry interface { settings.HandlerProvider settings.ErrorHandlerProvider - settings.RequestPersistenceProvider + settings.FlowPersistenceProvider settings.StrategyProvider login.FlowPersistenceProvider diff --git a/driver/registry_default.go b/driver/registry_default.go index d69199dab3b..395ca6719db 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -494,7 +494,7 @@ func (m *RegistryDefault) LoginFlowPersister() login.FlowPersister { return m.persister } -func (m *RegistryDefault) SettingsRequestPersister() settings.RequestPersister { +func (m *RegistryDefault) SettingsFlowPersister() settings.FlowPersister { return m.persister } diff --git a/driver/registry_default_settings.go b/driver/registry_default_settings.go index 4af6fc2d717..6d17477f2fd 100644 --- a/driver/registry_default_settings.go +++ b/driver/registry_default_settings.go @@ -38,7 +38,7 @@ func (m *RegistryDefault) SettingsHandler() *settings.Handler { return m.selfserviceSettingsHandler } -func (m *RegistryDefault) SettingsRequestErrorHandler() *settings.ErrorHandler { +func (m *RegistryDefault) SettingsFlowErrorHandler() *settings.ErrorHandler { if m.selfserviceSettingsErrorHandler == nil { m.selfserviceSettingsErrorHandler = settings.NewErrorHandler(m, m.c) } diff --git a/internal/faker.go b/internal/faker.go index 8ff47964504..e348930e888 100644 --- a/internal/faker.go +++ b/internal/faker.go @@ -117,15 +117,15 @@ func RegisterFakes() { } if err := faker.AddProvider("settings_flow_methods", func(v reflect.Value) (interface{}, error) { - var methods = make(map[string]*settings.RequestMethod) + var methods = make(map[string]*settings.FlowMethod) for _, ct := range []string{settings.StrategyProfile, string(identity.CredentialsTypePassword), string(identity.CredentialsTypeOIDC)} { var f form.HTMLForm if err := faker.FakeData(&f); err != nil { return nil, err } - methods[ct] = &settings.RequestMethod{ + methods[ct] = &settings.FlowMethod{ Method: ct, - Config: &settings.RequestMethodConfig{RequestMethodConfigurator: &f}, + Config: &settings.FlowMethodConfig{FlowMethodConfigurator: &f}, } } return methods, nil diff --git a/internal/httpclient/client/common/common_client.go b/internal/httpclient/client/common/common_client.go index 0079ba3b3d7..7c6a41b2128 100644 --- a/internal/httpclient/client/common/common_client.go +++ b/internal/httpclient/client/common/common_client.go @@ -31,20 +31,22 @@ type ClientService interface { GetSelfServiceBrowserRecoveryRequest(params *GetSelfServiceBrowserRecoveryRequestParams) (*GetSelfServiceBrowserRecoveryRequestOK, error) - GetSelfServiceBrowserSettingsRequest(params *GetSelfServiceBrowserSettingsRequestParams) (*GetSelfServiceBrowserSettingsRequestOK, error) - GetSelfServiceError(params *GetSelfServiceErrorParams) (*GetSelfServiceErrorOK, error) GetSelfServiceLoginFlow(params *GetSelfServiceLoginFlowParams) (*GetSelfServiceLoginFlowOK, error) GetSelfServiceRegistrationFlow(params *GetSelfServiceRegistrationFlowParams) (*GetSelfServiceRegistrationFlowOK, error) + GetSelfServiceSettingsFlow(params *GetSelfServiceSettingsFlowParams) (*GetSelfServiceSettingsFlowOK, error) + GetSelfServiceVerificationRequest(params *GetSelfServiceVerificationRequestParams) (*GetSelfServiceVerificationRequestOK, error) InitializeSelfServiceLoginViaAPIFlow(params *InitializeSelfServiceLoginViaAPIFlowParams) (*InitializeSelfServiceLoginViaAPIFlowOK, error) InitializeSelfServiceRegistrationViaAPIFlow(params *InitializeSelfServiceRegistrationViaAPIFlowParams) (*InitializeSelfServiceRegistrationViaAPIFlowOK, error) + InitializeSelfServiceSettingsViaAPIFlow(params *InitializeSelfServiceSettingsViaAPIFlowParams) (*InitializeSelfServiceSettingsViaAPIFlowOK, error) + SetTransport(transport runtime.ClientTransport) } @@ -122,46 +124,6 @@ func (a *Client) GetSelfServiceBrowserRecoveryRequest(params *GetSelfServiceBrow panic(msg) } -/* - GetSelfServiceBrowserSettingsRequest gets the request context of browser based settings flows - - When accessing this endpoint through ORY Kratos' Public API, ensure that cookies are set as they are required -for checking the auth session. To prevent scanning attacks, the public endpoint does not return 404 status codes -but instead 403 or 500. - -More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). -*/ -func (a *Client) GetSelfServiceBrowserSettingsRequest(params *GetSelfServiceBrowserSettingsRequestParams) (*GetSelfServiceBrowserSettingsRequestOK, error) { - // TODO: Validate the params before sending - if params == nil { - params = NewGetSelfServiceBrowserSettingsRequestParams() - } - - result, err := a.transport.Submit(&runtime.ClientOperation{ - ID: "getSelfServiceBrowserSettingsRequest", - Method: "GET", - PathPattern: "/self-service/browser/flows/requests/settings", - ProducesMediaTypes: []string{"application/json"}, - ConsumesMediaTypes: []string{"application/json", "application/x-www-form-urlencoded"}, - Schemes: []string{"http", "https"}, - Params: params, - Reader: &GetSelfServiceBrowserSettingsRequestReader{formats: a.formats}, - Context: params.Context, - Client: params.HTTPClient, - }) - if err != nil { - return nil, err - } - success, ok := result.(*GetSelfServiceBrowserSettingsRequestOK) - if ok { - return success, nil - } - // unexpected success response - // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue - msg := fmt.Sprintf("unexpected success response for getSelfServiceBrowserSettingsRequest: API contract not enforced by server. Client expected to get an error, but got: %T", result) - panic(msg) -} - /* GetSelfServiceError gets user facing self service errors @@ -283,6 +245,48 @@ func (a *Client) GetSelfServiceRegistrationFlow(params *GetSelfServiceRegistrati panic(msg) } +/* + GetSelfServiceSettingsFlow gets information about a settings flow + + When accessing this endpoint through ORY Kratos' Public API you must ensure that either the ORY Kratos Session Cookie +or the ORY Kratos Session Token are set. The public endpoint does not return 404 status codes +but instead 403 or 500 to improve data privacy. + +You can access this endpoint without credentials when using ORY Kratos' Admin API. + +More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). +*/ +func (a *Client) GetSelfServiceSettingsFlow(params *GetSelfServiceSettingsFlowParams) (*GetSelfServiceSettingsFlowOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetSelfServiceSettingsFlowParams() + } + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "getSelfServiceSettingsFlow", + Method: "GET", + PathPattern: "/self-service/settings/flows", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json", "application/x-www-form-urlencoded"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &GetSelfServiceSettingsFlowReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + success, ok := result.(*GetSelfServiceSettingsFlowOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getSelfServiceSettingsFlow: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* GetSelfServiceVerificationRequest gets the request context of browser based verification flows @@ -333,10 +337,13 @@ will be returned unless the URL query parameter `?refresh=true` is set. To fetch an existing login flow call `/self-service/login/flows?flow=`. -:::note +:::warning + +You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server +Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make +you vulnerable to a variety of CSRF attacks, including CSRF login attacks. -This endpoint is NOT INTENDED for browser applications (Chrome, Firefox, ...). We recommend using this endpoint -for server-side browser applications and single page apps (SPA). +This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...). ::: @@ -383,10 +390,13 @@ will be returned unless the URL query parameter `?refresh=true` is set. To fetch an existing registration flow call `/self-service/registration/flows?flow=`. -:::note +:::warning -This endpoint is NOT INTENDED for browser applications (Chrome, Firefox, ...). We recommend using this endpoint -for server-side browser applications and single page apps (SPA). +You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server +Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make +you vulnerable to a variety of CSRF attacks. + +This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...). ::: @@ -423,6 +433,57 @@ func (a *Client) InitializeSelfServiceRegistrationViaAPIFlow(params *InitializeS panic(msg) } +/* + InitializeSelfServiceSettingsViaAPIFlow initializes settings flow for API clients + + This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on. +You must provide a valid ORY Kratos Session Token for this endpoint to respond with HTTP 200 OK. + +To fetch an existing settings flow call `/self-service/settings/flows?flow=`. + +:::warning + +You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server +Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make +you vulnerable to a variety of CSRF attacks. + +This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...). + +::: + +More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). +*/ +func (a *Client) InitializeSelfServiceSettingsViaAPIFlow(params *InitializeSelfServiceSettingsViaAPIFlowParams) (*InitializeSelfServiceSettingsViaAPIFlowOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewInitializeSelfServiceSettingsViaAPIFlowParams() + } + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "initializeSelfServiceSettingsViaAPIFlow", + Method: "GET", + PathPattern: "/self-service/settings/api", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json", "application/x-www-form-urlencoded"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &InitializeSelfServiceSettingsViaAPIFlowReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + success, ok := result.(*InitializeSelfServiceSettingsViaAPIFlowOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for initializeSelfServiceSettingsViaAPIFlow: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + // SetTransport changes the transport on the client func (a *Client) SetTransport(transport runtime.ClientTransport) { a.transport = transport diff --git a/internal/httpclient/client/common/get_self_service_browser_settings_request_parameters.go b/internal/httpclient/client/common/get_self_service_browser_settings_request_parameters.go deleted file mode 100644 index b5b4ebfc81e..00000000000 --- a/internal/httpclient/client/common/get_self_service_browser_settings_request_parameters.go +++ /dev/null @@ -1,142 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package common - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "context" - "net/http" - "time" - - "github.com/go-openapi/errors" - "github.com/go-openapi/runtime" - cr "github.com/go-openapi/runtime/client" - "github.com/go-openapi/strfmt" -) - -// NewGetSelfServiceBrowserSettingsRequestParams creates a new GetSelfServiceBrowserSettingsRequestParams object -// with the default values initialized. -func NewGetSelfServiceBrowserSettingsRequestParams() *GetSelfServiceBrowserSettingsRequestParams { - var () - return &GetSelfServiceBrowserSettingsRequestParams{ - - timeout: cr.DefaultTimeout, - } -} - -// NewGetSelfServiceBrowserSettingsRequestParamsWithTimeout creates a new GetSelfServiceBrowserSettingsRequestParams object -// with the default values initialized, and the ability to set a timeout on a request -func NewGetSelfServiceBrowserSettingsRequestParamsWithTimeout(timeout time.Duration) *GetSelfServiceBrowserSettingsRequestParams { - var () - return &GetSelfServiceBrowserSettingsRequestParams{ - - timeout: timeout, - } -} - -// NewGetSelfServiceBrowserSettingsRequestParamsWithContext creates a new GetSelfServiceBrowserSettingsRequestParams object -// with the default values initialized, and the ability to set a context for a request -func NewGetSelfServiceBrowserSettingsRequestParamsWithContext(ctx context.Context) *GetSelfServiceBrowserSettingsRequestParams { - var () - return &GetSelfServiceBrowserSettingsRequestParams{ - - Context: ctx, - } -} - -// NewGetSelfServiceBrowserSettingsRequestParamsWithHTTPClient creates a new GetSelfServiceBrowserSettingsRequestParams object -// with the default values initialized, and the ability to set a custom HTTPClient for a request -func NewGetSelfServiceBrowserSettingsRequestParamsWithHTTPClient(client *http.Client) *GetSelfServiceBrowserSettingsRequestParams { - var () - return &GetSelfServiceBrowserSettingsRequestParams{ - HTTPClient: client, - } -} - -/*GetSelfServiceBrowserSettingsRequestParams contains all the parameters to send to the API endpoint -for the get self service browser settings request operation typically these are written to a http.Request -*/ -type GetSelfServiceBrowserSettingsRequestParams struct { - - /*Request - Request is the Login Request ID - - The value for this parameter comes from `request` URL Query parameter sent to your - application (e.g. `/settingss?request=abcde`). - - */ - Request string - - timeout time.Duration - Context context.Context - HTTPClient *http.Client -} - -// WithTimeout adds the timeout to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) WithTimeout(timeout time.Duration) *GetSelfServiceBrowserSettingsRequestParams { - o.SetTimeout(timeout) - return o -} - -// SetTimeout adds the timeout to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) SetTimeout(timeout time.Duration) { - o.timeout = timeout -} - -// WithContext adds the context to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) WithContext(ctx context.Context) *GetSelfServiceBrowserSettingsRequestParams { - o.SetContext(ctx) - return o -} - -// SetContext adds the context to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) SetContext(ctx context.Context) { - o.Context = ctx -} - -// WithHTTPClient adds the HTTPClient to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) WithHTTPClient(client *http.Client) *GetSelfServiceBrowserSettingsRequestParams { - o.SetHTTPClient(client) - return o -} - -// SetHTTPClient adds the HTTPClient to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) SetHTTPClient(client *http.Client) { - o.HTTPClient = client -} - -// WithRequest adds the request to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) WithRequest(request string) *GetSelfServiceBrowserSettingsRequestParams { - o.SetRequest(request) - return o -} - -// SetRequest adds the request to the get self service browser settings request params -func (o *GetSelfServiceBrowserSettingsRequestParams) SetRequest(request string) { - o.Request = request -} - -// WriteToRequest writes these params to a swagger request -func (o *GetSelfServiceBrowserSettingsRequestParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { - - if err := r.SetTimeout(o.timeout); err != nil { - return err - } - var res []error - - // query param request - qrRequest := o.Request - qRequest := qrRequest - if qRequest != "" { - if err := r.SetQueryParam("request", qRequest); err != nil { - return err - } - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} diff --git a/internal/httpclient/client/common/get_self_service_browser_settings_request_responses.go b/internal/httpclient/client/common/get_self_service_browser_settings_request_responses.go deleted file mode 100644 index 2bd06d6b811..00000000000 --- a/internal/httpclient/client/common/get_self_service_browser_settings_request_responses.go +++ /dev/null @@ -1,225 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package common - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "fmt" - "io" - - "github.com/go-openapi/runtime" - "github.com/go-openapi/strfmt" - - "github.com/ory/kratos/internal/httpclient/models" -) - -// GetSelfServiceBrowserSettingsRequestReader is a Reader for the GetSelfServiceBrowserSettingsRequest structure. -type GetSelfServiceBrowserSettingsRequestReader struct { - formats strfmt.Registry -} - -// ReadResponse reads a server response into the received o. -func (o *GetSelfServiceBrowserSettingsRequestReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { - switch response.Code() { - case 200: - result := NewGetSelfServiceBrowserSettingsRequestOK() - if err := result.readResponse(response, consumer, o.formats); err != nil { - return nil, err - } - return result, nil - case 403: - result := NewGetSelfServiceBrowserSettingsRequestForbidden() - if err := result.readResponse(response, consumer, o.formats); err != nil { - return nil, err - } - return nil, result - case 404: - result := NewGetSelfServiceBrowserSettingsRequestNotFound() - if err := result.readResponse(response, consumer, o.formats); err != nil { - return nil, err - } - return nil, result - case 410: - result := NewGetSelfServiceBrowserSettingsRequestGone() - if err := result.readResponse(response, consumer, o.formats); err != nil { - return nil, err - } - return nil, result - case 500: - result := NewGetSelfServiceBrowserSettingsRequestInternalServerError() - if err := result.readResponse(response, consumer, o.formats); err != nil { - return nil, err - } - return nil, result - - default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) - } -} - -// NewGetSelfServiceBrowserSettingsRequestOK creates a GetSelfServiceBrowserSettingsRequestOK with default headers values -func NewGetSelfServiceBrowserSettingsRequestOK() *GetSelfServiceBrowserSettingsRequestOK { - return &GetSelfServiceBrowserSettingsRequestOK{} -} - -/*GetSelfServiceBrowserSettingsRequestOK handles this case with default header values. - -settingsRequest -*/ -type GetSelfServiceBrowserSettingsRequestOK struct { - Payload *models.SettingsRequest -} - -func (o *GetSelfServiceBrowserSettingsRequestOK) Error() string { - return fmt.Sprintf("[GET /self-service/browser/flows/requests/settings][%d] getSelfServiceBrowserSettingsRequestOK %+v", 200, o.Payload) -} - -func (o *GetSelfServiceBrowserSettingsRequestOK) GetPayload() *models.SettingsRequest { - return o.Payload -} - -func (o *GetSelfServiceBrowserSettingsRequestOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - - o.Payload = new(models.SettingsRequest) - - // response payload - if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { - return err - } - - return nil -} - -// NewGetSelfServiceBrowserSettingsRequestForbidden creates a GetSelfServiceBrowserSettingsRequestForbidden with default headers values -func NewGetSelfServiceBrowserSettingsRequestForbidden() *GetSelfServiceBrowserSettingsRequestForbidden { - return &GetSelfServiceBrowserSettingsRequestForbidden{} -} - -/*GetSelfServiceBrowserSettingsRequestForbidden handles this case with default header values. - -genericError -*/ -type GetSelfServiceBrowserSettingsRequestForbidden struct { - Payload *models.GenericError -} - -func (o *GetSelfServiceBrowserSettingsRequestForbidden) Error() string { - return fmt.Sprintf("[GET /self-service/browser/flows/requests/settings][%d] getSelfServiceBrowserSettingsRequestForbidden %+v", 403, o.Payload) -} - -func (o *GetSelfServiceBrowserSettingsRequestForbidden) GetPayload() *models.GenericError { - return o.Payload -} - -func (o *GetSelfServiceBrowserSettingsRequestForbidden) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - - o.Payload = new(models.GenericError) - - // response payload - if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { - return err - } - - return nil -} - -// NewGetSelfServiceBrowserSettingsRequestNotFound creates a GetSelfServiceBrowserSettingsRequestNotFound with default headers values -func NewGetSelfServiceBrowserSettingsRequestNotFound() *GetSelfServiceBrowserSettingsRequestNotFound { - return &GetSelfServiceBrowserSettingsRequestNotFound{} -} - -/*GetSelfServiceBrowserSettingsRequestNotFound handles this case with default header values. - -genericError -*/ -type GetSelfServiceBrowserSettingsRequestNotFound struct { - Payload *models.GenericError -} - -func (o *GetSelfServiceBrowserSettingsRequestNotFound) Error() string { - return fmt.Sprintf("[GET /self-service/browser/flows/requests/settings][%d] getSelfServiceBrowserSettingsRequestNotFound %+v", 404, o.Payload) -} - -func (o *GetSelfServiceBrowserSettingsRequestNotFound) GetPayload() *models.GenericError { - return o.Payload -} - -func (o *GetSelfServiceBrowserSettingsRequestNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - - o.Payload = new(models.GenericError) - - // response payload - if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { - return err - } - - return nil -} - -// NewGetSelfServiceBrowserSettingsRequestGone creates a GetSelfServiceBrowserSettingsRequestGone with default headers values -func NewGetSelfServiceBrowserSettingsRequestGone() *GetSelfServiceBrowserSettingsRequestGone { - return &GetSelfServiceBrowserSettingsRequestGone{} -} - -/*GetSelfServiceBrowserSettingsRequestGone handles this case with default header values. - -genericError -*/ -type GetSelfServiceBrowserSettingsRequestGone struct { - Payload *models.GenericError -} - -func (o *GetSelfServiceBrowserSettingsRequestGone) Error() string { - return fmt.Sprintf("[GET /self-service/browser/flows/requests/settings][%d] getSelfServiceBrowserSettingsRequestGone %+v", 410, o.Payload) -} - -func (o *GetSelfServiceBrowserSettingsRequestGone) GetPayload() *models.GenericError { - return o.Payload -} - -func (o *GetSelfServiceBrowserSettingsRequestGone) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - - o.Payload = new(models.GenericError) - - // response payload - if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { - return err - } - - return nil -} - -// NewGetSelfServiceBrowserSettingsRequestInternalServerError creates a GetSelfServiceBrowserSettingsRequestInternalServerError with default headers values -func NewGetSelfServiceBrowserSettingsRequestInternalServerError() *GetSelfServiceBrowserSettingsRequestInternalServerError { - return &GetSelfServiceBrowserSettingsRequestInternalServerError{} -} - -/*GetSelfServiceBrowserSettingsRequestInternalServerError handles this case with default header values. - -genericError -*/ -type GetSelfServiceBrowserSettingsRequestInternalServerError struct { - Payload *models.GenericError -} - -func (o *GetSelfServiceBrowserSettingsRequestInternalServerError) Error() string { - return fmt.Sprintf("[GET /self-service/browser/flows/requests/settings][%d] getSelfServiceBrowserSettingsRequestInternalServerError %+v", 500, o.Payload) -} - -func (o *GetSelfServiceBrowserSettingsRequestInternalServerError) GetPayload() *models.GenericError { - return o.Payload -} - -func (o *GetSelfServiceBrowserSettingsRequestInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - - o.Payload = new(models.GenericError) - - // response payload - if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { - return err - } - - return nil -} diff --git a/internal/httpclient/client/public/complete_self_service_browser_settings_profile_strategy_flow_responses.go b/internal/httpclient/client/public/complete_self_service_browser_settings_profile_strategy_flow_responses.go index 2df539415c5..5e5f1e84edf 100644 --- a/internal/httpclient/client/public/complete_self_service_browser_settings_profile_strategy_flow_responses.go +++ b/internal/httpclient/client/public/complete_self_service_browser_settings_profile_strategy_flow_responses.go @@ -23,6 +23,12 @@ type CompleteSelfServiceBrowserSettingsProfileStrategyFlowReader struct { // ReadResponse reads a server response into the received o. func (o *CompleteSelfServiceBrowserSettingsProfileStrategyFlowReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { + case 200: + result := NewCompleteSelfServiceBrowserSettingsProfileStrategyFlowOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil case 302: result := NewCompleteSelfServiceBrowserSettingsProfileStrategyFlowFound() if err := result.readResponse(response, consumer, o.formats); err != nil { @@ -41,6 +47,39 @@ func (o *CompleteSelfServiceBrowserSettingsProfileStrategyFlowReader) ReadRespon } } +// NewCompleteSelfServiceBrowserSettingsProfileStrategyFlowOK creates a CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK with default headers values +func NewCompleteSelfServiceBrowserSettingsProfileStrategyFlowOK() *CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK { + return &CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK{} +} + +/*CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK handles this case with default header values. + +settingsViaApiResponse +*/ +type CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK struct { + Payload *models.SettingsViaAPIResponse +} + +func (o *CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK) Error() string { + return fmt.Sprintf("[POST /self-service/browser/flows/settings/strategies/profile][%d] completeSelfServiceBrowserSettingsProfileStrategyFlowOK %+v", 200, o.Payload) +} + +func (o *CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK) GetPayload() *models.SettingsViaAPIResponse { + return o.Payload +} + +func (o *CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.SettingsViaAPIResponse) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + // NewCompleteSelfServiceBrowserSettingsProfileStrategyFlowFound creates a CompleteSelfServiceBrowserSettingsProfileStrategyFlowFound with default headers values func NewCompleteSelfServiceBrowserSettingsProfileStrategyFlowFound() *CompleteSelfServiceBrowserSettingsProfileStrategyFlowFound { return &CompleteSelfServiceBrowserSettingsProfileStrategyFlowFound{} diff --git a/internal/httpclient/client/public/complete_self_service_login_flow_with_password_method_parameters.go b/internal/httpclient/client/public/complete_self_service_login_flow_with_password_method_parameters.go index 6895ce19021..5e13742bb5b 100644 --- a/internal/httpclient/client/public/complete_self_service_login_flow_with_password_method_parameters.go +++ b/internal/httpclient/client/public/complete_self_service_login_flow_with_password_method_parameters.go @@ -60,14 +60,25 @@ for the complete self service login flow with password method operation typicall */ type CompleteSelfServiceLoginFlowWithPasswordMethodParams struct { + /*CsrfToken + Sending the anti-csrf token is only required for browser login flows. + + */ + CsrfToken *string /*Flow The Flow ID */ Flow string - /*Identifier*/ + /*Identifier + Identifier is the email or username of the user trying to log in. + + */ Identifier *string - /*Password*/ + /*Password + The user's password. + + */ Password *string timeout time.Duration @@ -108,6 +119,17 @@ func (o *CompleteSelfServiceLoginFlowWithPasswordMethodParams) SetHTTPClient(cli o.HTTPClient = client } +// WithCsrfToken adds the csrfToken to the complete self service login flow with password method params +func (o *CompleteSelfServiceLoginFlowWithPasswordMethodParams) WithCsrfToken(csrfToken *string) *CompleteSelfServiceLoginFlowWithPasswordMethodParams { + o.SetCsrfToken(csrfToken) + return o +} + +// SetCsrfToken adds the csrfToken to the complete self service login flow with password method params +func (o *CompleteSelfServiceLoginFlowWithPasswordMethodParams) SetCsrfToken(csrfToken *string) { + o.CsrfToken = csrfToken +} + // WithFlow adds the flow to the complete self service login flow with password method params func (o *CompleteSelfServiceLoginFlowWithPasswordMethodParams) WithFlow(flow string) *CompleteSelfServiceLoginFlowWithPasswordMethodParams { o.SetFlow(flow) @@ -149,6 +171,22 @@ func (o *CompleteSelfServiceLoginFlowWithPasswordMethodParams) WriteToRequest(r } var res []error + if o.CsrfToken != nil { + + // query param csrf_token + var qrCsrfToken string + if o.CsrfToken != nil { + qrCsrfToken = *o.CsrfToken + } + qCsrfToken := qrCsrfToken + if qCsrfToken != "" { + if err := r.SetQueryParam("csrf_token", qCsrfToken); err != nil { + return err + } + } + + } + // query param flow qrFlow := o.Flow qFlow := qrFlow diff --git a/internal/httpclient/client/public/initialize_self_service_settings_flow_parameters.go b/internal/httpclient/client/public/initialize_self_service_settings_flow_parameters.go deleted file mode 100644 index 0036a3b9c58..00000000000 --- a/internal/httpclient/client/public/initialize_self_service_settings_flow_parameters.go +++ /dev/null @@ -1,112 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package public - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "context" - "net/http" - "time" - - "github.com/go-openapi/errors" - "github.com/go-openapi/runtime" - cr "github.com/go-openapi/runtime/client" - "github.com/go-openapi/strfmt" -) - -// NewInitializeSelfServiceSettingsFlowParams creates a new InitializeSelfServiceSettingsFlowParams object -// with the default values initialized. -func NewInitializeSelfServiceSettingsFlowParams() *InitializeSelfServiceSettingsFlowParams { - - return &InitializeSelfServiceSettingsFlowParams{ - - timeout: cr.DefaultTimeout, - } -} - -// NewInitializeSelfServiceSettingsFlowParamsWithTimeout creates a new InitializeSelfServiceSettingsFlowParams object -// with the default values initialized, and the ability to set a timeout on a request -func NewInitializeSelfServiceSettingsFlowParamsWithTimeout(timeout time.Duration) *InitializeSelfServiceSettingsFlowParams { - - return &InitializeSelfServiceSettingsFlowParams{ - - timeout: timeout, - } -} - -// NewInitializeSelfServiceSettingsFlowParamsWithContext creates a new InitializeSelfServiceSettingsFlowParams object -// with the default values initialized, and the ability to set a context for a request -func NewInitializeSelfServiceSettingsFlowParamsWithContext(ctx context.Context) *InitializeSelfServiceSettingsFlowParams { - - return &InitializeSelfServiceSettingsFlowParams{ - - Context: ctx, - } -} - -// NewInitializeSelfServiceSettingsFlowParamsWithHTTPClient creates a new InitializeSelfServiceSettingsFlowParams object -// with the default values initialized, and the ability to set a custom HTTPClient for a request -func NewInitializeSelfServiceSettingsFlowParamsWithHTTPClient(client *http.Client) *InitializeSelfServiceSettingsFlowParams { - - return &InitializeSelfServiceSettingsFlowParams{ - HTTPClient: client, - } -} - -/*InitializeSelfServiceSettingsFlowParams contains all the parameters to send to the API endpoint -for the initialize self service settings flow operation typically these are written to a http.Request -*/ -type InitializeSelfServiceSettingsFlowParams struct { - timeout time.Duration - Context context.Context - HTTPClient *http.Client -} - -// WithTimeout adds the timeout to the initialize self service settings flow params -func (o *InitializeSelfServiceSettingsFlowParams) WithTimeout(timeout time.Duration) *InitializeSelfServiceSettingsFlowParams { - o.SetTimeout(timeout) - return o -} - -// SetTimeout adds the timeout to the initialize self service settings flow params -func (o *InitializeSelfServiceSettingsFlowParams) SetTimeout(timeout time.Duration) { - o.timeout = timeout -} - -// WithContext adds the context to the initialize self service settings flow params -func (o *InitializeSelfServiceSettingsFlowParams) WithContext(ctx context.Context) *InitializeSelfServiceSettingsFlowParams { - o.SetContext(ctx) - return o -} - -// SetContext adds the context to the initialize self service settings flow params -func (o *InitializeSelfServiceSettingsFlowParams) SetContext(ctx context.Context) { - o.Context = ctx -} - -// WithHTTPClient adds the HTTPClient to the initialize self service settings flow params -func (o *InitializeSelfServiceSettingsFlowParams) WithHTTPClient(client *http.Client) *InitializeSelfServiceSettingsFlowParams { - o.SetHTTPClient(client) - return o -} - -// SetHTTPClient adds the HTTPClient to the initialize self service settings flow params -func (o *InitializeSelfServiceSettingsFlowParams) SetHTTPClient(client *http.Client) { - o.HTTPClient = client -} - -// WriteToRequest writes these params to a swagger request -func (o *InitializeSelfServiceSettingsFlowParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { - - if err := r.SetTimeout(o.timeout); err != nil { - return err - } - var res []error - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} diff --git a/internal/httpclient/client/public/initialize_self_service_settings_flow_responses.go b/internal/httpclient/client/public/initialize_self_service_settings_flow_responses.go deleted file mode 100644 index bb6bc9a14d7..00000000000 --- a/internal/httpclient/client/public/initialize_self_service_settings_flow_responses.go +++ /dev/null @@ -1,97 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package public - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "fmt" - "io" - - "github.com/go-openapi/runtime" - "github.com/go-openapi/strfmt" - - "github.com/ory/kratos/internal/httpclient/models" -) - -// InitializeSelfServiceSettingsFlowReader is a Reader for the InitializeSelfServiceSettingsFlow structure. -type InitializeSelfServiceSettingsFlowReader struct { - formats strfmt.Registry -} - -// ReadResponse reads a server response into the received o. -func (o *InitializeSelfServiceSettingsFlowReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { - switch response.Code() { - case 302: - result := NewInitializeSelfServiceSettingsFlowFound() - if err := result.readResponse(response, consumer, o.formats); err != nil { - return nil, err - } - return nil, result - case 500: - result := NewInitializeSelfServiceSettingsFlowInternalServerError() - if err := result.readResponse(response, consumer, o.formats); err != nil { - return nil, err - } - return nil, result - - default: - return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) - } -} - -// NewInitializeSelfServiceSettingsFlowFound creates a InitializeSelfServiceSettingsFlowFound with default headers values -func NewInitializeSelfServiceSettingsFlowFound() *InitializeSelfServiceSettingsFlowFound { - return &InitializeSelfServiceSettingsFlowFound{} -} - -/*InitializeSelfServiceSettingsFlowFound handles this case with default header values. - -Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is -typically 201. -*/ -type InitializeSelfServiceSettingsFlowFound struct { -} - -func (o *InitializeSelfServiceSettingsFlowFound) Error() string { - return fmt.Sprintf("[GET /self-service/browser/flows/settings][%d] initializeSelfServiceSettingsFlowFound ", 302) -} - -func (o *InitializeSelfServiceSettingsFlowFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - - return nil -} - -// NewInitializeSelfServiceSettingsFlowInternalServerError creates a InitializeSelfServiceSettingsFlowInternalServerError with default headers values -func NewInitializeSelfServiceSettingsFlowInternalServerError() *InitializeSelfServiceSettingsFlowInternalServerError { - return &InitializeSelfServiceSettingsFlowInternalServerError{} -} - -/*InitializeSelfServiceSettingsFlowInternalServerError handles this case with default header values. - -genericError -*/ -type InitializeSelfServiceSettingsFlowInternalServerError struct { - Payload *models.GenericError -} - -func (o *InitializeSelfServiceSettingsFlowInternalServerError) Error() string { - return fmt.Sprintf("[GET /self-service/browser/flows/settings][%d] initializeSelfServiceSettingsFlowInternalServerError %+v", 500, o.Payload) -} - -func (o *InitializeSelfServiceSettingsFlowInternalServerError) GetPayload() *models.GenericError { - return o.Payload -} - -func (o *InitializeSelfServiceSettingsFlowInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - - o.Payload = new(models.GenericError) - - // response payload - if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { - return err - } - - return nil -} diff --git a/internal/httpclient/client/public/public_client.go b/internal/httpclient/client/public/public_client.go index edf572db9d9..a99a715e2b8 100644 --- a/internal/httpclient/client/public/public_client.go +++ b/internal/httpclient/client/public/public_client.go @@ -33,7 +33,7 @@ type ClientService interface { CompleteSelfServiceBrowserSettingsPasswordStrategyFlow(params *CompleteSelfServiceBrowserSettingsPasswordStrategyFlowParams) error - CompleteSelfServiceBrowserSettingsProfileStrategyFlow(params *CompleteSelfServiceBrowserSettingsProfileStrategyFlowParams) error + CompleteSelfServiceBrowserSettingsProfileStrategyFlow(params *CompleteSelfServiceBrowserSettingsProfileStrategyFlowParams) (*CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK, error) CompleteSelfServiceBrowserVerificationFlow(params *CompleteSelfServiceBrowserVerificationFlowParams) error @@ -51,7 +51,7 @@ type ClientService interface { InitializeSelfServiceRegistrationViaBrowserFlow(params *InitializeSelfServiceRegistrationViaBrowserFlowParams) error - InitializeSelfServiceSettingsFlow(params *InitializeSelfServiceSettingsFlowParams) error + InitializeSelfServiceSettingsViaBrowserFlow(params *InitializeSelfServiceSettingsViaBrowserFlowParams) error RevokeSession(params *RevokeSessionParams) (*RevokeSessionNoContent, error) @@ -174,13 +174,13 @@ the browser redirected to `url.settings_ui` for further steps. More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). */ -func (a *Client) CompleteSelfServiceBrowserSettingsProfileStrategyFlow(params *CompleteSelfServiceBrowserSettingsProfileStrategyFlowParams) error { +func (a *Client) CompleteSelfServiceBrowserSettingsProfileStrategyFlow(params *CompleteSelfServiceBrowserSettingsProfileStrategyFlowParams) (*CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK, error) { // TODO: Validate the params before sending if params == nil { params = NewCompleteSelfServiceBrowserSettingsProfileStrategyFlowParams() } - _, err := a.transport.Submit(&runtime.ClientOperation{ + result, err := a.transport.Submit(&runtime.ClientOperation{ ID: "completeSelfServiceBrowserSettingsProfileStrategyFlow", Method: "POST", PathPattern: "/self-service/browser/flows/settings/strategies/profile", @@ -193,9 +193,16 @@ func (a *Client) CompleteSelfServiceBrowserSettingsProfileStrategyFlow(params *C Client: params.HTTPClient, }) if err != nil { - return err + return nil, err } - return nil + success, ok := result.(*CompleteSelfServiceBrowserSettingsProfileStrategyFlowOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for completeSelfServiceBrowserSettingsProfileStrategyFlow: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) } /* @@ -412,12 +419,8 @@ func (a *Client) InitializeSelfServiceBrowserVerificationFlow(params *Initialize exists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter `?refresh=true` was set. -:::note - This endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...). -::: - More information can be found at [ORY Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration). */ func (a *Client) InitializeSelfServiceLoginViaBrowserFlow(params *InitializeSelfServiceLoginViaBrowserFlowParams) error { @@ -521,32 +524,35 @@ func (a *Client) InitializeSelfServiceRegistrationViaBrowserFlow(params *Initial } /* - InitializeSelfServiceSettingsFlow initializes browser based settings flow + InitializeSelfServiceSettingsViaBrowserFlow initializes settings flow for browsers - This endpoint initializes a browser-based settings flow. Once initialized, the browser will be redirected to -`selfservice.flows.settings.ui_url` with the request ID set as a query parameter. If no valid user session exists, a login -flow will be initialized. + This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to +`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid +ORY Kratos Session Cookie is included in the request, a login flow will be initialized. -> This endpoint is NOT INTENDED for API clients and only works -with browsers (Chrome, Firefox, ...). +:::note + +This endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...). + +::: More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). */ -func (a *Client) InitializeSelfServiceSettingsFlow(params *InitializeSelfServiceSettingsFlowParams) error { +func (a *Client) InitializeSelfServiceSettingsViaBrowserFlow(params *InitializeSelfServiceSettingsViaBrowserFlowParams) error { // TODO: Validate the params before sending if params == nil { - params = NewInitializeSelfServiceSettingsFlowParams() + params = NewInitializeSelfServiceSettingsViaBrowserFlowParams() } _, err := a.transport.Submit(&runtime.ClientOperation{ - ID: "initializeSelfServiceSettingsFlow", + ID: "initializeSelfServiceSettingsViaBrowserFlow", Method: "GET", - PathPattern: "/self-service/browser/flows/settings", + PathPattern: "/self-service/settings/browser/flows", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json", "application/x-www-form-urlencoded"}, Schemes: []string{"http", "https"}, Params: params, - Reader: &InitializeSelfServiceSettingsFlowReader{formats: a.formats}, + Reader: &InitializeSelfServiceSettingsViaBrowserFlowReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, }) diff --git a/internal/httpclient/models/settings_request.go b/internal/httpclient/models/settings_request.go deleted file mode 100644 index d6a613ef21a..00000000000 --- a/internal/httpclient/models/settings_request.go +++ /dev/null @@ -1,259 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "github.com/go-openapi/errors" - "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" - "github.com/go-openapi/validate" -) - -// SettingsRequest Request presents a settings request -// -// This request is used when an identity wants to update settings -// (e.g. profile data, passwords, ...) in a selfservice manner. -// -// We recommend reading the [User Settings Documentation](../self-service/flows/user-settings) -// -// swagger:model settingsRequest -type SettingsRequest struct { - - // Active, if set, contains the registration method that is being used. It is initially - // not set. - Active string `json:"active,omitempty"` - - // ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting, - // a new request has to be initiated. - // Required: true - // Format: date-time - ExpiresAt *strfmt.DateTime `json:"expires_at"` - - // id - // Required: true - // Format: uuid4 - ID UUID `json:"id"` - - // identity - // Required: true - Identity *Identity `json:"identity"` - - // IssuedAt is the time (UTC) when the request occurred. - // Required: true - // Format: date-time - IssuedAt *strfmt.DateTime `json:"issued_at"` - - // messages - Messages Messages `json:"messages,omitempty"` - - // Methods contains context for all enabled registration methods. If a registration request has been - // processed, but for example the password is incorrect, this will contain error messages. - // Required: true - Methods map[string]SettingsRequestMethod `json:"methods"` - - // RequestURL is the initial URL that was requested from ORY Kratos. It can be used - // to forward information contained in the URL's path or query for example. - // Required: true - RequestURL *string `json:"request_url"` - - // state - // Required: true - State State `json:"state"` - - // type - Type Type `json:"type,omitempty"` -} - -// Validate validates this settings request -func (m *SettingsRequest) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateExpiresAt(formats); err != nil { - res = append(res, err) - } - - if err := m.validateID(formats); err != nil { - res = append(res, err) - } - - if err := m.validateIdentity(formats); err != nil { - res = append(res, err) - } - - if err := m.validateIssuedAt(formats); err != nil { - res = append(res, err) - } - - if err := m.validateMessages(formats); err != nil { - res = append(res, err) - } - - if err := m.validateMethods(formats); err != nil { - res = append(res, err) - } - - if err := m.validateRequestURL(formats); err != nil { - res = append(res, err) - } - - if err := m.validateState(formats); err != nil { - res = append(res, err) - } - - if err := m.validateType(formats); err != nil { - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *SettingsRequest) validateExpiresAt(formats strfmt.Registry) error { - - if err := validate.Required("expires_at", "body", m.ExpiresAt); err != nil { - return err - } - - if err := validate.FormatOf("expires_at", "body", "date-time", m.ExpiresAt.String(), formats); err != nil { - return err - } - - return nil -} - -func (m *SettingsRequest) validateID(formats strfmt.Registry) error { - - if err := m.ID.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("id") - } - return err - } - - return nil -} - -func (m *SettingsRequest) validateIdentity(formats strfmt.Registry) error { - - if err := validate.Required("identity", "body", m.Identity); err != nil { - return err - } - - if m.Identity != nil { - if err := m.Identity.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("identity") - } - return err - } - } - - return nil -} - -func (m *SettingsRequest) validateIssuedAt(formats strfmt.Registry) error { - - if err := validate.Required("issued_at", "body", m.IssuedAt); err != nil { - return err - } - - if err := validate.FormatOf("issued_at", "body", "date-time", m.IssuedAt.String(), formats); err != nil { - return err - } - - return nil -} - -func (m *SettingsRequest) validateMessages(formats strfmt.Registry) error { - - if swag.IsZero(m.Messages) { // not required - return nil - } - - if err := m.Messages.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("messages") - } - return err - } - - return nil -} - -func (m *SettingsRequest) validateMethods(formats strfmt.Registry) error { - - for k := range m.Methods { - - if err := validate.Required("methods"+"."+k, "body", m.Methods[k]); err != nil { - return err - } - if val, ok := m.Methods[k]; ok { - if err := val.Validate(formats); err != nil { - return err - } - } - - } - - return nil -} - -func (m *SettingsRequest) validateRequestURL(formats strfmt.Registry) error { - - if err := validate.Required("request_url", "body", m.RequestURL); err != nil { - return err - } - - return nil -} - -func (m *SettingsRequest) validateState(formats strfmt.Registry) error { - - if err := m.State.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("state") - } - return err - } - - return nil -} - -func (m *SettingsRequest) validateType(formats strfmt.Registry) error { - - if swag.IsZero(m.Type) { // not required - return nil - } - - if err := m.Type.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("type") - } - return err - } - - return nil -} - -// MarshalBinary interface implementation -func (m *SettingsRequest) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *SettingsRequest) UnmarshalBinary(b []byte) error { - var res SettingsRequest - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/internal/httpclient/models/settings_request_method.go b/internal/httpclient/models/settings_request_method.go deleted file mode 100644 index 996d3e98546..00000000000 --- a/internal/httpclient/models/settings_request_method.go +++ /dev/null @@ -1,74 +0,0 @@ -// Code generated by go-swagger; DO NOT EDIT. - -package models - -// This file was generated by the swagger tool. -// Editing this file might prove futile when you re-run the swagger generate command - -import ( - "github.com/go-openapi/errors" - "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" -) - -// SettingsRequestMethod settings request method -// -// swagger:model settingsRequestMethod -type SettingsRequestMethod struct { - - // config - Config *RequestMethodConfig `json:"config,omitempty"` - - // Method contains the request credentials type. - Method string `json:"method,omitempty"` -} - -// Validate validates this settings request method -func (m *SettingsRequestMethod) Validate(formats strfmt.Registry) error { - var res []error - - if err := m.validateConfig(formats); err != nil { - res = append(res, err) - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil -} - -func (m *SettingsRequestMethod) validateConfig(formats strfmt.Registry) error { - - if swag.IsZero(m.Config) { // not required - return nil - } - - if m.Config != nil { - if err := m.Config.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("config") - } - return err - } - } - - return nil -} - -// MarshalBinary interface implementation -func (m *SettingsRequestMethod) MarshalBinary() ([]byte, error) { - if m == nil { - return nil, nil - } - return swag.WriteJSON(m) -} - -// UnmarshalBinary interface implementation -func (m *SettingsRequestMethod) UnmarshalBinary(b []byte) error { - var res SettingsRequestMethod - if err := swag.ReadJSON(b, &res); err != nil { - return err - } - *m = res - return nil -} diff --git a/internal/testhelpers/login.go b/internal/testhelpers/login.go index 8e30bf5cb71..fc26c18c873 100644 --- a/internal/testhelpers/login.go +++ b/internal/testhelpers/login.go @@ -35,3 +35,14 @@ func NewRegistrationUIFlowEchoServer(t *testing.T, reg driver.Registry) *httptes t.Cleanup(ts.Close) return ts } + +func NewSettingsUIFlowEchoServer(t *testing.T, reg driver.Registry) *httptest.Server { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + e, err := reg.SettingsFlowPersister().GetSettingsFlow(r.Context(), x.ParseUUID(r.URL.Query().Get("flow"))) + require.NoError(t, err) + reg.Writer().Write(w, r, e) + })) + viper.Set(configuration.ViperKeySelfServiceSettingsURL, ts.URL+"/settings-ts") + t.Cleanup(ts.Close) + return ts +} diff --git a/internal/testhelpers/selfservice.go b/internal/testhelpers/selfservice.go index 0e3d7b36eec..ca1b0a0ce4d 100644 --- a/internal/testhelpers/selfservice.go +++ b/internal/testhelpers/selfservice.go @@ -178,6 +178,7 @@ func selfServiceMakeHookRequest(t *testing.T, ts *httptest.Server, suffix string } req, err := http.NewRequest("GET", ts.URL+suffix, nil) require.NoError(t, err) + req.Header.Set("Accept", "text/html") if asAPI { req.Header.Set("Accept", "application/json") } diff --git a/internal/testhelpers/selfservice_settings.go b/internal/testhelpers/selfservice_settings.go index c77de6a1a02..cf22f998636 100644 --- a/internal/testhelpers/selfservice_settings.go +++ b/internal/testhelpers/selfservice_settings.go @@ -30,16 +30,16 @@ import ( "github.com/ory/kratos/x" ) -func GetSettingsRequest(t *testing.T, primaryUser *http.Client, ts *httptest.Server) *common.GetSelfServiceBrowserSettingsRequestOK { +func GetSettingsFlow(t *testing.T, primaryUser *http.Client, ts *httptest.Server) *common.GetSelfServiceSettingsFlowOK { publicClient := NewSDKClient(ts) - res, err := primaryUser.Get(ts.URL + settings.PublicPath) + res, err := primaryUser.Get(ts.URL + settings.RouteInitBrowserFlow) require.NoError(t, err) require.NoError(t, res.Body.Close()) - rs, err := publicClient.Common.GetSelfServiceBrowserSettingsRequest( - common.NewGetSelfServiceBrowserSettingsRequestParams().WithHTTPClient(primaryUser). - WithRequest(res.Request.URL.Query().Get("request")), + rs, err := publicClient.Common.GetSelfServiceSettingsFlow( + common.NewGetSelfServiceSettingsFlowParams().WithHTTPClient(primaryUser). + WithID(res.Request.URL.Query().Get("flow")), ) require.NoError(t, err) assert.Empty(t, rs.Payload.Active) @@ -47,8 +47,8 @@ func GetSettingsRequest(t *testing.T, primaryUser *http.Client, ts *httptest.Ser return rs } -func GetSettingsMethodConfig(t *testing.T, primaryUser *http.Client, ts *httptest.Server, id string) *models.RequestMethodConfig { - rs := GetSettingsRequest(t, primaryUser, ts) +func GetSettingsMethodConfig(t *testing.T, primaryUser *http.Client, ts *httptest.Server, id string) *models.FlowMethodConfig { + rs := GetSettingsFlow(t, primaryUser, ts) require.NotEmpty(t, rs.Payload.Methods[id]) require.NotEmpty(t, rs.Payload.Methods[id].Config) @@ -152,7 +152,7 @@ func SettingsSubmitForm( f *models.RequestMethodConfig, hc *http.Client, values url.Values, -) (string, *common.GetSelfServiceBrowserSettingsRequestOK) { +) (string, *common.GetSelfServiceSettingsFlowOK) { require.NotEmpty(t, f.Action) res, err := hc.PostForm(pointerx.StringR(f.Action), values) @@ -165,9 +165,9 @@ func SettingsSubmitForm( assert.Equal(t, viper.GetString(configuration.ViperKeySelfServiceSettingsURL), res.Request.URL.Scheme+"://"+res.Request.URL.Host+res.Request.URL.Path, "should end up at the settings URL, used: %s", pointerx.StringR(f.Action)) - rs, err := NewSDKClientFromURL(viper.GetString(configuration.ViperKeyPublicBaseURL)).Common.GetSelfServiceBrowserSettingsRequest( - common.NewGetSelfServiceBrowserSettingsRequestParams().WithHTTPClient(hc). - WithRequest(res.Request.URL.Query().Get("request")), + rs, err := NewSDKClientFromURL(viper.GetString(configuration.ViperKeyPublicBaseURL)).Common.GetSelfServiceSettingsFlow( + common.NewGetSelfServiceSettingsFlowParams().WithHTTPClient(hc). + WithID(res.Request.URL.Query().Get("id")), ) require.NoError(t, err) body, err := json.Marshal(rs.Payload) diff --git a/internal/testhelpers/session.go b/internal/testhelpers/session.go index 60a66c1dee4..08f0bfc3566 100644 --- a/internal/testhelpers/session.go +++ b/internal/testhelpers/session.go @@ -1,12 +1,88 @@ package testhelpers import ( + "context" "net/http" "testing" + "time" + + "github.com/gobuffalo/httptest" + "github.com/stretchr/testify/require" + + "github.com/ory/kratos/driver" + "github.com/ory/kratos/identity" + "github.com/ory/kratos/session" + "github.com/ory/kratos/x" ) +type SessionLifespanProvider struct { + e time.Duration +} + +func (p *SessionLifespanProvider) SessionLifespan() time.Duration { + return p.e +} + +func NewSessionLifespanProvider(expiresIn time.Duration) *SessionLifespanProvider { + return &SessionLifespanProvider{e: expiresIn} +} + func NewSessionClient(t *testing.T, u string) *http.Client { c := NewClientWithCookies(t) MockHydrateCookieClient(t, c, u) return c } + +func maybePersistSession(t *testing.T, reg *driver.RegistryDefault, sess *session.Session) { + id, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), sess.Identity.ID) + if err != nil { + require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), sess.Identity)) + id, err = reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), sess.Identity.ID) + require.NoError(t, err) + } + sess.Identity = id + sess.IdentityID = id.ID + + require.NoError(t, err, reg.SessionPersister().CreateSession(context.Background(), sess)) +} + +func NewHTTPClientWithSessionCookie(t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client { + maybePersistSession(t, reg, sess) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.NoError(t, reg.SessionManager().IssueCookie(context.Background(), w, r, sess)) + })) + defer ts.Close() + + c := NewClientWithCookies(t) + + // This should work for other test servers as well because cookies ignore ports. + MockHydrateCookieClient(t, c, ts.URL) + return c +} + +func NewHTTPClientWithSessionToken(t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client { + maybePersistSession(t, reg, sess) + + return &http.Client{ + Transport: x.NewTransportWithHeader(http.Header{ + "Authorization": {"Bearer " + sess.Token}, + }), + } +} + +func NewHTTPClientWithArbitrarySessionToken(t *testing.T, reg *driver.RegistryDefault) *http.Client { + return NewHTTPClientWithSessionToken(t, reg, session.NewActiveSession( + &identity.Identity{ID: x.NewUUID()}, + NewSessionLifespanProvider(time.Hour), + time.Now(), + )) +} + +func NewHTTPClientWithArbitrarySessionCookie(t *testing.T, reg *driver.RegistryDefault) *http.Client { + return NewHTTPClientWithSessionCookie(t, reg, session.NewActiveSession( + &identity.Identity{ID: x.NewUUID()}, + NewSessionLifespanProvider(time.Hour), + time.Now(), + )) +} diff --git a/internal/testhelpers/sql.go b/internal/testhelpers/sql.go index fbc27c3a960..fc53b00dca2 100644 --- a/internal/testhelpers/sql.go +++ b/internal/testhelpers/sql.go @@ -27,8 +27,8 @@ func CleanSQL(t *testing.T, c *pop.Connection) { new(login.Flow).TableName(), new(registration.FlowMethods).TableName(), new(registration.Flow).TableName(), - new(settings.RequestMethods).TableName(), - new(settings.Request).TableName(), + new(settings.FlowMethods).TableName(), + new(settings.Flow).TableName(), new(recoverytoken.Token).TableName(), new(recovery.RequestMethods).TableName(), new(recovery.Request).TableName(), diff --git a/persistence/sql/migratest/migration_test.go b/persistence/sql/migratest/migration_test.go index 4b86b5c66b3..f41f7b99445 100644 --- a/persistence/sql/migratest/migration_test.go +++ b/persistence/sql/migratest/migration_test.go @@ -145,11 +145,11 @@ func TestMigrations(t *testing.T) { } }) t.Run("case=settings_request", func(t *testing.T) { - var ids []settings.Request + var ids []settings.Flow require.NoError(t, c.Select("id").All(&ids)) for _, id := range ids { - actual, err := d.Registry().SettingsRequestPersister().GetSettingsRequest(context.Background(), id.ID) + actual, err := d.Registry().SettingsFlowPersister().GetSettingsFlow(context.Background(), id.ID) require.NoError(t, err) compareWithFixture(t, actual, "settings_request", id.ID.String()) } @@ -168,12 +168,12 @@ func TestMigrations(t *testing.T) { }) t.Run("suite=constraints", func(t *testing.T) { - sr, err := d.Registry().SettingsRequestPersister().GetSettingsRequest(context.Background(), x.ParseUUID("a79bfcf1-68ae-49de-8b23-4f96921b8341")) + sr, err := d.Registry().SettingsFlowPersister().GetSettingsFlow(context.Background(), x.ParseUUID("a79bfcf1-68ae-49de-8b23-4f96921b8341")) require.NoError(t, err) require.NoError(t, d.Registry().PrivilegedIdentityPool().DeleteIdentity(context.Background(), sr.IdentityID)) - _, err = d.Registry().SettingsRequestPersister().GetSettingsRequest(context.Background(), x.ParseUUID("a79bfcf1-68ae-49de-8b23-4f96921b8341")) + _, err = d.Registry().SettingsFlowPersister().GetSettingsFlow(context.Background(), x.ParseUUID("a79bfcf1-68ae-49de-8b23-4f96921b8341")) require.Error(t, err) require.True(t, errors.Is(err, sqlcon.ErrNoRows)) }) diff --git a/persistence/sql/persister_settings.go b/persistence/sql/persister_settings.go index ad70d33b3d9..0a1d20fb491 100644 --- a/persistence/sql/persister_settings.go +++ b/persistence/sql/persister_settings.go @@ -5,6 +5,7 @@ import ( "github.com/gobuffalo/pop/v5" "github.com/gofrs/uuid" + "github.com/ory/x/sqlxx" "github.com/ory/x/sqlcon" diff --git a/selfservice/flow/settings/error.go b/selfservice/flow/settings/error.go index dafa35ef873..9d6c1bd897d 100644 --- a/selfservice/flow/settings/error.go +++ b/selfservice/flow/settings/error.go @@ -3,24 +3,23 @@ package settings import ( "net/http" "net/url" + "time" "github.com/pkg/errors" - "github.com/ory/x/sqlxx" - "github.com/ory/herodot" "github.com/ory/x/urlx" "github.com/ory/kratos/driver/configuration" + "github.com/ory/kratos/identity" "github.com/ory/kratos/selfservice/errorx" + "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" + "github.com/ory/kratos/text" "github.com/ory/kratos/x" ) var ( - ErrRequestExpired = herodot.ErrBadRequest. - WithError("settings request expired"). - WithReasonf(`The settings request has expired. Please restart the flow.`) ErrHookAbortRequest = errors.New("aborted settings hook execution") ErrRequestNeedsReAuthentication = herodot.ErrForbidden.WithReasonf("The login session is too old and thus not allowed to update these fields. Please re-authenticate.") ) @@ -31,17 +30,34 @@ type ( x.WriterProvider x.LoggingProvider - RequestPersistenceProvider + HandlerProvider + FlowPersistenceProvider } - ErrorHandlerProvider interface{ SettingsRequestErrorHandler() *ErrorHandler } + ErrorHandlerProvider interface{ SettingsFlowErrorHandler() *ErrorHandler } ErrorHandler struct { d errorHandlerDependencies c configuration.Provider } + + FlowExpiredError struct { + *herodot.DefaultError + ago time.Duration + } ) +func NewFlowExpiredError(at time.Time) *FlowExpiredError { + ago := time.Since(at) + return &FlowExpiredError{ + ago: ago, + DefaultError: herodot.ErrBadRequest. + WithError("settings flow expired"). + WithReasonf(`The settings flow has expired. Please restart the flow.`). + WithReasonf("The settings flow expired %.2f minutes ago, please try again.", ago.Minutes()), + } +} + func NewErrorHandler(d errorHandlerDependencies, c configuration.Provider) *ErrorHandler { return &ErrorHandler{ d: d, @@ -52,67 +68,112 @@ func NewErrorHandler(d errorHandlerDependencies, c configuration.Provider) *Erro func (s *ErrorHandler) reauthenticate( w http.ResponseWriter, r *http.Request, - rr *Request) { - if err := s.d.SettingsRequestPersister().UpdateSettingsRequest(r.Context(), rr); err != nil { - s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + f *Flow, + err error, +) { + if f.Type == flow.TypeAPI { + s.d.Writer().WriteError(w, r, err) return } returnTo := urlx.CopyWithQuery(urlx.AppendPaths(s.c.SelfPublicURL(), r.URL.Path), r.URL.Query()) - s.c.SelfPublicURL() - u := urlx.AppendPaths( - urlx.CopyWithQuery(s.c.SelfPublicURL(), url.Values{ + http.Redirect(w, r, urlx.AppendPaths(urlx.CopyWithQuery(s.c.SelfPublicURL(), + url.Values{ "refresh": {"true"}, "return_to": {returnTo.String()}, - }), login.RouteInitBrowserFlow) - - http.Redirect(w, r, u.String(), http.StatusFound) + }), login.RouteInitBrowserFlow).String(), http.StatusFound) } -func (s *ErrorHandler) HandleSettingsError( +func (s *ErrorHandler) WriteFlowError( w http.ResponseWriter, r *http.Request, - rr *Request, - err error, method string, + f *Flow, + id *identity.Identity, + err error, ) { s.d.Audit(). WithError(err). WithRequest(r). - WithField("settings_request", rr). + WithField("settings_flow", f). Info("Encountered self-service settings error.") - if rr == nil { - s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + if f == nil { + s.forward(w, r, f, err) return - } else if x.IsJSONRequest(r) { - s.d.Writer().WriteError(w, r, err) + } + + if e := new(FlowExpiredError); errors.As(err, &e) { + // create new flow because the old one is not valid + a, err := s.d.SettingsHandler().NewFlow(w, r, id, f.Type) + if err != nil { + // failed to create a new session and redirect to it, handle that error as a new one + s.WriteFlowError(w, r, method, f, id, err) + return + } + + a.Messages.Add(text.NewErrorValidationSettingsFlowExpired(e.ago)) + if err := s.d.SettingsFlowPersister().UpdateSettingsFlow(r.Context(), a); err != nil { + s.forward(w, r, a, err) + return + } + + if f.Type == flow.TypeAPI { + http.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(s.c.SelfPublicURL(), + RouteGetFlow), url.Values{"id": {a.ID.String()}}).String(), http.StatusFound) + } else { + http.Redirect(w, r, a.AppendTo(s.c.SelfServiceFlowSettingsUI()).String(), http.StatusFound) + } return } if errors.Is(err, ErrRequestNeedsReAuthentication) { - s.reauthenticate(w, r, rr) + s.reauthenticate(w, r, f, err) return } - if _, ok := rr.Methods[method]; !ok { - s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Expected settings method %s to exist.", method))) + if _, ok := f.Methods[method]; !ok { + s.forward(w, r, f, errors.WithStack(herodot.ErrInternalServerError. + WithErrorf(`Expected settings method "%s" to exist in flow. This is a bug in the code and should be reported on GitHub.`, method))) return } - rr.Active = sqlxx.NullString(method) - if err := rr.Methods[method].Config.ParseError(err); err != nil { - s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + if err := f.Methods[method].Config.ParseError(err); err != nil { + s.forward(w, r, f, err) + return + } + + if err := s.d.SettingsFlowPersister().UpdateSettingsFlowMethod(r.Context(), f.ID, method, f.Methods[method]); err != nil { + s.forward(w, r, f, err) + return + } + + if f.Type == flow.TypeBrowser { + http.Redirect(w, r, f.AppendTo(s.c.SelfServiceFlowSettingsUI()).String(), http.StatusFound) return } - if err := s.d.SettingsRequestPersister().UpdateSettingsRequest(r.Context(), rr); err != nil { + updatedFlow, innerErr := s.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), f.ID) + if innerErr != nil { + s.forward(w, r, updatedFlow, innerErr) + } + + s.d.Writer().WriteCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), updatedFlow) +} + +func (s *ErrorHandler) forward(w http.ResponseWriter, r *http.Request, rr *Flow, err error) { + if rr == nil { + if x.IsJSONRequest(r) { + s.d.Writer().WriteError(w, r, err) + return + } s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) return } - http.Redirect(w, r, - urlx.CopyWithQuery(s.c.SelfServiceFlowSettingsUI(), url.Values{"request": {rr.ID.String()}}).String(), - http.StatusFound, - ) + if rr.Type == flow.TypeAPI { + s.d.Writer().WriteErrorCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), err) + } else { + s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + } } diff --git a/selfservice/flow/settings/flow.go b/selfservice/flow/settings/flow.go index 27edc47de11..0ddad5e7ef0 100644 --- a/selfservice/flow/settings/flow.go +++ b/selfservice/flow/settings/flow.go @@ -101,6 +101,23 @@ type Flow struct { UpdatedAt time.Time `json:"-" faker:"-" db:"updated_at"` } +// The Response for Settings Flows via API +// +// swagger:model settingsViaApiResponse +type APIFlowResponse struct { + // The Flow + // + // required: true + Flow *Flow `json:"flow"` + + // The Identity + // + // The updated identity + // + // required: true + Identity *identity.Identity `json:"identity"` +} + func NewFlow(exp time.Duration, r *http.Request, i *identity.Identity, ft flow.Type) *Flow { now := time.Now().UTC() return &Flow{ diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go index 38fc98fb26d..c1ba8ef843b 100644 --- a/selfservice/flow/settings/handler.go +++ b/selfservice/flow/settings/handler.go @@ -8,11 +8,13 @@ import ( "github.com/justinas/nosurf" "github.com/pkg/errors" - "github.com/ory/kratos/continuity" - "github.com/ory/kratos/schema" + "github.com/ory/x/urlx" "github.com/ory/herodot" - "github.com/ory/x/urlx" + + "github.com/ory/kratos/continuity" + "github.com/ory/kratos/schema" + "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/driver/configuration" "github.com/ory/kratos/identity" @@ -22,8 +24,9 @@ import ( ) const ( - PublicPath = "/self-service/browser/flows/settings" - PublicRequestPath = "/self-service/browser/flows/requests/settings" + RouteInitBrowserFlow = "/self-service/settings/browser" + RouteInitAPIFlow = "/self-service/settings/api" + RouteGetFlow = "/self-service/settings/flows" ContinuityPrefix = "ory_kratos_settings" ) @@ -50,7 +53,7 @@ type ( errorx.ManagementProvider ErrorHandlerProvider - RequestPersistenceProvider + FlowPersistenceProvider StrategyProvider IdentityTraitsSchemas() schema.Schemas @@ -71,87 +74,143 @@ func NewHandler(d handlerDependencies, c configuration.Provider) *Handler { func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) { redirect := session.RedirectOnUnauthenticated(h.c.SelfServiceFlowLoginUI().String()) - public.GET(PublicPath, h.d.SessionHandler().IsAuthenticated(h.initUpdateSettings, redirect)) - public.GET(PublicRequestPath, h.d.SessionHandler().IsAuthenticated(h.publicFetchUpdateSettingsRequest, redirect)) + public.GET(RouteInitBrowserFlow, h.d.SessionHandler().IsAuthenticated(h.initBrowserFlow, redirect)) + public.GET(RouteInitAPIFlow, h.d.SessionHandler().IsAuthenticated(h.initApiFlow, nil)) + + public.GET(RouteGetFlow, h.d.SessionHandler().IsAuthenticated(h.fetchPublicFLow, nil)) } func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) { - admin.GET(PublicRequestPath, h.adminFetchUpdateSettingsRequest) + admin.GET(RouteGetFlow, h.fetchAdminFlow) } -// swagger:route GET /self-service/browser/flows/settings public initializeSelfServiceSettingsFlow +func (h *Handler) NewFlow(w http.ResponseWriter, r *http.Request, i *identity.Identity, ft flow.Type) (*Flow, error) { + f := NewFlow(h.c.SelfServiceFlowSettingsFlowLifespan(), r, i, ft) + for _, strategy := range h.d.SettingsStrategies() { + if err := h.d.ContinuityManager().Abort(r.Context(), w, r, ContinuityKey(strategy.SettingsStrategyID())); err != nil { + return nil, err + } + + if err := strategy.PopulateSettingsMethod(r, i, f); err != nil { + return nil, err + } + } + + if err := h.d.SettingsFlowPersister().CreateSettingsFlow(r.Context(), f); err != nil { + return nil, err + } + + return f, nil +} + +// swagger:route GET /self-service/settings/api common public admin initializeSelfServiceSettingsViaAPIFlow +// +// Initialize Settings Flow for API Clients // -// Initialize browser-based settings flow +// This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on. +// You must provide a valid ORY Kratos Session Token for this endpoint to respond with HTTP 200 OK. // -// This endpoint initializes a browser-based settings flow. Once initialized, the browser will be redirected to -// `selfservice.flows.settings.ui_url` with the request ID set as a query parameter. If no valid user session exists, a login -// flow will be initialized. +// To fetch an existing settings flow call `/self-service/settings/flows?flow=`. // -// > This endpoint is NOT INTENDED for API clients and only works -// with browsers (Chrome, Firefox, ...). +// :::warning +// +// You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server +// Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make +// you vulnerable to a variety of CSRF attacks. +// +// This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...). +// +// ::: // // More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). // // Schemes: http, https // +// Security: +// - sessionToken +// // Responses: -// 302: emptyResponse +// 200: settingsFlow +// 400: genericError // 500: genericError -func (h *Handler) initUpdateSettings(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { +func (h *Handler) initApiFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { s, err := h.d.SessionManager().FetchFromRequest(r.Context(), r) if err != nil { - h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + h.d.Writer().WriteError(w, r, err) return } - req := NewRequest(h.c.SelfServiceFlowSettingsRequestLifespan(), r, s) - - if err := h.CreateRequest(w, r, s, req); err != nil { - h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + f, err := h.NewFlow(w, r, s.Identity, flow.TypeAPI) + if err != nil { + h.d.Writer().WriteError(w, r, err) return } - http.Redirect(w, r, req.URL(h.c.SelfServiceFlowSettingsUI()).String(), http.StatusFound) + h.d.Writer().Write(w, r, f) } -func (h *Handler) CreateRequest(w http.ResponseWriter, r *http.Request, sess *session.Session, req *Request) error { - for _, strategy := range h.d.SettingsStrategies() { - if err := h.d.ContinuityManager().Abort(r.Context(), w, r, ContinuityKey(strategy.SettingsStrategyID())); err != nil { - return err - } - - if err := strategy.PopulateSettingsMethod(r, sess, req); err != nil { - return err - } +// swagger:route GET /self-service/settings/browser/flows public initializeSelfServiceSettingsViaBrowserFlow +// +// Initialize Settings Flow for Browsers +// +// This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to +// `selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid +// ORY Kratos Session Cookie is included in the request, a login flow will be initialized. +// +// :::note +// +// This endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...). +// +// ::: +// +// More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). +// +// Schemes: http, https +// +// Security: +// - sessionToken +// +// Responses: +// 302: emptyResponse +// 500: genericError +func (h *Handler) initBrowserFlow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + s, err := h.d.SessionManager().FetchFromRequest(r.Context(), r) + if err != nil { + h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + return } - if err := h.d.SettingsRequestPersister().CreateSettingsRequest(r.Context(), req); err != nil { - return err + f, err := h.NewFlow(w, r, s.Identity, flow.TypeBrowser) + if err != nil { + h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + return } - return nil + http.Redirect(w, r, f.AppendTo(h.c.SelfServiceFlowSettingsUI()).String(), http.StatusFound) } // nolint:deadcode,unused -// swagger:parameters getSelfServiceBrowserSettingsRequest -type getSelfServiceBrowserSettingsRequestParameters struct { - // Request is the Login Request ID +// swagger:parameters getSelfServiceSettingsFlow +type getSelfServiceSettingsFlow struct { + // ID is the Settings Flow ID // - // The value for this parameter comes from `request` URL Query parameter sent to your - // application (e.g. `/settingss?request=abcde`). + // The value for this parameter comes from `flow` URL Query parameter sent to your + // application (e.g. `/settings?flow=abcde`). // // required: true // in: query - Request string `json:"request"` + ID string `json:"id"` } -// swagger:route GET /self-service/browser/flows/requests/settings common public admin getSelfServiceBrowserSettingsRequest +// swagger:route GET /self-service/settings/flows common public admin getSelfServiceSettingsFlow // -// Get the request context of browser-based settings flows +// Get Information About a Settings Flow // -// When accessing this endpoint through ORY Kratos' Public API, ensure that cookies are set as they are required -// for checking the auth session. To prevent scanning attacks, the public endpoint does not return 404 status codes -// but instead 403 or 500. +// When accessing this endpoint through ORY Kratos' Public API you must ensure that either the ORY Kratos Session Cookie +// or the ORY Kratos Session Token are set. The public endpoint does not return 404 status codes +// but instead 403 or 500 to improve data privacy. +// +// You can access this endpoint without credentials when using ORY Kratos' Admin API. // // More information can be found at [ORY Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings). // @@ -160,21 +219,24 @@ type getSelfServiceBrowserSettingsRequestParameters struct { // // Schemes: http, https // +// Security: +// - sessionToken +// // Responses: -// 200: settingsRequest +// 200: settingsFlow // 403: genericError // 404: genericError // 410: genericError // 500: genericError -func (h *Handler) publicFetchUpdateSettingsRequest(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if err := h.fetchUpdateSettingsRequest(w, r, true); err != nil { +func (h *Handler) fetchPublicFLow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + if err := h.fetchFlow(w, r, true); err != nil { h.d.Writer().WriteError(w, r, err) return } } -func (h *Handler) adminFetchUpdateSettingsRequest(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if err := h.fetchUpdateSettingsRequest(w, r, false); err != nil { +func (h *Handler) fetchAdminFlow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + if err := h.fetchFlow(w, r, false); err != nil { h.d.Writer().WriteError(w, r, err) return } @@ -182,15 +244,17 @@ func (h *Handler) adminFetchUpdateSettingsRequest(w http.ResponseWriter, r *http func (h *Handler) wrapErrorForbidden(err error, shouldWrap bool) error { if shouldWrap { - return herodot.ErrForbidden.WithReasonf("Access privileges are missing, invalid, or not sufficient to access this endpoint.").WithTrace(err).WithDebugf("%s", err) + return herodot.ErrForbidden. + WithReasonf("Access privileges are missing, invalid, or not sufficient to access this endpoint."). + WithTrace(err).WithDebugf("%s", err) } return err } -func (h *Handler) fetchUpdateSettingsRequest(w http.ResponseWriter, r *http.Request, checkSession bool) error { - rid := x.ParseUUID(r.URL.Query().Get("request")) - pr, err := h.d.SettingsRequestPersister().GetSettingsRequest(r.Context(), rid) +func (h *Handler) fetchFlow(w http.ResponseWriter, r *http.Request, checkSession bool) error { + rid := x.ParseUUID(r.URL.Query().Get("id")) + pr, err := h.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), rid) if err != nil { return h.wrapErrorForbidden(err, checkSession) } @@ -207,9 +271,16 @@ func (h *Handler) fetchUpdateSettingsRequest(w http.ResponseWriter, r *http.Requ } if pr.ExpiresAt.Before(time.Now().UTC()) { - return errors.WithStack(x.ErrGone. - WithReason("The settings request has expired. Redirect the user to the login endpoint to initialize a new session."). - WithDetail("redirect_to", urlx.AppendPaths(h.c.SelfPublicURL(), PublicPath).String())) + if pr.Type == flow.TypeBrowser { + 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.c.SelfPublicURL(), RouteInitBrowserFlow).String()))) + return nil + } + h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. + WithReason("The settings flow has expired. Call the settings flow init API endpoint to initialize a new settings flow."). + WithDetail("api", urlx.AppendPaths(h.c.SelfPublicURL(), RouteInitAPIFlow).String()))) + return nil } h.d.Writer().Write(w, r, pr) diff --git a/selfservice/flow/settings/handler_test.go b/selfservice/flow/settings/handler_test.go index b046034018b..1b3f9d2a6b3 100644 --- a/selfservice/flow/settings/handler_test.go +++ b/selfservice/flow/settings/handler_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" "github.com/ory/x/pointerx" @@ -20,9 +21,9 @@ import ( "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/httpclient/client/common" "github.com/ory/kratos/internal/testhelpers" + "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/selfservice/flow/settings" - "github.com/ory/kratos/session" "github.com/ory/kratos/x" ) @@ -47,74 +48,95 @@ func TestHandler(t *testing.T) { primaryUser, otherUser := clients["primary"], clients["secondary"] publicClient, adminClient := testhelpers.NewSDKClient(publicTS), testhelpers.NewSDKClient(adminTS) - newExpiredRequest := func() *settings.Request { - return settings.NewRequest(-time.Minute, + newExpiredFlow := func() *settings.Flow { + return settings.NewFlow(-time.Minute, &http.Request{URL: urlx.ParseOrPanic(publicTS.URL + login.RouteInitBrowserFlow)}, - &session.Session{Identity: primaryIdentity}) + primaryIdentity, flow.TypeBrowser) } t.Run("daemon=admin", func(t *testing.T) { - t.Run("description=fetching a non-existent request should return a 404 error", func(t *testing.T) { - _, err := adminClient.Common.GetSelfServiceBrowserSettingsRequest( - common.NewGetSelfServiceBrowserSettingsRequestParams().WithHTTPClient(otherUser).WithRequest("i-do-not-exist"), + t.Run("description=fetching a non-existent flow should return a 404 error", func(t *testing.T) { + _, err := adminClient.Common.GetSelfServiceSettingsFlow( + common.NewGetSelfServiceSettingsFlowParams().WithHTTPClient(otherUser).WithID("i-do-not-exist"), ) require.Error(t, err) - require.IsType(t, &common.GetSelfServiceBrowserSettingsRequestNotFound{}, err) - assert.Equal(t, int64(http.StatusNotFound), err.(*common.GetSelfServiceBrowserSettingsRequestNotFound).Payload.Error.Code) + require.IsType(t, &common.GetSelfServiceSettingsFlowNotFound{}, err) + assert.Equal(t, int64(http.StatusNotFound), err.(*common.GetSelfServiceSettingsFlowNotFound).Payload.Error.Code) }) - t.Run("description=fetching an expired request returns 410", func(t *testing.T) { - pr := newExpiredRequest() - require.NoError(t, reg.SettingsRequestPersister().CreateSettingsRequest(context.Background(), pr)) + t.Run("description=fetching an expired flow returns 410", func(t *testing.T) { + pr := newExpiredFlow() + require.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(context.Background(), pr)) - _, err := adminClient.Common.GetSelfServiceBrowserSettingsRequest( - common.NewGetSelfServiceBrowserSettingsRequestParams().WithHTTPClient(primaryUser).WithRequest(pr.ID.String()), + _, err := adminClient.Common.GetSelfServiceSettingsFlow( + common.NewGetSelfServiceSettingsFlowParams().WithHTTPClient(primaryUser).WithID(pr.ID.String()), ) require.Error(t, err) - require.IsType(t, &common.GetSelfServiceBrowserSettingsRequestGone{}, err, "%+v", err) - assert.Equal(t, int64(http.StatusGone), err.(*common.GetSelfServiceBrowserSettingsRequestGone).Payload.Error.Code) + require.IsType(t, &common.GetSelfServiceSettingsFlowGone{}, err, "%+v", err) + assert.Equal(t, int64(http.StatusGone), err.(*common.GetSelfServiceSettingsFlowGone).Payload.Error.Code) }) }) t.Run("daemon=public", func(t *testing.T) { - t.Run("description=fetching a non-existent request should return a 403 error", func(t *testing.T) { - _, err := publicClient.Common.GetSelfServiceBrowserSettingsRequest( - common.NewGetSelfServiceBrowserSettingsRequestParams().WithHTTPClient(otherUser).WithRequest("i-do-not-exist"), + t.Run("description=fetching a non-existent flow should return a 403 error", func(t *testing.T) { + _, err := publicClient.Common.GetSelfServiceSettingsFlow( + common.NewGetSelfServiceSettingsFlowParams().WithHTTPClient(otherUser).WithID("i-do-not-exist"), ) require.Error(t, err) - require.IsType(t, &common.GetSelfServiceBrowserSettingsRequestForbidden{}, err) - assert.Equal(t, int64(http.StatusForbidden), err.(*common.GetSelfServiceBrowserSettingsRequestForbidden).Payload.Error.Code) + require.IsType(t, &common.GetSelfServiceSettingsFlowForbidden{}, err) + assert.Equal(t, int64(http.StatusForbidden), err.(*common.GetSelfServiceSettingsFlowForbidden).Payload.Error.Code) }) - t.Run("description=fetching an expired request returns 410", func(t *testing.T) { - pr := newExpiredRequest() - require.NoError(t, reg.SettingsRequestPersister().CreateSettingsRequest(context.Background(), pr)) + t.Run("description=fetching an expired flow returns 410", func(t *testing.T) { + pr := newExpiredFlow() + require.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(context.Background(), pr)) - _, err := publicClient.Common.GetSelfServiceBrowserSettingsRequest( - common.NewGetSelfServiceBrowserSettingsRequestParams().WithHTTPClient(primaryUser).WithRequest(pr.ID.String()), + _, err := publicClient.Common.GetSelfServiceSettingsFlow( + common.NewGetSelfServiceSettingsFlowParams().WithHTTPClient(primaryUser).WithID(pr.ID.String()), ) require.Error(t, err) - require.IsType(t, &common.GetSelfServiceBrowserSettingsRequestGone{}, err) - assert.Equal(t, int64(http.StatusGone), err.(*common.GetSelfServiceBrowserSettingsRequestGone).Payload.Error.Code) + require.IsType(t, &common.GetSelfServiceSettingsFlowGone{}, err) + assert.Equal(t, int64(http.StatusGone), err.(*common.GetSelfServiceSettingsFlowGone).Payload.Error.Code) }) t.Run("description=should fail to fetch request if identity changed", func(t *testing.T) { - res, err := primaryUser.Get(publicTS.URL + settings.PublicPath) - require.NoError(t, err) - - rid := res.Request.URL.Query().Get("request") - require.NotEmpty(t, rid) - - _, err = publicClient.Common.GetSelfServiceBrowserSettingsRequest( - common.NewGetSelfServiceBrowserSettingsRequestParams().WithHTTPClient(otherUser).WithRequest(rid), - ) - require.Error(t, err) - require.IsType(t, &common.GetSelfServiceBrowserSettingsRequestForbidden{}, err) - assert.EqualValues(t, int64(http.StatusForbidden), err.(*common.GetSelfServiceBrowserSettingsRequestForbidden).Payload.Error.Code, "should return a 403 error because the identities from the cookies do not match") + t.Run("type=api", func(t *testing.T) { + user1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(t, reg) + user2 := testhelpers.NewHTTPClientWithArbitrarySessionToken(t, reg) + + res, err := user1.Get(publicTS.URL + settings.RouteInitAPIFlow) + require.NoError(t, err) + defer res.Body.Close() + body := x.MustReadAll(res.Body) + id := gjson.GetBytes(body, "id") + require.NotEmpty(t, id) + + res, err = user2.Get(publicTS.URL + settings.RouteGetFlow) + require.NoError(t, err) + defer res.Body.Close() + require.EqualValues(t, res.StatusCode, http.StatusForbidden) + body = x.MustReadAll(res.Body) + assert.Contains(t, gjson.GetBytes(body, "error.reason").String(), "Access privileges are missing", "%s", body) + }) + + t.Run("type=browser", func(t *testing.T) { + res, err := primaryUser.Get(publicTS.URL + settings.RouteInitBrowserFlow) + require.NoError(t, err) + + rid := res.Request.URL.Query().Get("flow") + require.NotEmpty(t, rid) + + _, err = publicClient.Common.GetSelfServiceSettingsFlow( + common.NewGetSelfServiceSettingsFlowParams().WithHTTPClient(otherUser).WithID(rid), + ) + require.Error(t, err) + require.IsType(t, &common.GetSelfServiceSettingsFlowForbidden{}, err) + assert.EqualValues(t, int64(http.StatusForbidden), err.(*common.GetSelfServiceSettingsFlowForbidden).Payload.Error.Code, "should return a 403 error because the identities from the cookies do not match") + }) }) t.Run("description=should fail to post data if CSRF is missing", func(t *testing.T) { diff --git a/selfservice/flow/settings/hook.go b/selfservice/flow/settings/hook.go index 85b7cb95a38..8674a8a74fe 100644 --- a/selfservice/flow/settings/hook.go +++ b/selfservice/flow/settings/hook.go @@ -3,28 +3,26 @@ package settings import ( "fmt" "net/http" - "net/url" "time" "github.com/pkg/errors" - "github.com/ory/x/urlx" - "github.com/ory/kratos/driver/configuration" "github.com/ory/kratos/identity" + "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/x" ) type ( PostHookPrePersistExecutor interface { - ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Request, s *identity.Identity) error + ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error } - PostHookPrePersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Request, s *identity.Identity) error + PostHookPrePersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error PostHookPostPersistExecutor interface { - ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *Request, s *identity.Identity) error + ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error } - PostHookPostPersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Request, s *identity.Identity) error + PostHookPostPersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error HooksProvider interface { PostSettingsPrePersistHooks(settingsType string) []PostHookPrePersistExecutor @@ -32,11 +30,11 @@ type ( } ) -func (f PostHookPrePersistExecutorFunc) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Request, s *identity.Identity) error { +func (f PostHookPrePersistExecutorFunc) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error { return f(w, r, a, s) } -func (f PostHookPostPersistExecutorFunc) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *Request, s *identity.Identity) error { +func (f PostHookPostPersistExecutorFunc) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *identity.Identity) error { return f(w, r, a, s) } @@ -46,7 +44,7 @@ type ( identity.ValidationProvider HooksProvider x.LoggingProvider - RequestPersistenceProvider + FlowPersistenceProvider x.WriterProvider } HookExecutor struct { @@ -58,6 +56,14 @@ type ( } ) +func PostHookPostPersistExecutorNames(e []PostHookPostPersistExecutor) []string { + names := make([]string, len(e)) + for k, ee := range e { + names[k] = fmt.Sprintf("%T", ee) + } + return names +} + func NewHookExecutor( d executorDependencies, c configuration.Provider, @@ -81,22 +87,40 @@ func WithCallback(cb func(ctxUpdate *UpdateContext) error) func(o *postSettingsH } func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, settingsType string, ctxUpdate *UpdateContext, i *identity.Identity, opts ...PostSettingsHookOption) error { + e.d.Logger(). + WithRequest(r). + WithField("identity_id", i.ID). + WithField("flow_method", settingsType). + Debug("Running PostSettingsPrePersistHooks.") + config := new(postSettingsHookOptions) for _, f := range opts { f(config) } - e.d.Logger(). - WithField("identity_id", i.ID). - Debug("An identity's settings have been updated, running post hooks.") - - for _, executor := range e.d.PostSettingsPrePersistHooks(settingsType) { - if err := executor.ExecuteSettingsPrePersistHook(w, r, ctxUpdate.Request, i); err != nil { + for k, executor := range e.d.PostSettingsPrePersistHooks(settingsType) { + if err := executor.ExecuteSettingsPrePersistHook(w, r, ctxUpdate.Flow, i); err != nil { if errors.Is(err, ErrHookAbortRequest) { + e.d.Logger(). + WithRequest(r). + WithField("executor", fmt.Sprintf("%T", executor)). + WithField("executor_position", k). + WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(settingsType))). + WithField("identity_id", i.ID). + WithField("flow_method", settingsType). + Debug("A ExecuteSettingsPrePersistHook hook aborted early.") return nil } return err } + + e.d.Logger().WithRequest(r). + WithField("executor", fmt.Sprintf("%T", executor)). + WithField("executor_position", k). + WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(settingsType))). + WithField("identity_id", i.ID). + WithField("flow_method", settingsType). + Debug("ExecuteSettingsPrePersistHook completed successfully.") } options := []identity.ManagerOption{identity.ManagerExposeValidationErrorsForInternalTypeAssertion} @@ -107,45 +131,70 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, if err := e.d.IdentityManager().Update(r.Context(), i, options...); err != nil { if errors.Is(err, identity.ErrProtectedFieldModified) { - e.d.Logger().WithField("error", fmt.Sprintf("%+v", err)).Debug("Modifying protected field requires re-authentication.") + e.d.Logger().WithError(err).Debug("Modifying protected field requires re-authentication.") return errors.WithStack(ErrRequestNeedsReAuthentication) } return err } + e.d.Audit(). + WithRequest(r). + WithField("identity_id", i.ID). + Debug("An identity's settings have been updated.") ctxUpdate.Session.Identity = i - ctxUpdate.Request.State = StateSuccess + ctxUpdate.Flow.State = StateSuccess if config.cb != nil { if err := config.cb(ctxUpdate); err != nil { return err } } - if method, ok := ctxUpdate.Request.Methods[settingsType]; ok { + if method, ok := ctxUpdate.Flow.Methods[settingsType]; ok { method.Config.ResetMessages() } - if err := e.d.SettingsRequestPersister().UpdateSettingsRequest(r.Context(), ctxUpdate.Request); err != nil { + if err := e.d.SettingsFlowPersister().UpdateSettingsFlow(r.Context(), ctxUpdate.Flow); err != nil { return err } - for _, executor := range e.d.PostSettingsPostPersistHooks(settingsType) { - if err := executor.ExecuteSettingsPostPersistHook(w, r, ctxUpdate.Request, i); err != nil { + for k, executor := range e.d.PostSettingsPostPersistHooks(settingsType) { + if err := executor.ExecuteSettingsPostPersistHook(w, r, ctxUpdate.Flow, i); err != nil { if errors.Is(err, ErrHookAbortRequest) { + e.d.Logger(). + WithRequest(r). + WithField("executor", fmt.Sprintf("%T", executor)). + WithField("executor_position", k). + WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(settingsType))). + WithField("identity_id", i.ID). + WithField("flow_method", settingsType). + Debug("A ExecuteSettingsPostPersistHook hook aborted early.") return nil } return err } + + e.d.Logger().WithRequest(r). + WithField("executor", fmt.Sprintf("%T", executor)). + WithField("executor_position", k). + WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(settingsType))). + WithField("identity_id", i.ID). + WithField("flow_method", settingsType). + Debug("ExecuteSettingsPostPersistHook completed successfully.") } e.d.Logger(). + WithRequest(r). WithField("identity_id", i.ID). - Debug("Post settings execution hooks completed successfully.") + WithField("flow_method", settingsType). + Debug("Completed all PostSettingsPrePersistHooks and PostSettingsPostPersistHooks.") + + if ctxUpdate.Flow.Type == flow.TypeAPI { + e.d.Writer().Write(w, r, &APIFlowResponse{Flow: ctxUpdate.Flow, Identity: i}) + return nil + } - return x.SecureContentNegotiationRedirection(w, r, ctxUpdate.Session.Declassify(), ctxUpdate.Request.RequestURL, e.d.Writer(), e.c, + return x.SecureContentNegotiationRedirection(w, r, ctxUpdate.Session.Declassify(), ctxUpdate.Flow.RequestURL, e.d.Writer(), e.c, x.SecureRedirectOverrideDefaultReturnTo( e.c.SelfServiceFlowSettingsReturnTo(settingsType, - urlx.CopyWithQuery( - e.c.SelfServiceFlowSettingsUI(), - url.Values{"request": {ctxUpdate.Request.ID.String()}})))) + ctxUpdate.Flow.AppendTo(e.c.SelfServiceFlowSettingsUI())))) } diff --git a/selfservice/flow/settings/hook_test.go b/selfservice/flow/settings/hook_test.go index 65e018b0e3b..d2769d3ae31 100644 --- a/selfservice/flow/settings/hook_test.go +++ b/selfservice/flow/settings/hook_test.go @@ -18,6 +18,7 @@ import ( "github.com/ory/kratos/identity" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" + "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/selfservice/hook" "github.com/ory/kratos/session" @@ -40,18 +41,18 @@ func TestSettingsExecutor(t *testing.T) { }, }) - newServer := func(t *testing.T) *httptest.Server { + newServer := func(t *testing.T, ft flow.Type) *httptest.Server { router := httprouter.New() handleErr := testhelpers.SelfServiceHookSettingsErrorHandler router.GET("/settings/post", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { i := testhelpers.SelfServiceHookCreateFakeIdentity(t, reg) sess := session.NewActiveSession(i, conf, time.Now().UTC()) - a := settings.NewRequest(time.Minute, r, sess) + a := settings.NewFlow(time.Minute, r, sess.Identity, ft) a.RequestURL = x.RequestURL(r).String() - require.NoError(t, reg.SettingsRequestPersister().CreateSettingsRequest(r.Context(), a)) + require.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(r.Context(), a)) _ = handleErr(t, w, r, reg.SettingsHookExecutor(). - PostSettingsHook(w, r, strategy, &settings.UpdateContext{Request: a, Session: sess}, i)) + PostSettingsHook(w, r, strategy, &settings.UpdateContext{Flow: a, Session: sess}, i)) }) ts := httptest.NewServer(router) t.Cleanup(ts.Close) @@ -73,7 +74,7 @@ func TestSettingsExecutor(t *testing.T) { t.Run("case=pass without hooks", func(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset) - res, _ := makeRequestPost(t, newServer(t), false, url.Values{}) + res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.Contains(t, res.Request.URL.String(), uiURL) }) @@ -82,7 +83,7 @@ func TestSettingsExecutor(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset) viperSetPost(strategy, []configuration.SelfServiceHook{{Name: "err", Config: []byte(`{}`)}}) - res, _ := makeRequestPost(t, newServer(t), false, url.Values{}) + res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.Contains(t, res.Request.URL.String(), uiURL) }) @@ -91,7 +92,7 @@ func TestSettingsExecutor(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset) viperSetPost(strategy, []configuration.SelfServiceHook{{Name: "err", Config: []byte(`{"ExecuteSettingsPrePersistHook": "abort"}`)}}) - res, body := makeRequestPost(t, newServer(t), false, url.Values{}) + res, body := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.Equal(t, "", body) }) @@ -99,7 +100,7 @@ func TestSettingsExecutor(t *testing.T) { t.Run("case=prevent return_to value because domain not whitelisted", func(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset) - res, _ := makeRequestPost(t, newServer(t), false, url.Values{"return_to": {"https://www.ory.sh/kratos/"}}) + res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{"return_to": {"https://www.ory.sh/kratos/"}}) assert.EqualValues(t, http.StatusInternalServerError, res.StatusCode) }) @@ -108,7 +109,7 @@ func TestSettingsExecutor(t *testing.T) { viper.Set(configuration.ViperKeyURLsWhitelistedReturnToDomains, []string{"https://www.ory.sh/"}) testhelpers.SelfServiceHookSettingsSetDefaultRedirectTo("https://www.ory.sh") - res, _ := makeRequestPost(t, newServer(t), false, url.Values{"return_to": {"https://www.ory.sh/kratos/"}}) + res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{"return_to": {"https://www.ory.sh/kratos/"}}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.EqualValues(t, "https://www.ory.sh/kratos/", res.Request.URL.String()) }) @@ -117,7 +118,7 @@ func TestSettingsExecutor(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset) testhelpers.SelfServiceHookSettingsSetDefaultRedirectTo("https://www.ory.sh/kratos") - res, _ := makeRequestPost(t, newServer(t), false, url.Values{}) + res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.EqualValues(t, "https://www.ory.sh/kratos/", res.Request.URL.String()) }) @@ -127,7 +128,7 @@ func TestSettingsExecutor(t *testing.T) { testhelpers.SelfServiceHookSettingsSetDefaultRedirectTo("https://www.ory.sh/not-kratos") testhelpers.SelfServiceHookSettingsSetDefaultRedirectToStrategy(strategy, "https://www.ory.sh/kratos") - res, _ := makeRequestPost(t, newServer(t), false, url.Values{}) + res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.EqualValues(t, "https://www.ory.sh/kratos/", res.Request.URL.String()) }) @@ -135,7 +136,7 @@ func TestSettingsExecutor(t *testing.T) { t.Run("case=pass if hooks pass", func(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset) viperSetPost(strategy, []configuration.SelfServiceHook{{Name: "err", Config: []byte(`{}`)}}) - res, _ := makeRequestPost(t, newServer(t), false, url.Values{}) + res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser), false, url.Values{}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.Contains(t, res.Request.URL.String(), uiURL) }) @@ -143,7 +144,7 @@ func TestSettingsExecutor(t *testing.T) { t.Run("case=send a json response for API clients", func(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset) viperSetPost(strategy, nil) - res, body := makeRequestPost(t, newServer(t), true, url.Values{}) + res, body := makeRequestPost(t, newServer(t, flow.TypeAPI), true, url.Values{}) assert.EqualValues(t, http.StatusOK, res.StatusCode) assert.NotEmpty(t, gjson.Get(body, "identity.id")) }) diff --git a/selfservice/flow/settings/strategy.go b/selfservice/flow/settings/strategy.go index 4a2108dad28..13fb1d3f212 100644 --- a/selfservice/flow/settings/strategy.go +++ b/selfservice/flow/settings/strategy.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" - "github.com/ory/kratos/session" + "github.com/ory/kratos/identity" "github.com/ory/kratos/x" ) @@ -19,7 +19,7 @@ var pkgName = reflect.TypeOf(Strategies{}).PkgPath() type Strategy interface { SettingsStrategyID() string RegisterSettingsRoutes(*x.RouterPublic) - PopulateSettingsMethod(*http.Request, *session.Session, *Request) error + PopulateSettingsMethod(*http.Request, *identity.Identity, *Flow) error } type Strategies []Strategy diff --git a/selfservice/flow/settings/strategy_helper.go b/selfservice/flow/settings/strategy_helper.go index c8400652b94..efb06cc6962 100644 --- a/selfservice/flow/settings/strategy_helper.go +++ b/selfservice/flow/settings/strategy_helper.go @@ -31,14 +31,14 @@ type UpdatePayload interface { type UpdateContext struct { Valid bool Session *session.Session - Request *Request + Flow *Flow } func PrepareUpdate(d interface { x.LoggingProvider continuity.ManagementProvider session.ManagementProvider - RequestPersistenceProvider + FlowPersistenceProvider }, w http.ResponseWriter, r *http.Request, name string, payload UpdatePayload) (*UpdateContext, error) { ss, err := d.SessionManager().FetchFromRequest(r.Context(), r) if err != nil { @@ -52,7 +52,7 @@ func PrepareUpdate(d interface { payload.SetRequestID(rid) - req, err := d.SettingsRequestPersister().GetSettingsRequest(r.Context(), rid) + req, err := d.SettingsFlowPersister().GetSettingsFlow(r.Context(), rid) if errors.Is(err, sqlcon.ErrNoRows) { return new(UpdateContext), errors.WithStack(herodot.ErrNotFound.WithReasonf("The settings request could not be found. Please restart the flow.")) } else if err != nil { @@ -63,7 +63,7 @@ func PrepareUpdate(d interface { return new(UpdateContext), err } - c := &UpdateContext{Session: ss, Request: req, Valid: true} + c := &UpdateContext{Session: ss, Flow: req, Valid: true} if _, err := d.ContinuityManager().Continue( r.Context(), w, r, name, ContinuityOptions(payload, ss.Identity)...); err == nil { diff --git a/selfservice/flow/verification/error.go b/selfservice/flow/verification/error.go index 76c516430f1..de57232a4fd 100644 --- a/selfservice/flow/verification/error.go +++ b/selfservice/flow/verification/error.go @@ -73,7 +73,7 @@ func (s *ErrorHandler) HandleVerificationError( if e := new(errRequestExpired); errors.As(err, &e) { a := NewRequest( - s.c.SelfServiceFlowSettingsRequestLifespan(), r, rr.Via, + s.c.SelfServiceFlowSettingsFlowLifespan(), r, rr.Via, urlx.AppendPaths(s.c.SelfPublicURL(), PublicVerificationRequestPath), s.d.GenerateCSRFToken, ) diff --git a/selfservice/flow/verification/handler.go b/selfservice/flow/verification/handler.go index 0de202dd9f3..0d693cd1e28 100644 --- a/selfservice/flow/verification/handler.go +++ b/selfservice/flow/verification/handler.go @@ -105,7 +105,7 @@ func (h *Handler) init(w http.ResponseWriter, r *http.Request, ps httprouter.Par } a := NewRequest( - h.c.SelfServiceFlowSettingsRequestLifespan(), r, via, + h.c.SelfServiceFlowSettingsFlowLifespan(), r, via, urlx.AppendPaths(h.c.SelfPublicURL(), strings.ReplaceAll(PublicVerificationCompletePath, ":via", string(via))), h.d.GenerateCSRFToken, ) @@ -338,7 +338,7 @@ func (h *Handler) verify(w http.ResponseWriter, r *http.Request, ps httprouter.P if err := h.d.PrivilegedIdentityPool().VerifyAddress(r.Context(), ps.ByName("code")); err != nil { if errors.Is(err, sqlcon.ErrNoRows) { a := NewRequest( - h.c.SelfServiceFlowSettingsRequestLifespan(), r, via, + h.c.SelfServiceFlowSettingsFlowLifespan(), r, via, urlx.AppendPaths(h.c.SelfPublicURL(), strings.ReplaceAll(PublicVerificationCompletePath, ":via", string(via))), h.d.GenerateCSRFToken, ) diff --git a/selfservice/hook/error.go b/selfservice/hook/error.go index 3091d8bab9b..2168f55ecae 100644 --- a/selfservice/hook/error.go +++ b/selfservice/hook/error.go @@ -40,11 +40,11 @@ func (e Error) err(path string, abort error) error { return nil } -func (e Error) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *settings.Request, s *identity.Identity) error { +func (e Error) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, s *identity.Identity) error { return e.err("ExecuteSettingsPrePersistHook", settings.ErrHookAbortRequest) } -func (e Error) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *settings.Request, s *identity.Identity) error { +func (e Error) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, s *identity.Identity) error { return e.err("ExecuteSettingsPostPersistHook", settings.ErrHookAbortRequest) } diff --git a/selfservice/hook/verification.go b/selfservice/hook/verification.go index 29ed741166d..b8b45d3b9a9 100644 --- a/selfservice/hook/verification.go +++ b/selfservice/hook/verification.go @@ -30,7 +30,7 @@ func (e *Verifier) ExecutePostRegistrationPostPersistHook(_ http.ResponseWriter, return e.do(r, s.Identity) } -func (e *Verifier) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *settings.Request, i *identity.Identity) error { +func (e *Verifier) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, i *identity.Identity) error { return e.do(r, i) } diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index 3ba878abacb..e17d3744f53 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -72,7 +72,7 @@ type dependencies interface { registration.ErrorHandlerProvider settings.ErrorHandlerProvider - settings.RequestPersistenceProvider + settings.FlowPersistenceProvider settings.HookExecutorProvider continuity.ManagementProvider @@ -226,7 +226,7 @@ func (s *Strategy) validateRequest(ctx context.Context, r *http.Request, rid uui return ar, nil } - ar, err := s.d.SettingsRequestPersister().GetSettingsRequest(ctx, rid) + ar, err := s.d.SettingsFlowPersister().GetSettingsFlow(ctx, rid) if err == nil { sess, err := s.d.SessionManager().FetchFromRequest(ctx, r) if err != nil { @@ -280,7 +280,7 @@ func (s *Strategy) validateCallback(w http.ResponseWriter, r *http.Request) (req func (s *Strategy) alreadyAuthenticated(w http.ResponseWriter, r *http.Request, req interface{}) bool { // we assume an error means the user has no session if _, err := s.d.SessionManager().FetchFromRequest(r.Context(), r); err == nil { - if _, ok := req.(*settings.Request); ok { + if _, ok := req.(*settings.Flow); ok { // ignore this if it's a settings request } else if !isForced(req) { http.Redirect(w, r, s.c.SelfServiceBrowserDefaultReturnTo().String(), http.StatusFound) @@ -342,13 +342,13 @@ func (s *Strategy) handleCallback(w http.ResponseWriter, r *http.Request, ps htt case *registration.Flow: s.processRegistration(w, r, a, claims, provider, container) return - case *settings.Request: + case *settings.Flow: sess, err := s.d.SessionManager().FetchFromRequest(r.Context(), r) if err != nil { s.handleError(w, r, req.GetID(), pid, nil, err) return } - s.linkProvider(w, r, &settings.UpdateContext{Session: sess, Request: a}, claims, provider) + s.linkProvider(w, r, &settings.UpdateContext{Session: sess, Flow: a}, claims, provider) return default: s.handleError(w, r, req.GetID(), pid, nil, errors.WithStack(x.PseudoPanic. @@ -416,8 +416,14 @@ func (s *Strategy) handleError(w http.ResponseWriter, r *http.Request, rid uuid. if lr, rerr := s.d.LoginFlowPersister().GetLoginFlow(r.Context(), rid); rerr == nil { s.d.LoginFlowErrorHandler().WriteFlowError(w, r, s.ID(), lr, err) return - } else if sr, rerr := s.d.SettingsRequestPersister().GetSettingsRequest(r.Context(), rid); rerr == nil { - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, sr, err, s.SettingsStrategyID()) + } else if sr, rerr := s.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), rid); rerr == nil { + sess, err := s.d.SessionManager().FetchFromRequest(r.Context(), r) + if err != nil { + s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + return + } + + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), sr, sess.Identity, err) return } else if rr, rerr := s.d.RegistrationFlowPersister().GetRegistrationFlow(r.Context(), rid); rerr == nil { if method, ok := rr.Methods[s.ID()]; ok { diff --git a/selfservice/strategy/oidc/strategy_helper_test.go b/selfservice/strategy/oidc/strategy_helper_test.go index 5f2a395abf6..012277a3f6f 100644 --- a/selfservice/strategy/oidc/strategy_helper_test.go +++ b/selfservice/strategy/oidc/strategy_helper_test.go @@ -172,7 +172,7 @@ func newUI(t *testing.T, reg driver.Registry) *httptest.Server { } else if r.URL.Path == "/registration" { e, err = reg.RegistrationFlowPersister().GetRegistrationFlow(r.Context(), x.ParseUUID(r.URL.Query().Get("request"))) } else if r.URL.Path == "/settings" { - e, err = reg.SettingsRequestPersister().GetSettingsRequest(r.Context(), x.ParseUUID(r.URL.Query().Get("request"))) + e, err = reg.SettingsFlowPersister().GetSettingsFlow(r.Context(), x.ParseUUID(r.URL.Query().Get("request"))) } require.NoError(t, err) diff --git a/selfservice/strategy/oidc/strategy_settings.go b/selfservice/strategy/oidc/strategy_settings.go index c82db85eff8..43e7be7a3da 100644 --- a/selfservice/strategy/oidc/strategy_settings.go +++ b/selfservice/strategy/oidc/strategy_settings.go @@ -18,7 +18,6 @@ import ( "github.com/ory/kratos/identity" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/selfservice/form" - "github.com/ory/kratos/session" "github.com/ory/kratos/x" ) @@ -114,13 +113,13 @@ func (s *Strategy) linkableProviders(conf *ConfigurationCollection, confidential return result, nil } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, ss *session.Session, sr *settings.Request) error { +func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, sr *settings.Flow) error { conf, err := s.Config() if err != nil { return err } - confidential, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), ss.IdentityID) + confidential, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), id.ID) if err != nil { return err } @@ -136,7 +135,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, ss *session.Session, } f := form.NewHTMLForm(urlx.CopyWithQuery(urlx.AppendPaths( - s.c.SelfPublicURL(), SettingsPath), url.Values{"request": {sr.ID.String()}}).String()) + s.c.SelfPublicURL(), SettingsPath), url.Values{"flow": {sr.ID.String()}}).String()) f.SetCSRF(s.d.GenerateCSRFToken(r)) for _, l := range linkable { @@ -155,9 +154,9 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, ss *session.Session, }) } - sr.Methods[s.SettingsStrategyID()] = &settings.RequestMethod{ + sr.Methods[s.SettingsStrategyID()] = &settings.FlowMethod{ Method: s.SettingsStrategyID(), - Config: &settings.RequestMethodConfig{RequestMethodConfigurator: NewRequestMethodConfig(f)}, + Config: &settings.FlowMethodConfig{FlowMethodConfigurator: NewRequestMethodConfig(f)}, } return nil @@ -305,7 +304,7 @@ func (s *Strategy) initLinkProvider(w http.ResponseWriter, r *http.Request, ctxU func (s *Strategy) linkProvider(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, claims *Claims, provider Provider) { p := &completeSelfServiceBrowserSettingsOIDCFlowPayload{ - Link: provider.Config().ID, RequestID: ctxUpdate.Request.ID.String()} + Link: provider.Config().ID, RequestID: ctxUpdate.Flow.ID.String()} if ctxUpdate.Session.AuthenticatedAt.Add(s.c.SelfServiceFlowSettingsPrivilegedSessionMaxAge()).Before(time.Now()) { s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(settings.ErrRequestNeedsReAuthentication)) return @@ -341,7 +340,7 @@ func (s *Strategy) linkProvider(w http.ResponseWriter, r *http.Request, i.Credentials[s.ID()] = *creds if err := s.d.SettingsHookExecutor().PostSettingsHook(w, r, s.SettingsStrategyID(), ctxUpdate, i, settings.WithCallback(func(ctxUpdate *settings.UpdateContext) error { - return s.PopulateSettingsMethod(r, ctxUpdate.Session, ctxUpdate.Request) + return s.PopulateSettingsMethod(r, ctxUpdate.Session.Identity, ctxUpdate.Flow) })); err != nil { s.handleSettingsError(w, r, ctxUpdate, p, err) return @@ -410,7 +409,7 @@ func (s *Strategy) unlinkProvider(w http.ResponseWriter, r *http.Request, i.Credentials[s.ID()] = *creds if err := s.d.SettingsHookExecutor().PostSettingsHook(w, r, s.SettingsStrategyID(), ctxUpdate, i, settings.WithCallback(func(ctxUpdate *settings.UpdateContext) error { - return s.PopulateSettingsMethod(r, ctxUpdate.Session, ctxUpdate.Request) + return s.PopulateSettingsMethod(r, ctxUpdate.Session.Identity, ctxUpdate.Flow) })); err != nil { s.handleSettingsError(w, r, ctxUpdate, p, err) return @@ -421,15 +420,15 @@ func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, c if errors.Is(err, settings.ErrRequestNeedsReAuthentication) { if err := s.d.ContinuityManager().Pause(r.Context(), w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.Session.Identity)...); err != nil { - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, ctxUpdate.Request, err, s.SettingsStrategyID()) + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), ctxUpdate.Flow, ctxUpdate.Session.Identity, err) return } } - if ctxUpdate.Request != nil { - ctxUpdate.Request.Methods[s.SettingsStrategyID()].Config.ResetMessages() - ctxUpdate.Request.Methods[s.SettingsStrategyID()].Config.SetCSRF(s.d.GenerateCSRFToken(r)) + if ctxUpdate.Flow != nil { + ctxUpdate.Flow.Methods[s.SettingsStrategyID()].Config.ResetMessages() + ctxUpdate.Flow.Methods[s.SettingsStrategyID()].Config.SetCSRF(s.d.GenerateCSRFToken(r)) } - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, ctxUpdate.Request, err, s.SettingsStrategyID()) + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), ctxUpdate.Flow, ctxUpdate.Session.Identity, err) } diff --git a/selfservice/strategy/oidc/strategy_settings_test.go b/selfservice/strategy/oidc/strategy_settings_test.go index a35eb2ce530..4926f925aef 100644 --- a/selfservice/strategy/oidc/strategy_settings_test.go +++ b/selfservice/strategy/oidc/strategy_settings_test.go @@ -107,9 +107,9 @@ func TestSettingsStrategy(t *testing.T) { agents := testhelpers.AddAndLoginIdentities(t, reg, publicTS, users) // new profile request - var npr = func(t *testing.T, client *http.Client, redirectTo string, exp time.Duration) *settings.Request { - req, err := reg.SettingsRequestPersister().GetSettingsRequest(context.Background(), - x.ParseUUID(string(testhelpers.GetSettingsRequest(t, client, publicTS).Payload.ID))) + var npr = func(t *testing.T, client *http.Client, redirectTo string, exp time.Duration) *settings.Flow { + req, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), + x.ParseUUID(string(testhelpers.GetSettingsFlow(t, client, publicTS).Payload.ID))) require.NoError(t, err) assert.Empty(t, req.Active) @@ -117,10 +117,10 @@ func TestSettingsStrategy(t *testing.T) { req.RequestURL = redirectTo } req.ExpiresAt = time.Now().Add(exp) - require.NoError(t, reg.SettingsRequestPersister().UpdateSettingsRequest(context.Background(), req)) + require.NoError(t, reg.SettingsFlowPersister().UpdateSettingsFlow(context.Background(), req)) // sanity check - got, err := reg.SettingsRequestPersister().GetSettingsRequest(context.Background(), req.ID) + got, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), req.ID) require.NoError(t, err) require.Len(t, got.Methods, len(req.Methods)) @@ -543,18 +543,18 @@ func TestPopulateSettingsMethod(t *testing.T) { return ss.(*oidc.Strategy) } - nr := func() *settings.Request { - return &settings.Request{ID: x.NewUUID(), Methods: map[string]*settings.RequestMethod{}} + nr := func() *settings.Flow { + return &settings.Flow{ID: x.NewUUID(), Methods: map[string]*settings.FlowMethod{}} } - populate := func(t *testing.T, reg *driver.RegistryDefault, i *identity.Identity, req *settings.Request) *form.HTMLForm { + populate := func(t *testing.T, reg *driver.RegistryDefault, i *identity.Identity, req *settings.Flow) *form.HTMLForm { require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i)) require.NoError(t, ns(t, reg).PopulateSettingsMethod(new(http.Request), &session.Session{Identity: i, IdentityID: i.ID}, req)) require.NotNil(t, req.Methods[identity.CredentialsTypeOIDC.String()]) require.NotNil(t, req.Methods[identity.CredentialsTypeOIDC.String()].Config) - require.NotNil(t, req.Methods[identity.CredentialsTypeOIDC.String()].Config.RequestMethodConfigurator) + require.NotNil(t, req.Methods[identity.CredentialsTypeOIDC.String()].Config.FlowMethodConfigurator) require.Equal(t, identity.CredentialsTypeOIDC.String(), req.Methods[identity.CredentialsTypeOIDC.String()].Method) - f := req.Methods[identity.CredentialsTypeOIDC.String()].Config.RequestMethodConfigurator.(*oidc.RequestMethod).HTMLForm + f := req.Methods[identity.CredentialsTypeOIDC.String()].Config.FlowMethodConfigurator.(*oidc.RequestMethod).HTMLForm assert.Equal(t, "https://www.ory.sh"+oidc.SettingsPath+"?request="+req.ID.String(), f.Action) assert.Equal(t, "POST", f.Method) return f diff --git a/selfservice/strategy/password/login.go b/selfservice/strategy/password/login.go index 149cbf40048..339badbc4aa 100644 --- a/selfservice/strategy/password/login.go +++ b/selfservice/strategy/password/login.go @@ -189,6 +189,6 @@ func (s *Strategy) PopulateLoginMethod(r *http.Request, sr *login.Flow) error { sr.Methods[identity.CredentialsTypePassword] = &login.FlowMethod{ Method: identity.CredentialsTypePassword, - Config: &login.FlowMethodConfig{FlowMethodConfigurator: &RequestMethod{HTMLForm: f}}} + Config: &login.FlowMethodConfig{FlowMethodConfigurator: &FlowMethod{HTMLForm: f}}} return nil } diff --git a/selfservice/strategy/password/login_test.go b/selfservice/strategy/password/login_test.go index f4852857731..457b317a308 100644 --- a/selfservice/strategy/password/login_test.go +++ b/selfservice/strategy/password/login_test.go @@ -547,7 +547,7 @@ func TestCompleteLogin(t *testing.T) { identity.CredentialsTypePassword: { Method: identity.CredentialsTypePassword, Config: &login.FlowMethodConfig{ - FlowMethodConfigurator: &password.RequestMethod{ + FlowMethodConfigurator: &password.FlowMethod{ HTMLForm: &form.HTMLForm{ Method: "POST", Action: "/action", diff --git a/selfservice/strategy/password/registration.go b/selfservice/strategy/password/registration.go index dbea0f74ab0..ff6742265d4 100644 --- a/selfservice/strategy/password/registration.go +++ b/selfservice/strategy/password/registration.go @@ -230,7 +230,7 @@ func (s *Strategy) PopulateRegistrationMethod(r *http.Request, sr *registration. sr.Methods[identity.CredentialsTypePassword] = ®istration.FlowMethod{ Method: identity.CredentialsTypePassword, - Config: ®istration.FlowMethodConfig{FlowMethodConfigurator: &RequestMethod{HTMLForm: htmlf}}, + Config: ®istration.FlowMethodConfig{FlowMethodConfigurator: &FlowMethod{HTMLForm: htmlf}}, } return nil diff --git a/selfservice/strategy/password/registration_test.go b/selfservice/strategy/password/registration_test.go index c86a2fd5cb1..d0b0c17ef8f 100644 --- a/selfservice/strategy/password/registration_test.go +++ b/selfservice/strategy/password/registration_test.go @@ -92,7 +92,7 @@ func TestRegistration(t *testing.T) { identity.CredentialsTypePassword: { Method: identity.CredentialsTypePassword, Config: ®istration.FlowMethodConfig{ - FlowMethodConfigurator: password.RequestMethod{ + FlowMethodConfigurator: password.FlowMethod{ HTMLForm: &form.HTMLForm{ Method: "POST", Action: "/action", @@ -355,9 +355,9 @@ func TestRegistration(t *testing.T) { } t.Run("type=api", func(t *testing.T) { - _, _ = run(t, true, `{"password":"c0a5af7a-fa32-4fe1-85b9-3beb4a127164","traits.username":"registration-identifier-8-api-duplicate","traits.foobar":"bar"}`,http.StatusOK) + _, _ = run(t, true, `{"password":"c0a5af7a-fa32-4fe1-85b9-3beb4a127164","traits.username":"registration-identifier-8-api-duplicate","traits.foobar":"bar"}`, http.StatusOK) - body, res := run(t, true, `{"password":"c0a5af7a-fa32-4fe1-85b9-3beb4a127164","traits.username":"registration-identifier-8-api-duplicate","traits.foobar":"bar"}`,http.StatusBadRequest) + body, res := run(t, true, `{"password":"c0a5af7a-fa32-4fe1-85b9-3beb4a127164","traits.username":"registration-identifier-8-api-duplicate","traits.foobar":"bar"}`, http.StatusBadRequest) assert.Contains(t, res.Request.URL.String(), publicTS.URL+password.RouteRegistration) assert.Contains(t, gjson.GetBytes(body, "methods.password.config.messages.0.text").String(), "An account with the same identifier (email, phone, username, ...) exists already.", "%s", body) }) @@ -394,7 +394,7 @@ func TestRegistration(t *testing.T) { identity.CredentialsTypePassword: { Method: identity.CredentialsTypePassword, Config: ®istration.FlowMethodConfig{ - FlowMethodConfigurator: &password.RequestMethod{ + FlowMethodConfigurator: &password.FlowMethod{ HTMLForm: &form.HTMLForm{ Method: "POST", Action: "/action", @@ -532,7 +532,7 @@ func TestRegistration(t *testing.T) { expected := ®istration.FlowMethod{ Method: identity.CredentialsTypePassword, Config: ®istration.FlowMethodConfig{ - FlowMethodConfigurator: &password.RequestMethod{ + FlowMethodConfigurator: &password.FlowMethod{ HTMLForm: &form.HTMLForm{ Action: "https://foo" + password.RouteRegistration + "?flow=" + sr.ID.String(), Method: "POST", @@ -563,6 +563,6 @@ func TestRegistration(t *testing.T) { } actual := sr.Methods[identity.CredentialsTypePassword] - assert.EqualValues(t, expected.Config.FlowMethodConfigurator.(*password.RequestMethod).HTMLForm, actual.Config.FlowMethodConfigurator.(*password.RequestMethod).HTMLForm) + assert.EqualValues(t, expected.Config.FlowMethodConfigurator.(*password.FlowMethod).HTMLForm, actual.Config.FlowMethodConfigurator.(*password.FlowMethod).HTMLForm) }) } diff --git a/selfservice/strategy/password/settings.go b/selfservice/strategy/password/settings.go index 8e3f8339bf9..e2d373e31b3 100644 --- a/selfservice/strategy/password/settings.go +++ b/selfservice/strategy/password/settings.go @@ -17,7 +17,6 @@ import ( "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/selfservice/form" - "github.com/ory/kratos/session" "github.com/ory/kratos/x" ) @@ -57,7 +56,7 @@ func (p *completeSelfServiceBrowserSettingsPasswordFlowPayload) SetRequestID(rid p.RequestID = rid.String() } -// swagger:route POST /self-service/browser/flows/settings/strategies/password public completeSelfServiceBrowserSettingsPasswordStrategyFlow +// swagger:route POST /self-service/browser/flows/settings/strategies/password public completeSelfServiceSettingsFlowWithPasswordMethod // // Complete the browser-based settings flow for the password strategy // @@ -88,7 +87,7 @@ func (s *Strategy) submitSettingsFlow(w http.ResponseWriter, r *http.Request, ps return } - p.RequestID = ctxUpdate.Request.ID.String() + p.RequestID = ctxUpdate.Flow.ID.String() p.Password = r.PostFormValue("password") s.continueSettingsFlow(w, r, ctxUpdate, &p) } @@ -146,18 +145,18 @@ func (s *Strategy) continueSettingsFlow( } } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, ss *session.Session, pr *settings.Request) error { - f := &form.HTMLForm{ +func (s *Strategy) PopulateSettingsMethod(r *http.Request, _ *identity.Identity, f *settings.Flow) error { + hf := &form.HTMLForm{ Action: urlx.CopyWithQuery(urlx.AppendPaths(s.c.SelfPublicURL(), SettingsPath), - url.Values{"request": {pr.ID.String()}}, + url.Values{"flow": {f.ID.String()}}, ).String(), Fields: form.Fields{{Name: "password", Type: "password", Required: true}}, Method: "POST", } - f.SetCSRF(s.d.GenerateCSRFToken(r)) + hf.SetCSRF(s.d.GenerateCSRFToken(r)) - pr.Methods[string(s.ID())] = &settings.RequestMethod{ + f.Methods[string(s.ID())] = &settings.FlowMethod{ Method: string(s.ID()), - Config: &settings.RequestMethodConfig{RequestMethodConfigurator: &RequestMethod{HTMLForm: f}}, + Config: &settings.FlowMethodConfig{FlowMethodConfigurator: &FlowMethod{HTMLForm: hf}}, } return nil } @@ -166,15 +165,15 @@ func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, c if errors.Is(err, settings.ErrRequestNeedsReAuthentication) { if err := s.d.ContinuityManager().Pause(r.Context(), w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.Session.Identity)...); err != nil { - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, ctxUpdate.Request, err, s.SettingsStrategyID()) + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), ctxUpdate.Flow, ctxUpdate.Session.Identity, err) return } } - if ctxUpdate.Request != nil { - ctxUpdate.Request.Methods[s.SettingsStrategyID()].Config.Reset() - ctxUpdate.Request.Methods[s.SettingsStrategyID()].Config.SetCSRF(s.d.GenerateCSRFToken(r)) + if ctxUpdate.Flow != nil { + ctxUpdate.Flow.Methods[s.SettingsStrategyID()].Config.Reset() + ctxUpdate.Flow.Methods[s.SettingsStrategyID()].Config.SetCSRF(s.d.GenerateCSRFToken(r)) } - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, ctxUpdate.Request, err, s.SettingsStrategyID()) + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), ctxUpdate.Flow, ctxUpdate.Session.Identity, err) } diff --git a/selfservice/strategy/password/settings_test.go b/selfservice/strategy/password/settings_test.go index f618743b76e..5be4799ce11 100644 --- a/selfservice/strategy/password/settings_test.go +++ b/selfservice/strategy/password/settings_test.go @@ -62,7 +62,7 @@ func TestSettings(t *testing.T) { adminClient := testhelpers.NewSDKClient(adminTS) t.Run("description=should update the password and clear errors after input error occurred", func(t *testing.T) { - rs := testhelpers.GetSettingsRequest(t, primaryUser, publicTS) + rs := testhelpers.GetSettingsFlow(t, primaryUser, publicTS) form := rs.Payload.Methods[string(identity.CredentialsTypePassword)].Config values := testhelpers.SDKFormFieldsToURLValues(form.Fields) @@ -108,7 +108,7 @@ func TestSettings(t *testing.T) { }) t.Run("description=should update the password even if no password was set before", func(t *testing.T) { - rs := testhelpers.GetSettingsRequest(t, secondaryUser, publicTS) + rs := testhelpers.GetSettingsFlow(t, secondaryUser, publicTS) form := rs.Payload.Methods[string(identity.CredentialsTypePassword)].Config values := testhelpers.SDKFormFieldsToURLValues(form.Fields) @@ -140,7 +140,7 @@ func TestSettings(t *testing.T) { viper.Set(configuration.ViperKeySelfServiceSettingsAfter, nil) }) - rs := testhelpers.GetSettingsRequest(t, primaryUser, publicTS) + rs := testhelpers.GetSettingsFlow(t, primaryUser, publicTS) form := rs.Payload.Methods[string(identity.CredentialsTypePassword)].Config values := testhelpers.SDKFormFieldsToURLValues(form.Fields) diff --git a/selfservice/strategy/password/strategy.go b/selfservice/strategy/password/strategy.go index b5076a6cfff..f3851833c4e 100644 --- a/selfservice/strategy/password/strategy.go +++ b/selfservice/strategy/password/strategy.go @@ -49,7 +49,7 @@ type registrationStrategyDependencies interface { login.FlowPersistenceProvider login.HandlerProvider - settings.RequestPersistenceProvider + settings.FlowPersistenceProvider settings.HookExecutorProvider settings.HooksProvider settings.ErrorHandlerProvider diff --git a/selfservice/strategy/password/types.go b/selfservice/strategy/password/types.go index 5ab5bd1d489..a1c0fb83fd8 100644 --- a/selfservice/strategy/password/types.go +++ b/selfservice/strategy/password/types.go @@ -22,7 +22,7 @@ type ( } ) -// RequestMethod contains the configuration for this selfservice strategy. -type RequestMethod struct { +// FlowMethod contains the configuration for this selfservice strategy. +type FlowMethod struct { *form.HTMLForm } diff --git a/selfservice/strategy/profile/strategy.go b/selfservice/strategy/profile/strategy.go index 18fb15289aa..b012ef974f2 100644 --- a/selfservice/strategy/profile/strategy.go +++ b/selfservice/strategy/profile/strategy.go @@ -31,7 +31,7 @@ import ( ) const ( - PublicSettingsProfilePath = "/self-service/browser/flows/settings/strategies/profile" + PublicSettingsProfilePath = "/self-service/settings/methods/profile" ) var _ settings.Strategy = new(Strategy) @@ -57,7 +57,7 @@ type ( settings.HookExecutorProvider settings.ErrorHandlerProvider - settings.RequestPersistenceProvider + settings.FlowPersistenceProvider settings.StrategyProvider settings.HooksProvider @@ -88,8 +88,8 @@ func (s *Strategy) RegisterSettingsRoutes(public *x.RouterPublic) { public.GET(PublicSettingsProfilePath, s.d.SessionHandler().IsAuthenticated(s.handleSubmit, redirect)) } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, ss *session.Session, pr *settings.Request) error { - traitsSchema, err := s.c.IdentityTraitsSchemas().FindSchemaByID(ss.Identity.SchemaID) +func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, pr *settings.Flow) error { + traitsSchema, err := s.c.IdentityTraitsSchemas().FindSchemaByID(id.SchemaID) if err != nil { return err } @@ -99,27 +99,27 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, ss *session.Session, f, err := form.NewHTMLFormFromJSONSchema(urlx.CopyWithQuery( urlx.AppendPaths(s.c.SelfPublicURL(), PublicSettingsProfilePath), - url.Values{"request": {pr.ID.String()}}, + url.Values{"flow": {pr.ID.String()}}, ).String(), traitsSchema.URL, "", schemaCompiler) if err != nil { return err } - f.SetValuesFromJSON(json.RawMessage(ss.Identity.Traits), "traits") + f.SetValuesFromJSON(json.RawMessage(id.Traits), "traits") f.SetCSRF(s.d.GenerateCSRFToken(r)) if err := f.SortFields(traitsSchema.URL); err != nil { return err } - pr.Methods[s.SettingsStrategyID()] = &settings.RequestMethod{ + pr.Methods[s.SettingsStrategyID()] = &settings.FlowMethod{ Method: s.SettingsStrategyID(), - Config: &settings.RequestMethodConfig{RequestMethodConfigurator: &SettingsProfileRequestMethod{HTMLForm: f}}, + Config: &settings.FlowMethodConfig{FlowMethodConfigurator: &SettingsProfileRequestMethod{HTMLForm: f}}, } return nil } -// swagger:route POST /self-service/browser/flows/settings/strategies/profile public completeSelfServiceBrowserSettingsProfileStrategyFlow +// swagger:route POST /self-service/settings/methods/profile public completeSelfServiceSettingsFlowWithProfileMethod // // Complete the browser-based settings flow for profile data // @@ -140,6 +140,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, ss *session.Session, // Schemes: http, https // // Responses: +// 200: settingsViaApiResponse // 302: emptyResponse // 500: genericError func (s *Strategy) handleSubmit(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { @@ -171,7 +172,7 @@ func (s *Strategy) handleSubmit(w http.ResponseWriter, r *http.Request, ps httpr } // Reset after decoding form - p.SetRequestID(ctxUpdate.Request.ID) + p.SetRequestID(ctxUpdate.Flow.ID) s.continueFlow(w, r, ctxUpdate, &p) } @@ -182,14 +183,14 @@ func (s *Strategy) continueFlow(w http.ResponseWriter, r *http.Request, ctxUpdat return } - if err := s.hydrateForm(r, ctxUpdate.Request, ctxUpdate.Session, p.Traits); err != nil { - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, ctxUpdate.Request, err, settings.StrategyProfile) + if err := s.hydrateForm(r, ctxUpdate.Flow, ctxUpdate.Session, p.Traits); err != nil { + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, settings.StrategyProfile, ctxUpdate.Flow, ctxUpdate.Session.Identity, err) return } update, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), ctxUpdate.Session.Identity.ID) if err != nil { - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, ctxUpdate.Request, err, settings.StrategyProfile) + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, settings.StrategyProfile, ctxUpdate.Flow, ctxUpdate.Session.Identity, err) return } @@ -244,7 +245,7 @@ func (p *completeSelfServiceBrowserSettingsStrategyProfileFlowPayload) SetReques p.rid = rid } -func (s *Strategy) hydrateForm(r *http.Request, ar *settings.Request, ss *session.Session, traits json.RawMessage) error { +func (s *Strategy) hydrateForm(r *http.Request, ar *settings.Flow, ss *session.Session, traits json.RawMessage) error { action := urlx.CopyWithQuery( urlx.AppendPaths(s.c.SelfPublicURL(), PublicSettingsProfilePath), url.Values{"request": {ar.ID.String()}}, @@ -284,23 +285,23 @@ func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, p if err := s.d.ContinuityManager().Pause(r.Context(), w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, puc.Session.Identity)...); err != nil { - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, puc.Request, err, s.SettingsStrategyID()) + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), puc.Flow, puc.Session.Identity, err) return } } - if puc.Request != nil { + if puc.Flow != nil { if traits == nil { traits = json.RawMessage(puc.Session.Identity.Traits) } - if err := s.hydrateForm(r, puc.Request, puc.Session, traits); err != nil { - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, puc.Request, err, s.SettingsStrategyID()) + if err := s.hydrateForm(r, puc.Flow, puc.Session, traits); err != nil { + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), puc.Flow, puc.Session.Identity, err) return } } - s.d.SettingsRequestErrorHandler().HandleSettingsError(w, r, puc.Request, err, s.SettingsStrategyID()) + s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), puc.Flow, puc.Session.Identity, err) } // newSettingsProfileDecoder returns a decoderx.HTTPDecoderOption with a JSON Schema for type assertion and diff --git a/selfservice/strategy/profile/strategy_test.go b/selfservice/strategy/profile/strategy_test.go index 4339b10d329..c8df87d87b1 100644 --- a/selfservice/strategy/profile/strategy_test.go +++ b/selfservice/strategy/profile/strategy_test.go @@ -104,7 +104,7 @@ func TestStrategyTraits(t *testing.T) { }) t.Run("description=should redirect to settings management ui and /settings/requests?request=... should come back with the right information", func(t *testing.T) { - res, err := primaryUser.Get(publicTS.URL + settings.PublicPath) + res, err := primaryUser.Get(publicTS.URL + settings.RouteInitBrowserFlow) require.NoError(t, err) assert.Equal(t, ui.URL, res.Request.URL.Scheme+"://"+res.Request.URL.Host) @@ -123,7 +123,7 @@ func TestStrategyTraits(t *testing.T) { assert.Equal(t, primaryIdentity.ID.String(), string(pr.Payload.Identity.ID)) assert.JSONEq(t, string(primaryIdentity.Traits), x.MustEncodeJSON(t, pr.Payload.Identity.Traits)) assert.Equal(t, primaryIdentity.SchemaID, pointerx.StringR(pr.Payload.Identity.SchemaID)) - assert.Equal(t, publicTS.URL+settings.PublicPath, pointerx.StringR(pr.Payload.RequestURL)) + assert.Equal(t, publicTS.URL+settings.RouteInitBrowserFlow, pointerx.StringR(pr.Payload.RequestURL)) found := false diff --git a/selfservice/strategy/recoverytoken/strategy.go b/selfservice/strategy/recoverytoken/strategy.go index 6c9ce741f61..7a5fb95a0f1 100644 --- a/selfservice/strategy/recoverytoken/strategy.go +++ b/selfservice/strategy/recoverytoken/strategy.go @@ -24,6 +24,7 @@ import ( "github.com/ory/kratos/identity" "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/errorx" + "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/recovery" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/selfservice/form" @@ -349,14 +350,14 @@ func (s *Strategy) issueSession(w http.ResponseWriter, r *http.Request, req *rec return } - sr := settings.NewRequest(s.c.SelfServiceFlowSettingsRequestLifespan(), r, sess) + sr := settings.NewFlow(s.c.SelfServiceFlowSettingsFlowLifespan(), r, sess.Identity, flow.TypeBrowser) sr.Messages.Set(text.NewRecoverySuccessful(time.Now().Add(s.c.SelfServiceFlowSettingsPrivilegedSessionMaxAge()))) - if err := s.d.SettingsHandler().CreateRequest(w, r, sess, sr); err != nil { + if _, err := s.d.SettingsHandler().NewFlow(w, r, sess.Identity, flow.TypeBrowser); err != nil { s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) return } - http.Redirect(w, r, sr.URL(s.c.SelfServiceFlowSettingsUI()).String(), http.StatusFound) + http.Redirect(w, r, sr.AppendTo(s.c.SelfServiceFlowSettingsUI()).String(), http.StatusFound) } func (s *Strategy) verifyToken(w http.ResponseWriter, r *http.Request) { diff --git a/x/http.go b/x/http.go index 4d1d3aa56e5..daf47754a1f 100644 --- a/x/http.go +++ b/x/http.go @@ -51,3 +51,22 @@ func RequestURL(r *http.Request) *url.URL { return &source } + +func NewTransportWithHeader(h http.Header) *TransportWithHeader { + return &TransportWithHeader{ + RoundTripper: http.DefaultTransport, + h: h, + } +} + +type TransportWithHeader struct { + http.RoundTripper + h http.Header +} + +func (ct *TransportWithHeader) RoundTrip(req *http.Request) (*http.Response, error) { + for k := range ct.h { + req.Header.Set(k, ct.h.Get(k)) + } + return ct.RoundTripper.RoundTrip(req) +}