From 98140f2fd43ccd889e2635e4f3e7582b92fe96ab Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Wed, 25 Sep 2024 07:37:35 +0000 Subject: [PATCH 001/158] fix: return error if invalid UUID is supplied to ids filter (#4116) --- identity/handler.go | 30 ++++++++++++++++++++---------- identity/handler_test.go | 9 +++++++-- identity/pool.go | 2 +- identity/test/pool.go | 5 +---- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/identity/handler.go b/identity/handler.go index c4f90cec7619..58578d56e72c 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -11,6 +11,8 @@ import ( "strings" "time" + "github.com/gofrs/uuid" + "github.com/ory/x/crdbx" "github.com/ory/x/pagination/keysetpagination" @@ -193,6 +195,7 @@ type listIdentitiesParameters struct { // default: errorGeneric func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { includeCredentials := r.URL.Query()["include_credential"] + var err error var declassify []CredentialsType for _, v := range includeCredentials { tc, ok := ParseCredentialsType(v) @@ -204,17 +207,24 @@ func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Para } } - var ( - err error - params = ListIdentityParameters{ - Expand: ExpandDefault, - IdsFilter: r.URL.Query()["ids"], - CredentialsIdentifier: r.URL.Query().Get("credentials_identifier"), - CredentialsIdentifierSimilar: r.URL.Query().Get("preview_credentials_identifier_similar"), - ConsistencyLevel: crdbx.ConsistencyLevelFromRequest(r), - DeclassifyCredentials: declassify, + var idsFilter []uuid.UUID + for _, v := range r.URL.Query()["ids"] { + id, err := uuid.FromString(v) + if err != nil { + h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `ids`.", v))) + return } - ) + idsFilter = append(idsFilter, id) + } + + params := ListIdentityParameters{ + Expand: ExpandDefault, + IdsFilter: idsFilter, + CredentialsIdentifier: r.URL.Query().Get("credentials_identifier"), + CredentialsIdentifierSimilar: r.URL.Query().Get("preview_credentials_identifier_similar"), + ConsistencyLevel: crdbx.ConsistencyLevelFromRequest(r), + DeclassifyCredentials: declassify, + } if params.CredentialsIdentifier != "" && params.CredentialsIdentifierSimilar != "" { h.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithReason("Cannot pass both credentials_identifier and preview_credentials_identifier_similar.")) return diff --git a/identity/handler_test.go b/identity/handler_test.go index 33d6a46adf87..12bbde6cfa80 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -372,18 +372,23 @@ func TestHandler(t *testing.T) { require.Equal(t, len(ids), identitiesAmount) }) - t.Run("case= list few identities", func(t *testing.T) { + t.Run("case=list few identities", func(t *testing.T) { url := "/identities?ids=" + ids[0].String() for i := 1; i < listAmount; i++ { url += "&ids=" + ids[i].String() } - res := get(t, adminTS, url, 200) + res := get(t, adminTS, url, http.StatusOK) identities := res.Array() require.Equal(t, len(identities), listAmount) }) }) + t.Run("case=malformed ids should return an error", func(t *testing.T) { + res := get(t, adminTS, "/identities?ids=not-a-uuid", http.StatusBadRequest) + assert.Contains(t, res.Get("error.reason").String(), "Invalid UUID value `not-a-uuid` for parameter `ids`.", "%s", res.Raw) + }) + t.Run("suite=create and update", func(t *testing.T) { var i identity.Identity createOidcIdentity := func(t *testing.T, identifier, accessToken, refreshToken, idToken string, encrypt bool) string { diff --git a/identity/pool.go b/identity/pool.go index 86559f0a8a3f..30a7308245b4 100644 --- a/identity/pool.go +++ b/identity/pool.go @@ -18,7 +18,7 @@ import ( type ( ListIdentityParameters struct { Expand Expandables - IdsFilter []string + IdsFilter []uuid.UUID CredentialsIdentifier string CredentialsIdentifierSimilar string DeclassifyCredentials []CredentialsType diff --git a/identity/test/pool.go b/identity/test/pool.go index 20aa96462eb2..4d9f4c440910 100644 --- a/identity/test/pool.go +++ b/identity/test/pool.go @@ -676,10 +676,7 @@ func TestPool(ctx context.Context, p persistence.Persister, m *identity.Manager, }) t.Run("list some using ids filter", func(t *testing.T) { - var filterIds []string - for _, id := range createdIDs[:2] { - filterIds = append(filterIds, id.String()) - } + filterIds := createdIDs[:2] is, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{Expand: identity.ExpandDefault, IdsFilter: filterIds}) require.NoError(t, err) From eb97243d6499e2d9f2338a2ce3f5e39579d19086 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Wed, 25 Sep 2024 09:47:53 +0200 Subject: [PATCH 002/158] feat: enable new-style OIDC state generation (#4121) --- ...dc_credentials-case=should_fail_login.json | 254 ------------------ ...entials-case=should_fail_registration.json | 254 ------------------ ...egistration_id_first_strategy_enabled.json | 254 ------------------ ...rd_credentials-case=should_fail_login.json | 254 ------------------ ...entials-case=should_fail_registration.json | 254 ------------------ ...egistration_id_first_strategy_enabled.json | 254 ------------------ ...dc_credentials-case=should_fail_login.json | 83 ------ ...entials-case=should_fail_registration.json | 83 ------ ...egistration_id_first_strategy_enabled.json | 83 ------ ...rd_credentials-case=should_fail_login.json | 100 ------- ...entials-case=should_fail_registration.json | 100 ------- ...egistration_id_first_strategy_enabled.json | 100 ------- ...State-method=TestPopulateSignUpMethod.json | 202 -------------- selfservice/strategy/oidc/pkce_test.go | 1 - selfservice/strategy/oidc/state.go | 55 ---- selfservice/strategy/oidc/state_test.go | 37 +-- selfservice/strategy/oidc/strategy.go | 2 +- selfservice/strategy/oidc/strategy_test.go | 31 --- 18 files changed, 13 insertions(+), 2388 deletions(-) delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json delete mode 100644 selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-method=TestPopulateSignUpMethod.json diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json deleted file mode 100644 index cfcda57ec4e1..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "autoPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with autoPKCE", - "type": "info", - "context": { - "provider": "autoPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "claimsViaUserInfo", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with claimsViaUserInfo", - "type": "info", - "context": { - "provider": "claimsViaUserInfo" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "forcePKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with forcePKCE", - "type": "info", - "context": { - "provider": "forcePKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "invalid-issuer", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with invalid-issuer", - "type": "info", - "context": { - "provider": "invalid-issuer" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "neverPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with neverPKCE", - "type": "info", - "context": { - "provider": "neverPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid2", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with valid2", - "type": "info", - "context": { - "provider": "valid2" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-oidc-strategy-lh-false@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-oidc-strategy-lh-false@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": [], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "duplicate_identifier": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json deleted file mode 100644 index cfcda57ec4e1..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "autoPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with autoPKCE", - "type": "info", - "context": { - "provider": "autoPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "claimsViaUserInfo", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with claimsViaUserInfo", - "type": "info", - "context": { - "provider": "claimsViaUserInfo" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "forcePKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with forcePKCE", - "type": "info", - "context": { - "provider": "forcePKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "invalid-issuer", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with invalid-issuer", - "type": "info", - "context": { - "provider": "invalid-issuer" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "neverPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with neverPKCE", - "type": "info", - "context": { - "provider": "neverPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid2", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with valid2", - "type": "info", - "context": { - "provider": "valid2" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-oidc-strategy-lh-false@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-oidc-strategy-lh-false@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": [], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "duplicate_identifier": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json deleted file mode 100644 index cfcda57ec4e1..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "autoPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with autoPKCE", - "type": "info", - "context": { - "provider": "autoPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "claimsViaUserInfo", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with claimsViaUserInfo", - "type": "info", - "context": { - "provider": "claimsViaUserInfo" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "forcePKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with forcePKCE", - "type": "info", - "context": { - "provider": "forcePKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "invalid-issuer", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with invalid-issuer", - "type": "info", - "context": { - "provider": "invalid-issuer" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "neverPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with neverPKCE", - "type": "info", - "context": { - "provider": "neverPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid2", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with valid2", - "type": "info", - "context": { - "provider": "valid2" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-oidc-strategy-lh-false@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-oidc-strategy-lh-false@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": [], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "duplicate_identifier": "email-exist-with-oidc-strategy-lh-false@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json deleted file mode 100644 index 5fbb69e1fcc6..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "autoPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with autoPKCE", - "type": "info", - "context": { - "provider": "autoPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "claimsViaUserInfo", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with claimsViaUserInfo", - "type": "info", - "context": { - "provider": "claimsViaUserInfo" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "forcePKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with forcePKCE", - "type": "info", - "context": { - "provider": "forcePKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "invalid-issuer", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with invalid-issuer", - "type": "info", - "context": { - "provider": "invalid-issuer" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "neverPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with neverPKCE", - "type": "info", - "context": { - "provider": "neverPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid2", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with valid2", - "type": "info", - "context": { - "provider": "valid2" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-password-strategy-lh-false@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-password-strategy-lh-false@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-password-strategy-lh-false@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": [], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-password-strategy-lh-false@ory.sh", - "duplicate_identifier": "email-exist-with-password-strategy-lh-false@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json deleted file mode 100644 index 5fbb69e1fcc6..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "autoPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with autoPKCE", - "type": "info", - "context": { - "provider": "autoPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "claimsViaUserInfo", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with claimsViaUserInfo", - "type": "info", - "context": { - "provider": "claimsViaUserInfo" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "forcePKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with forcePKCE", - "type": "info", - "context": { - "provider": "forcePKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "invalid-issuer", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with invalid-issuer", - "type": "info", - "context": { - "provider": "invalid-issuer" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "neverPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with neverPKCE", - "type": "info", - "context": { - "provider": "neverPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid2", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with valid2", - "type": "info", - "context": { - "provider": "valid2" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-password-strategy-lh-false@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-password-strategy-lh-false@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-password-strategy-lh-false@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": [], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-password-strategy-lh-false@ory.sh", - "duplicate_identifier": "email-exist-with-password-strategy-lh-false@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json deleted file mode 100644 index 5fbb69e1fcc6..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "autoPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with autoPKCE", - "type": "info", - "context": { - "provider": "autoPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "claimsViaUserInfo", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with claimsViaUserInfo", - "type": "info", - "context": { - "provider": "claimsViaUserInfo" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "forcePKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with forcePKCE", - "type": "info", - "context": { - "provider": "forcePKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "invalid-issuer", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with invalid-issuer", - "type": "info", - "context": { - "provider": "invalid-issuer" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "neverPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with neverPKCE", - "type": "info", - "context": { - "provider": "neverPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid2", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with valid2", - "type": "info", - "context": { - "provider": "valid2" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-password-strategy-lh-false@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-password-strategy-lh-false@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-password-strategy-lh-false@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": [], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-password-strategy-lh-false@ory.sh", - "duplicate_identifier": "email-exist-with-password-strategy-lh-false@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json deleted file mode 100644 index 77bef5d097ae..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-oidc-strategy-lh-true@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-oidc-strategy-lh-true@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": ["oidc"], - "available_providers": ["secondProvider"], - "duplicateIdentifier": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "duplicate_identifier": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json deleted file mode 100644 index 77bef5d097ae..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-oidc-strategy-lh-true@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-oidc-strategy-lh-true@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": ["oidc"], - "available_providers": ["secondProvider"], - "duplicateIdentifier": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "duplicate_identifier": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json deleted file mode 100644 index 77bef5d097ae..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010018, - "text": "Confirm with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-oidc-strategy-lh-true@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-oidc-strategy-lh-true@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": ["oidc"], - "available_providers": ["secondProvider"], - "duplicateIdentifier": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "duplicate_identifier": "email-exist-with-oidc-strategy-lh-true@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json deleted file mode 100644 index 93317c6e479a..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-password-strategy-lh-true@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-password-strategy-lh-true@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-password-strategy-lh-true@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": ["password"], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-password-strategy-lh-true@ory.sh", - "duplicate_identifier": "email-exist-with-password-strategy-lh-true@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json deleted file mode 100644 index 93317c6e479a..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-password-strategy-lh-true@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-password-strategy-lh-true@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-password-strategy-lh-true@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": ["password"], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-password-strategy-lh-true@ory.sh", - "duplicate_identifier": "email-exist-with-password-strategy-lh-true@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json deleted file mode 100644 index 93317c6e479a..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "organization_id": null, - "type": "browser", - "active": "oidc", - "ui": { - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "default", - "attributes": { - "name": "identifier", - "type": "hidden", - "value": "email-exist-with-password-strategy-lh-true@ory.sh", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070004, - "text": "ID", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "password", - "type": "password", - "required": true, - "autocomplete": "current-password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1070001, - "text": "Password", - "type": "info" - } - } - }, - { - "type": "input", - "group": "password", - "attributes": { - "name": "method", - "type": "submit", - "value": "password", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1010022, - "text": "Sign in with password", - "type": "info" - } - } - } - ], - "messages": [ - { - "id": 1010016, - "text": "You tried to sign in with \"email-exist-with-password-strategy-lh-true@ory.sh\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"email-exist-with-password-strategy-lh-true@ory.sh\" at \"generic\" as another way to sign in.", - "type": "info", - "context": { - "available_credential_types": ["password"], - "available_providers": [], - "duplicateIdentifier": "email-exist-with-password-strategy-lh-true@ory.sh", - "duplicate_identifier": "email-exist-with-password-strategy-lh-true@ory.sh", - "provider": "generic" - } - } - ] - }, - "refresh": false, - "requested_aal": "aal1", - "state": "choose_method" -} - diff --git a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-method=TestPopulateSignUpMethod.json b/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-method=TestPopulateSignUpMethod.json deleted file mode 100644 index 2177c514d3fd..000000000000 --- a/selfservice/strategy/oidc/.snapshots/TestStrategy-newStyleState-method=TestPopulateSignUpMethod.json +++ /dev/null @@ -1,202 +0,0 @@ -{ - "method": "POST", - "nodes": [ - { - "type": "input", - "group": "default", - "attributes": { - "name": "csrf_token", - "type": "hidden", - "required": true, - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": {} - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with valid", - "type": "info", - "context": { - "provider": "valid", - "provider_id": "valid" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "valid2", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with valid2", - "type": "info", - "context": { - "provider": "valid2", - "provider_id": "valid2" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "secondProvider", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with secondProvider", - "type": "info", - "context": { - "provider": "secondProvider", - "provider_id": "secondProvider" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "claimsViaUserInfo", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with claimsViaUserInfo", - "type": "info", - "context": { - "provider": "claimsViaUserInfo", - "provider_id": "claimsViaUserInfo" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "neverPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with neverPKCE", - "type": "info", - "context": { - "provider": "neverPKCE", - "provider_id": "neverPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "autoPKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with autoPKCE", - "type": "info", - "context": { - "provider": "autoPKCE", - "provider_id": "autoPKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "forcePKCE", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with forcePKCE", - "type": "info", - "context": { - "provider": "forcePKCE", - "provider_id": "forcePKCE" - } - } - } - }, - { - "type": "input", - "group": "oidc", - "attributes": { - "name": "provider", - "type": "submit", - "value": "invalid-issuer", - "disabled": false, - "node_type": "input" - }, - "messages": [], - "meta": { - "label": { - "id": 1040002, - "text": "Sign up with invalid-issuer", - "type": "info", - "context": { - "provider": "invalid-issuer", - "provider_id": "invalid-issuer" - } - } - } - } - ] -} diff --git a/selfservice/strategy/oidc/pkce_test.go b/selfservice/strategy/oidc/pkce_test.go index 7b42dfd2a8eb..24ef31c5d251 100644 --- a/selfservice/strategy/oidc/pkce_test.go +++ b/selfservice/strategy/oidc/pkce_test.go @@ -31,7 +31,6 @@ func TestPKCESupport(t *testing.T) { conf, reg := internal.NewFastRegistryWithMocks(t) _ = conf strat := oidc.NewStrategy(reg) - oidc.TestHookEnableNewStyleState(t) for _, tc := range []struct { c *oidc.Configuration diff --git a/selfservice/strategy/oidc/state.go b/selfservice/strategy/oidc/state.go index 72a52c24ad2e..ce8cb17bc653 100644 --- a/selfservice/strategy/oidc/state.go +++ b/selfservice/strategy/oidc/state.go @@ -4,16 +4,11 @@ package oidc import ( - "bytes" "context" "crypto/sha512" "crypto/subtle" - "encoding/base64" - "fmt" - "testing" "github.com/gofrs/uuid" - "github.com/pkg/errors" "golang.org/x/oauth2" "google.golang.org/protobuf/proto" @@ -43,26 +38,6 @@ func DecryptState(ctx context.Context, c cipher.Cipher, ciphertext string) (*oid return &state, nil } -func legacyString(s *oidcv1.State) string { - flowID := uuid.FromBytesOrNil(s.GetFlowId()) - code := s.GetSessionTokenExchangeCodeSha512() - return base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", flowID.String(), code))) -} - -var newStyleState = false - -func TestHookEnableNewStyleState(t *testing.T) { - prev := newStyleState - newStyleState = true - t.Cleanup(func() { - newStyleState = prev - }) -} - -func TestHookNewStyleStateEnabled(*testing.T) bool { - return newStyleState -} - func (s *Strategy) GenerateState(ctx context.Context, p Provider, flowID uuid.UUID) (stateParam string, pkce []oauth2.AuthCodeOption, err error) { state := oidcv1.State{ FlowId: flowID.Bytes(), @@ -75,13 +50,6 @@ func (s *Strategy) GenerateState(ctx context.Context, p Provider, flowID uuid.UU state.SessionTokenExchangeCodeSha512 = sum[:] } - // TODO: compatibility: remove later - if !newStyleState { - state.PkceVerifier = "" - return legacyString(&state), nil, nil // compat: disable later - } - // END TODO - param, err := encryptState(ctx, s.d.Cipher(ctx), &state) if err != nil { return "", nil, herodot.ErrInternalServerError.WithReason("Unable to encrypt state").WithWrap(err) @@ -93,26 +61,3 @@ func codeMatches(s *oidcv1.State, code string) bool { sum := sha512.Sum512([]byte(code)) return subtle.ConstantTimeCompare(s.GetSessionTokenExchangeCodeSha512(), sum[:]) == 1 } - -func ParseStateCompatiblity(ctx context.Context, c cipher.Cipher, s string) (*oidcv1.State, error) { - // new-style: encrypted - state, err := DecryptState(ctx, c, s) - if err == nil { - return state, nil - } - // old-style: unencrypted - raw, err := base64.RawURLEncoding.DecodeString(s) - if err != nil { - return nil, err - } - if id, data, ok := bytes.Cut(raw, []byte(":")); !ok { - return nil, errors.New("state has invalid format (1)") - } else if flowID, err := uuid.FromString(string(id)); err != nil { - return nil, errors.New("state has invalid format (2)") - } else { - return &oidcv1.State{ - FlowId: flowID.Bytes(), - SessionTokenExchangeCodeSha512: data, - }, nil - } -} diff --git a/selfservice/strategy/oidc/state_test.go b/selfservice/strategy/oidc/state_test.go index 2d21eaa4aef9..bf0366571a6b 100644 --- a/selfservice/strategy/oidc/state_test.go +++ b/selfservice/strategy/oidc/state_test.go @@ -25,31 +25,18 @@ func TestGenerateState(t *testing.T) { _, ok := ciph.(*cipher.Noop) require.False(t, ok) - var expectProvider string - assertions := func(t *testing.T) { - flowID := x.NewUUID() - - stateParam, pkce, err := strat.GenerateState(ctx, &testProvider{}, flowID) - require.NoError(t, err) - require.NotEmpty(t, stateParam) - assert.Empty(t, pkce) - - state, err := oidc.ParseStateCompatiblity(ctx, ciph, stateParam) - require.NoError(t, err) - assert.Equal(t, flowID.Bytes(), state.FlowId) - assert.Empty(t, oidc.PKCEVerifier(state)) - assert.Equal(t, expectProvider, state.ProviderId) - } - - t.Run("case=old-style", func(t *testing.T) { - expectProvider = "" - assertions(t) - }) - t.Run("case=new-style", func(t *testing.T) { - oidc.TestHookEnableNewStyleState(t) - expectProvider = "test-provider" - assertions(t) - }) + flowID := x.NewUUID() + + stateParam, pkce, err := strat.GenerateState(ctx, &testProvider{}, flowID) + require.NoError(t, err) + require.NotEmpty(t, stateParam) + assert.Empty(t, pkce) + + state, err := oidc.DecryptState(ctx, ciph, stateParam) + require.NoError(t, err) + assert.Equal(t, flowID.Bytes(), state.FlowId) + assert.Empty(t, oidc.PKCEVerifier(state)) + assert.Equal(t, "test-provider", state.ProviderId) } type testProvider struct{} diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index 410b19499341..6539d2cfd094 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -268,7 +268,7 @@ func (s *Strategy) ValidateCallback(w http.ResponseWriter, r *http.Request, ps h if stateParam == "" { return nil, nil, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the OpenID Provider did not return the state query parameter.`)) } - state, err := ParseStateCompatiblity(r.Context(), s.d.Cipher(r.Context()), stateParam) + state, err := DecryptState(r.Context(), s.d.Cipher(r.Context()), stateParam) if err != nil { return nil, nil, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the state parameter is invalid.`)) } diff --git a/selfservice/strategy/oidc/strategy_test.go b/selfservice/strategy/oidc/strategy_test.go index 0fdaecb5a1f6..25faf0e91a55 100644 --- a/selfservice/strategy/oidc/strategy_test.go +++ b/selfservice/strategy/oidc/strategy_test.go @@ -61,15 +61,6 @@ import ( ) func TestStrategy(t *testing.T) { - t.Run("newStyleState", func(t *testing.T) { - oidc.TestHookEnableNewStyleState(t) - testStrategy(t) - }) - - testStrategy(t) -} - -func testStrategy(t *testing.T) { ctx := context.Background() if testing.Short() { t.Skip() @@ -492,9 +483,6 @@ func testStrategy(t *testing.T) { } t.Run("case=force PKCE", func(t *testing.T) { - if !oidc.TestHookNewStyleStateEnabled(t) { - t.Skip("This test is not compatible with the old state handling") - } r := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute) action := assertFormValues(t, r.ID, "forcePKCE") subject = "force-pkce@ory.sh" @@ -515,9 +503,6 @@ func testStrategy(t *testing.T) { assert.Equal(t, "forcePKCE", gjson.GetBytes(body, "authentication_methods.0.provider").String(), "%s", body) }) t.Run("case=force PKCE, invalid verifier", func(t *testing.T) { - if !oidc.TestHookNewStyleStateEnabled(t) { - t.Skip("This test is not compatible with the old state handling") - } r := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute) action := assertFormValues(t, r.ID, "forcePKCE") subject = "force-pkce@ory.sh" @@ -539,9 +524,6 @@ func testStrategy(t *testing.T) { assert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()) }) t.Run("case=force PKCE, code challenge params removed from initial redirect", func(t *testing.T) { - if !oidc.TestHookNewStyleStateEnabled(t) { - t.Skip("This test is not compatible with the old state handling") - } r := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute) action := assertFormValues(t, r.ID, "forcePKCE") subject = "force-pkce@ory.sh" @@ -564,9 +546,6 @@ func testStrategy(t *testing.T) { assert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()) }) t.Run("case=PKCE prevents authorization code injection attacks", func(t *testing.T) { - if !oidc.TestHookNewStyleStateEnabled(t) { - t.Skip("This test is not compatible with the old state handling") - } r := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute) action := assertFormValues(t, r.ID, "forcePKCE") subject = "attacker@ory.sh" @@ -605,9 +584,6 @@ func testStrategy(t *testing.T) { assertSystemErrorWithMessage(t, res, body, http.StatusInternalServerError, "The PKCE code challenge did not match the code verifier.") }) t.Run("case=confused providers are detected", func(t *testing.T) { - if !oidc.TestHookNewStyleStateEnabled(t) { - t.Skip("This test is not compatible with the old state handling") - } r := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute) action := assertFormValues(t, r.ID, "valid") subject = "attacker@ory.sh" @@ -628,9 +604,6 @@ func testStrategy(t *testing.T) { assertSystemErrorWithReason(t, res, body, http.StatusBadRequest, "provider mismatch between internal state and URL") }) t.Run("case=automatic PKCE", func(t *testing.T) { - if !oidc.TestHookNewStyleStateEnabled(t) { - t.Skip("This test is not compatible with the old state handling") - } r := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute) action := assertFormValues(t, r.ID, "autoPKCE") subject = "auto-pkce@ory.sh" @@ -1241,10 +1214,6 @@ func testStrategy(t *testing.T) { {name: "auto-pkce", provider: "autoPKCE"}, {name: "force-pkce", provider: "forcePKCE"}, } { - if !oidc.TestHookNewStyleStateEnabled(t) && tc.name == "force-pkce" { - t.Log("Skipping test because old state handling is enabled") - continue - } subject = fmt.Sprintf("incomplete-data@%s.ory.sh", tc.name) scope = []string{"openid"} claims = idTokenClaims{} From 78bc473d697ffc77397242b354eb12095e696c7a Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 25 Sep 2024 08:40:39 +0000 Subject: [PATCH 003/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 822a58608bb1..df98b220094c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-09-18)](#2024-09-18) +- [ (2024-09-25)](#2024-09-25) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Documentation](#documentation) @@ -331,7 +331,7 @@ -# [](https://github.com/ory/kratos/compare/v1.2.0...v) (2024-09-18) +# [](https://github.com/ory/kratos/compare/v1.2.0...v) (2024-09-25) ## Breaking Changes @@ -468,6 +468,9 @@ body in the future. Instead of re-fetching the credentials later (expensive), we load them only once. +- Return error if invalid UUID is supplied to ids filter + ([#4116](https://github.com/ory/kratos/issues/4116)) + ([98140f2](https://github.com/ory/kratos/commit/98140f2fd43ccd889e2635e4f3e7582b92fe96ab)) - **security:** Code credential does not respect `highest_available` setting ([b0111d4](https://github.com/ory/kratos/commit/b0111d4bd561d0f0e2f5883f30fac36fcf7135d5)): @@ -597,6 +600,9 @@ body in the future. - Emit events in identity persister ([#4107](https://github.com/ory/kratos/issues/4107)) ([20156f6](https://github.com/ory/kratos/commit/20156f651f2faa0a79842de8d2fb4a09ee7094c1)) +- Enable new-style OIDC state generation + ([#4121](https://github.com/ory/kratos/issues/4121)) + ([eb97243](https://github.com/ory/kratos/commit/eb97243d6499e2d9f2338a2ce3f5e39579d19086)) - Identifier first auth ([1bdc19a](https://github.com/ory/kratos/commit/1bdc19ae3e1a3df38234cb892f65de4a2c95f041)) - Identifier first login for all first factor login methods From aa7f958e3267272c2110f8524c8c24e9e404609c Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:27:05 +0200 Subject: [PATCH 004/158] chore: upgrade goreleaser to v2 (#4123) --- .goreleaser.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index b45adb94eceb..dd9755c68390 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,6 +1,8 @@ +version: 2 + includes: - from_url: - url: https://raw.githubusercontent.com/ory/xgoreleaser/master/build.tmpl.yml + url: https://raw.githubusercontent.com/ory/xgoreleaser/acbadd5d1b947b046fdd0a534b132509eba8e3c8/build.tmpl.yml variables: brew_name: kratos From 72aae5b625ca15552a81bcf956c96994fb716198 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:36:58 +0200 Subject: [PATCH 005/158] autogen: pin v1.3.0-pre.0 release commit --- .schemastore/config.schema.json | 175 +++++++++++++++++++++++++++++--- 1 file changed, 160 insertions(+), 15 deletions(-) diff --git a/.schemastore/config.schema.json b/.schemastore/config.schema.json index 51e1783e9757..4b815e53d0f8 100644 --- a/.schemastore/config.schema.json +++ b/.schemastore/config.schema.json @@ -567,6 +567,13 @@ "enum": ["id_token", "userinfo"], "default": "id_token", "examples": ["id_token", "userinfo"] + }, + "pkce": { + "title": "Proof Key for Code Exchange", + "description": "PKCE controls if the OpenID Connect OAuth2 flow should use PKCE (Proof Key for Code Exchange). IMPORTANT: If you set this to `force`, you must whitelist a different return URL for your OAuth2 client in the provider's configuration. Instead of /self-service/methods/oidc/callback/, you must use /self-service/methods/oidc/callback", + "type": "string", + "enum": ["auto", "never", "force"], + "default": "auto" } }, "additionalProperties": false, @@ -1297,6 +1304,13 @@ "default": "1h", "examples": ["1h", "1m", "1s"] }, + "style": { + "title": "Login Flow Style", + "description": "The style of the login flow. If set to `unified` the login flow will be a one-step process. If set to `identifier_first` (experimental!) the login flow will first ask for the identifier and then the credentials.", + "type": "string", + "enum": ["unified", "identifier_first"], + "default": "unified" + }, "before": { "$ref": "#/definitions/selfServiceBeforeLogin" }, @@ -1421,6 +1435,48 @@ "type": "object", "additionalProperties": false, "properties": { + "b2b": { + "title": "Single Sign-On for B2B", + "description": "Single Sign-On for B2B allows your customers to bring their own (workforce) identity server (e.g. OneLogin). This feature is not available in the open source licensed code.", + "type": "object", + "properties": { + "config": { + "type": "object", + "additionalProperties": false, + "properties": { + "organizations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the organization.", + "format": "uuid", + "examples": ["00000000-0000-0000-0000-000000000000"] + }, + "label": { + "type": "string", + "description": "The label of the organization.", + "examples": ["ACME SSO"] + }, + "domains": { + "type": "array", + "items": { + "type": "string", + "format": "hostname", + "examples": ["my-app.com"], + "description": "If this domain matches the email's domain, this provider is shown." + } + } + } + } + } + } + } + }, + "additionalProperties": false + }, "profile": { "type": "object", "additionalProperties": false, @@ -1464,24 +1520,36 @@ }, "code": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, "anyOf": [ { "properties": { - "passwordless_enabled": { "const": true }, - "mfa_enabled": { "const": false } + "passwordless_enabled": { + "const": true + }, + "mfa_enabled": { + "const": false + } } }, { "properties": { - "mfa_enabled": { "const": true }, - "passwordless_enabled": { "const": false } + "mfa_enabled": { + "const": true + }, + "passwordless_enabled": { + "const": false + } } }, { "properties": { - "mfa_enabled": { "const": false }, - "passwordless_enabled": { "const": false } + "mfa_enabled": { + "const": false + }, + "passwordless_enabled": { + "const": false + } } } ], @@ -1497,12 +1565,6 @@ "title": "Enables login flows code method to fulfil MFA requests", "default": false }, - "passwordless_login_fallback_enabled": { - "type": "boolean", - "title": "Passwordless Login Fallback Enabled", - "description": "This setting allows the code method to always login a user with code if they have registered with another authentication method such as password or social sign in.", - "default": false - }, "enabled": { "type": "boolean", "title": "Enables Code Method", @@ -1519,6 +1581,13 @@ "pattern": "^([0-9]+(ns|us|ms|s|m|h))+$", "default": "1h", "examples": ["1h", "1m", "1s"] + }, + "missing_credential_fallback_enabled": { + "type": "boolean", + "title": "Enable Code OTP as a Fallback", + "description": "Enabling this allows users to sign in with the code method, even if their identity schema or their credentials are not set up to use the code method. If enabled, a verified address (such as an email) will be used to send the code to the user. Use with caution and only if actually needed.", + + "default": false } } } @@ -1576,6 +1645,61 @@ "description": "If set to false the password validation does not check for similarity between the password and the user identifier.", "type": "boolean", "default": true + }, + "migrate_hook": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "title": "Enable Password Migration", + "description": "If set to true will enable password migration.", + "default": false + }, + "config": { + "type": "object", + "additionalProperties": false, + "properties": { + "url": { + "type": "string", + "description": "The URL the password migration hook should call", + "format": "uri" + }, + "method": { + "type": "string", + "description": "The HTTP method to use (GET, POST, etc).", + "const": "POST", + "default": "POST" + }, + "headers": { + "type": "object", + "description": "The HTTP headers that must be applied to the password migration hook.", + "additionalProperties": { + "type": "string" + } + }, + "emit_analytics_event": { + "type": "boolean", + "default": true, + "description": "Emit tracing events for this hook on delivery or error" + }, + "auth": { + "type": "object", + "title": "Auth mechanisms", + "description": "Define which auth mechanism the Web-Hook should use", + "oneOf": [ + { + "$ref": "#/definitions/webHookAuthApiKeyProperties" + }, + { + "$ref": "#/definitions/webHookAuthBasicAuthProperties" + } + ] + }, + "additionalProperties": false + } + } + } } }, "additionalProperties": false @@ -2412,7 +2536,7 @@ "additionalProperties": false }, "tracing": { - "$ref": "https://raw.githubusercontent.com/ory/x/v0.0.623/otelx/config.schema.json" + "$ref": "https://raw.githubusercontent.com/ory/x/v0.0.655/otelx/config.schema.json" }, "log": { "title": "Log", @@ -2764,6 +2888,21 @@ } } }, + "security": { + "type": "object", + "properties": { + "account_enumeration": { + "type": "object", + "properties": { + "mitigate": { + "type": "boolean", + "default": false, + "description": "Mitigate account enumeration by making it harder to figure out if an identifier (email, phone number) exists or not. Enabling this setting degrades user experience. This setting does not mitigate all possible attack vectors yet." + } + } + } + } + }, "version": { "title": "The kratos version this config is written for.", "description": "SemVer according to https://semver.org/ prefixed with `v` as in our releases.", @@ -2853,13 +2992,19 @@ "title": "Enable new flow transitions using `continue_with` items", "description": "If enabled allows new flow transitions using `continue_with` items.", "default": false + }, + "faster_session_extend": { + "type": "boolean", + "title": "Enable faster session extension", + "description": "If enabled allows faster session extension by skipping the session lookup. Disabling this feature will be deprecated in the future.", + "default": false } }, "additionalProperties": false }, "organizations": { "title": "Organizations", - "description": "Secifies which organizations are available. Only effective in the Ory Network.", + "description": "Please use selfservice.methods.b2b instead. This key will be removed. Only effective in the Ory Network.", "type": "array", "default": [] }, From 4f2c8542ab04b88c7112d7b564d91bcfd8f5791a Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 25 Sep 2024 12:18:09 +0200 Subject: [PATCH 006/158] test: additional code credential test case (#4122) --- ...estSchemaExtensionCredentials-case=13.json | 14 ++++++ identity/extension_credentials_test.go | 10 +++++ .../extension/credentials/email.schema.json | 44 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 identity/.snapshots/TestSchemaExtensionCredentials-case=13.json create mode 100644 identity/stub/extension/credentials/email.schema.json diff --git a/identity/.snapshots/TestSchemaExtensionCredentials-case=13.json b/identity/.snapshots/TestSchemaExtensionCredentials-case=13.json new file mode 100644 index 000000000000..0a7e7320cbaa --- /dev/null +++ b/identity/.snapshots/TestSchemaExtensionCredentials-case=13.json @@ -0,0 +1,14 @@ +{ + "type": "code", + "config": { + "addresses": [ + { + "channel": "email", + "address": "bar@ory.sh" + } + ] + }, + "version": 0, + "created_at": "0001-01-01T00:00:00Z", + "updated_at": "0001-01-01T00:00:00Z" +} diff --git a/identity/extension_credentials_test.go b/identity/extension_credentials_test.go index 990037146333..25c1c827303b 100644 --- a/identity/extension_credentials_test.go +++ b/identity/extension_credentials_test.go @@ -141,6 +141,16 @@ func TestSchemaExtensionCredentials(t *testing.T) { }, ct: identity.CredentialsTypeCodeAuth, }, + { + doc: `{"email":"bar@ory.sh"}`, + schema: "file://./stub/extension/credentials/email.schema.json", + expectedIdentifiers: []string{"bar@ory.sh"}, + existing: &identity.Credentials{ + Identifiers: []string{"foo@ory.sh"}, + Config: sqlxx.JSONRawMessage(`{"addresses":[{"channel":"email","address":"foo@ory.sh"}]}`), + }, + ct: identity.CredentialsTypeCodeAuth, + }, } { t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) { c := jsonschema.NewCompiler() diff --git a/identity/stub/extension/credentials/email.schema.json b/identity/stub/extension/credentials/email.schema.json new file mode 100644 index 000000000000..848bfe54c952 --- /dev/null +++ b/identity/stub/extension/credentials/email.schema.json @@ -0,0 +1,44 @@ +{ + "$id": "https://schemas.ory.sh/presets/kratos/identity.email.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Person", + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "title": "E-Mail", + "ory.sh/kratos": { + "credentials": { + "password": { + "identifier": true + }, + "webauthn": { + "identifier": true + }, + "totp": { + "account_name": true + }, + "code": { + "identifier": true, + "via": "email" + }, + "passkey": { + "display_name": true + } + }, + "recovery": { + "via": "email" + }, + "verification": { + "via": "email" + } + }, + "maxLength": 320 + } + }, + "required": [ + "email" + ], + "additionalProperties": false +} From aa48c6b63531eb75e33ce1bc79dc4fd81d392b8a Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:12:39 +0000 Subject: [PATCH 007/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 148 +++++++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df98b220094c..404dd7ee8b98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,247 +6,250 @@ **Table of Contents** - [ (2024-09-25)](#2024-09-25) + - [Tests](#tests) +- [1.3.0-pre.0 (2024-09-25)](#130-pre0-2024-09-25) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) + - [Code Generation](#code-generation) - [Documentation](#documentation) - [Features](#features) - - [Tests](#tests) + - [Tests](#tests-1) - [Unclassified](#unclassified) - [1.2.0 (2024-06-05)](#120-2024-06-05) - [Breaking Changes](#breaking-changes-1) - [Bug Fixes](#bug-fixes-1) - - [Code Generation](#code-generation) + - [Code Generation](#code-generation-1) - [Documentation](#documentation-1) - [Features](#features-1) - - [Tests](#tests-1) + - [Tests](#tests-2) - [Unclassified](#unclassified-1) - [1.1.0 (2024-02-20)](#110-2024-02-20) - [Breaking Changes](#breaking-changes-2) - [Bug Fixes](#bug-fixes-2) - - [Code Generation](#code-generation-1) + - [Code Generation](#code-generation-2) - [Documentation](#documentation-2) - [Features](#features-2) - [Reverts](#reverts) - - [Tests](#tests-2) + - [Tests](#tests-3) - [Unclassified](#unclassified-2) - [1.0.0 (2023-07-12)](#100-2023-07-12) - [Bug Fixes](#bug-fixes-3) - - [Code Generation](#code-generation-2) + - [Code Generation](#code-generation-3) - [Documentation](#documentation-3) - [Features](#features-3) - - [Tests](#tests-3) + - [Tests](#tests-4) - [Unclassified](#unclassified-3) - [0.13.0 (2023-04-18)](#0130-2023-04-18) - [Breaking Changes](#breaking-changes-3) - [Bug Fixes](#bug-fixes-4) - - [Code Generation](#code-generation-3) + - [Code Generation](#code-generation-4) - [Code Refactoring](#code-refactoring) - [Documentation](#documentation-4) - [Features](#features-4) - - [Tests](#tests-4) + - [Tests](#tests-5) - [Unclassified](#unclassified-4) - [0.11.1 (2023-01-14)](#0111-2023-01-14) - [Breaking Changes](#breaking-changes-4) - [Bug Fixes](#bug-fixes-5) - - [Code Generation](#code-generation-4) + - [Code Generation](#code-generation-5) - [Documentation](#documentation-5) - [Features](#features-5) - - [Tests](#tests-5) + - [Tests](#tests-6) - [0.11.0 (2022-12-02)](#0110-2022-12-02) - - [Code Generation](#code-generation-5) + - [Code Generation](#code-generation-6) - [Features](#features-6) - [0.11.0-alpha.0.pre.2 (2022-11-28)](#0110-alpha0pre2-2022-11-28) - [Breaking Changes](#breaking-changes-5) - [Bug Fixes](#bug-fixes-6) - - [Code Generation](#code-generation-6) + - [Code Generation](#code-generation-7) - [Code Refactoring](#code-refactoring-1) - [Documentation](#documentation-6) - [Features](#features-7) - [Reverts](#reverts-1) - - [Tests](#tests-6) + - [Tests](#tests-7) - [Unclassified](#unclassified-5) - [0.10.1 (2022-06-01)](#0101-2022-06-01) - [Bug Fixes](#bug-fixes-7) - - [Code Generation](#code-generation-7) + - [Code Generation](#code-generation-8) - [0.10.0 (2022-05-30)](#0100-2022-05-30) - [Breaking Changes](#breaking-changes-6) - [Bug Fixes](#bug-fixes-8) - - [Code Generation](#code-generation-8) + - [Code Generation](#code-generation-9) - [Code Refactoring](#code-refactoring-2) - [Documentation](#documentation-7) - [Features](#features-8) - - [Tests](#tests-7) + - [Tests](#tests-8) - [Unclassified](#unclassified-6) - [0.9.0-alpha.3 (2022-03-25)](#090-alpha3-2022-03-25) - [Breaking Changes](#breaking-changes-7) - [Bug Fixes](#bug-fixes-9) - - [Code Generation](#code-generation-9) + - [Code Generation](#code-generation-10) - [Documentation](#documentation-8) - [0.9.0-alpha.2 (2022-03-22)](#090-alpha2-2022-03-22) - [Bug Fixes](#bug-fixes-10) - - [Code Generation](#code-generation-10) + - [Code Generation](#code-generation-11) - [0.9.0-alpha.1 (2022-03-21)](#090-alpha1-2022-03-21) - [Breaking Changes](#breaking-changes-8) - [Bug Fixes](#bug-fixes-11) - - [Code Generation](#code-generation-11) + - [Code Generation](#code-generation-12) - [Code Refactoring](#code-refactoring-3) - [Documentation](#documentation-9) - [Features](#features-9) - - [Tests](#tests-8) + - [Tests](#tests-9) - [Unclassified](#unclassified-7) - [0.8.3-alpha.1.pre.0 (2022-01-21)](#083-alpha1pre0-2022-01-21) - [Breaking Changes](#breaking-changes-9) - [Bug Fixes](#bug-fixes-12) - - [Code Generation](#code-generation-12) + - [Code Generation](#code-generation-13) - [Code Refactoring](#code-refactoring-4) - [Documentation](#documentation-10) - [Features](#features-10) - - [Tests](#tests-9) + - [Tests](#tests-10) - [0.8.2-alpha.1 (2021-12-17)](#082-alpha1-2021-12-17) - [Bug Fixes](#bug-fixes-13) - - [Code Generation](#code-generation-13) + - [Code Generation](#code-generation-14) - [Documentation](#documentation-11) - [0.8.1-alpha.1 (2021-12-13)](#081-alpha1-2021-12-13) - [Bug Fixes](#bug-fixes-14) - - [Code Generation](#code-generation-14) + - [Code Generation](#code-generation-15) - [Documentation](#documentation-12) - [Features](#features-11) - - [Tests](#tests-10) + - [Tests](#tests-11) - [0.8.0-alpha.4.pre.0 (2021-11-09)](#080-alpha4pre0-2021-11-09) - [Breaking Changes](#breaking-changes-10) - [Bug Fixes](#bug-fixes-15) - - [Code Generation](#code-generation-15) + - [Code Generation](#code-generation-16) - [Documentation](#documentation-13) - [Features](#features-12) - - [Tests](#tests-11) + - [Tests](#tests-12) - [0.8.0-alpha.3 (2021-10-28)](#080-alpha3-2021-10-28) - [Bug Fixes](#bug-fixes-16) - - [Code Generation](#code-generation-16) -- [0.8.0-alpha.2 (2021-10-28)](#080-alpha2-2021-10-28) - [Code Generation](#code-generation-17) +- [0.8.0-alpha.2 (2021-10-28)](#080-alpha2-2021-10-28) + - [Code Generation](#code-generation-18) - [0.8.0-alpha.1 (2021-10-27)](#080-alpha1-2021-10-27) - [Breaking Changes](#breaking-changes-11) - [Bug Fixes](#bug-fixes-17) - - [Code Generation](#code-generation-18) + - [Code Generation](#code-generation-19) - [Code Refactoring](#code-refactoring-5) - [Documentation](#documentation-14) - [Features](#features-13) - [Reverts](#reverts-2) - - [Tests](#tests-12) + - [Tests](#tests-13) - [Unclassified](#unclassified-8) - [0.7.6-alpha.1 (2021-09-12)](#076-alpha1-2021-09-12) - - [Code Generation](#code-generation-19) -- [0.7.5-alpha.1 (2021-09-11)](#075-alpha1-2021-09-11) - [Code Generation](#code-generation-20) +- [0.7.5-alpha.1 (2021-09-11)](#075-alpha1-2021-09-11) + - [Code Generation](#code-generation-21) - [0.7.4-alpha.1 (2021-09-09)](#074-alpha1-2021-09-09) - [Bug Fixes](#bug-fixes-18) - - [Code Generation](#code-generation-21) + - [Code Generation](#code-generation-22) - [Documentation](#documentation-15) - [Features](#features-14) - - [Tests](#tests-13) + - [Tests](#tests-14) - [0.7.3-alpha.1 (2021-08-28)](#073-alpha1-2021-08-28) - [Bug Fixes](#bug-fixes-19) - - [Code Generation](#code-generation-22) + - [Code Generation](#code-generation-23) - [Documentation](#documentation-16) - [Features](#features-15) - [0.7.1-alpha.1 (2021-07-22)](#071-alpha1-2021-07-22) - [Bug Fixes](#bug-fixes-20) - - [Code Generation](#code-generation-23) + - [Code Generation](#code-generation-24) - [Documentation](#documentation-17) - - [Tests](#tests-14) + - [Tests](#tests-15) - [0.7.0-alpha.1 (2021-07-13)](#070-alpha1-2021-07-13) - [Breaking Changes](#breaking-changes-12) - [Bug Fixes](#bug-fixes-21) - - [Code Generation](#code-generation-24) + - [Code Generation](#code-generation-25) - [Code Refactoring](#code-refactoring-6) - [Documentation](#documentation-18) - [Features](#features-16) - - [Tests](#tests-15) + - [Tests](#tests-16) - [Unclassified](#unclassified-9) - [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17) - [Breaking Changes](#breaking-changes-13) - [Bug Fixes](#bug-fixes-22) - - [Code Generation](#code-generation-25) + - [Code Generation](#code-generation-26) - [Code Refactoring](#code-refactoring-7) - [0.6.2-alpha.1 (2021-05-14)](#062-alpha1-2021-05-14) - - [Code Generation](#code-generation-26) + - [Code Generation](#code-generation-27) - [Documentation](#documentation-19) - [0.6.1-alpha.1 (2021-05-11)](#061-alpha1-2021-05-11) - - [Code Generation](#code-generation-27) + - [Code Generation](#code-generation-28) - [Features](#features-17) - [0.6.0-alpha.2 (2021-05-07)](#060-alpha2-2021-05-07) - [Bug Fixes](#bug-fixes-23) - - [Code Generation](#code-generation-28) + - [Code Generation](#code-generation-29) - [Features](#features-18) - [0.6.0-alpha.1 (2021-05-05)](#060-alpha1-2021-05-05) - [Breaking Changes](#breaking-changes-14) - [Bug Fixes](#bug-fixes-24) - - [Code Generation](#code-generation-29) + - [Code Generation](#code-generation-30) - [Code Refactoring](#code-refactoring-8) - [Documentation](#documentation-20) - [Features](#features-19) - - [Tests](#tests-16) + - [Tests](#tests-17) - [Unclassified](#unclassified-10) - [0.5.5-alpha.1 (2020-12-09)](#055-alpha1-2020-12-09) - [Bug Fixes](#bug-fixes-25) - - [Code Generation](#code-generation-30) + - [Code Generation](#code-generation-31) - [Documentation](#documentation-21) - [Features](#features-20) - - [Tests](#tests-17) + - [Tests](#tests-18) - [Unclassified](#unclassified-11) - [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11) - [Bug Fixes](#bug-fixes-26) - - [Code Generation](#code-generation-31) + - [Code Generation](#code-generation-32) - [Code Refactoring](#code-refactoring-9) - [Documentation](#documentation-22) - [Features](#features-21) - [0.5.3-alpha.1 (2020-10-27)](#053-alpha1-2020-10-27) - [Bug Fixes](#bug-fixes-27) - - [Code Generation](#code-generation-32) + - [Code Generation](#code-generation-33) - [Documentation](#documentation-23) - [Features](#features-22) - - [Tests](#tests-18) + - [Tests](#tests-19) - [0.5.2-alpha.1 (2020-10-22)](#052-alpha1-2020-10-22) - [Bug Fixes](#bug-fixes-28) - - [Code Generation](#code-generation-33) + - [Code Generation](#code-generation-34) - [Documentation](#documentation-24) - - [Tests](#tests-19) + - [Tests](#tests-20) - [0.5.1-alpha.1 (2020-10-20)](#051-alpha1-2020-10-20) - [Bug Fixes](#bug-fixes-29) - - [Code Generation](#code-generation-34) + - [Code Generation](#code-generation-35) - [Documentation](#documentation-25) - [Features](#features-23) - - [Tests](#tests-20) + - [Tests](#tests-21) - [Unclassified](#unclassified-12) - [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15) - [Breaking Changes](#breaking-changes-15) - [Bug Fixes](#bug-fixes-30) - - [Code Generation](#code-generation-35) + - [Code Generation](#code-generation-36) - [Code Refactoring](#code-refactoring-10) - [Documentation](#documentation-26) - [Features](#features-24) - - [Tests](#tests-21) + - [Tests](#tests-22) - [Unclassified](#unclassified-13) - [0.4.6-alpha.1 (2020-07-13)](#046-alpha1-2020-07-13) - [Bug Fixes](#bug-fixes-31) - - [Code Generation](#code-generation-36) + - [Code Generation](#code-generation-37) - [0.4.5-alpha.1 (2020-07-13)](#045-alpha1-2020-07-13) - [Bug Fixes](#bug-fixes-32) - - [Code Generation](#code-generation-37) + - [Code Generation](#code-generation-38) - [0.4.4-alpha.1 (2020-07-10)](#044-alpha1-2020-07-10) - [Bug Fixes](#bug-fixes-33) - - [Code Generation](#code-generation-38) + - [Code Generation](#code-generation-39) - [Documentation](#documentation-27) - [0.4.3-alpha.1 (2020-07-08)](#043-alpha1-2020-07-08) - [Bug Fixes](#bug-fixes-34) - - [Code Generation](#code-generation-39) + - [Code Generation](#code-generation-40) - [0.4.2-alpha.1 (2020-07-08)](#042-alpha1-2020-07-08) - [Bug Fixes](#bug-fixes-35) - - [Code Generation](#code-generation-40) + - [Code Generation](#code-generation-41) - [0.4.0-alpha.1 (2020-07-08)](#040-alpha1-2020-07-08) - [Breaking Changes](#breaking-changes-16) - [Bug Fixes](#bug-fixes-36) - - [Code Generation](#code-generation-41) + - [Code Generation](#code-generation-42) - [Code Refactoring](#code-refactoring-11) - [Documentation](#documentation-28) - [Features](#features-25) @@ -331,7 +334,17 @@ -# [](https://github.com/ory/kratos/compare/v1.2.0...v) (2024-09-25) +# [](https://github.com/ory/kratos/compare/v1.3.0-pre.0...v) (2024-09-25) + +### Tests + +- Additional code credential test case + ([#4122](https://github.com/ory/kratos/issues/4122)) + ([4f2c854](https://github.com/ory/kratos/commit/4f2c8542ab04b88c7112d7b564d91bcfd8f5791a)) + +# [1.3.0-pre.0](https://github.com/ory/kratos/compare/v1.2.0...v1.3.0-pre.0) (2024-09-25) + +autogen: pin v1.3.0-pre.0 release commit ## Breaking Changes @@ -497,6 +510,11 @@ body in the future. - Whoami latency ([#4070](https://github.com/ory/kratos/issues/4070)) ([ff6ed5b](https://github.com/ory/kratos/commit/ff6ed5b70b7f715fc38a41cedd17b5323aebd79e)) +### Code Generation + +- Pin v1.3.0-pre.0 release commit + ([72aae5b](https://github.com/ory/kratos/commit/72aae5b625ca15552a81bcf956c96994fb716198)) + ### Documentation - Add google to supported providers in ID Token doc strings From 2cd8483e809170d0524fe6a5d13837108d29fa54 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:26:09 +0200 Subject: [PATCH 008/158] feat: change `method=profile:back` to `screen=previous` (#4119) BREAKING CHANGE: When using two-step registration, it was previously possible to send `method=profile:back` to get to the previous screen. This feature was not documented in the SDK API yet. Going forward, please instead use `screen=previous`. --- Makefile | 4 +-- go.mod | 2 +- go.sum | 4 +-- ...e_registration_flow_with_profile_method.go | 2 +- ...e_registration_flow_with_profile_method.go | 2 +- .../profile/.schema/registration.schema.json | 3 +- .../profile/.schema/settings.schema.json | 3 ++ .../strategy/profile/two_step_registration.go | 32 +++++++++++++------ spec/api.json | 9 ++++-- spec/swagger.json | 9 ++++-- .../two-steps/registration/password.spec.ts | 2 +- 11 files changed, 49 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 4924072c8111..05c9c0de9760 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ docs/swagger: npx @redocly/openapi-cli preview-docs spec/swagger.json .bin/golangci-lint: Makefile - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -d -b .bin v1.59.1 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -d -b .bin v1.61.0 .bin/hydra: Makefile bash <(curl https://raw.githubusercontent.com/ory/meta/master/install.sh) -d -b .bin hydra v2.2.0-rc.3 @@ -60,7 +60,7 @@ docs/swagger: .bin/buf: Makefile curl -sSL \ "https://github.com/bufbuild/buf/releases/download/v1.39.0/buf-$(shell uname -s)-$(shell uname -m).tar.gz" | \ - tar -xvzf - -C ".bin/" --strip-components=2 buf/bin/buf buf/bin/protoc-gen-buf-breaking buf/bin/protoc-gen-buf-lint + tar -xvzf - -C ".bin/" --strip-components=2 buf/bin/buf buf/bin/protoc-gen-buf-breaking buf/bin/protoc-gen-buf-lint touch -a -m .bin/buf .PHONY: lint diff --git a/go.mod b/go.mod index fe40535547ea..043213cc7acc 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.655 + github.com/ory/x v0.0.656-0.20240924084701-ce6822d48829 github.com/peterhellberg/link v1.2.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index f95bea93a9a7..60c01f878cb6 100644 --- a/go.sum +++ b/go.sum @@ -645,8 +645,8 @@ github.com/ory/pop/v6 v6.2.0 h1:hRFOGAOEHw91kUHQ32k5NHqCkcHrRou/romvrJP1w0E= github.com/ory/pop/v6 v6.2.0/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/ory/x v0.0.655 h1:P+uwq8GE2YoB9sEyo/8nxuPwdHzBvXE/Xnkyujl7HeQ= -github.com/ory/x v0.0.655/go.mod h1:M+0EAXo7DT7Z2/Yrzvh4mgxOoV1fGI1jOKyAJ72d4Qs= +github.com/ory/x v0.0.656-0.20240924084701-ce6822d48829 h1:y9BraWW+kjp/yYeuRLKBu951WVaLe2Z7lTqb4mPMlFk= +github.com/ory/x v0.0.656-0.20240924084701-ce6822d48829/go.mod h1:M+0EAXo7DT7Z2/Yrzvh4mgxOoV1fGI1jOKyAJ72d4Qs= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= diff --git a/internal/client-go/model_update_registration_flow_with_profile_method.go b/internal/client-go/model_update_registration_flow_with_profile_method.go index 221e5ea82ada..eb9572c1f431 100644 --- a/internal/client-go/model_update_registration_flow_with_profile_method.go +++ b/internal/client-go/model_update_registration_flow_with_profile_method.go @@ -21,7 +21,7 @@ type UpdateRegistrationFlowWithProfileMethod struct { CsrfToken *string `json:"csrf_token,omitempty"` // Method Should be set to profile when trying to update a profile. Method string `json:"method"` - // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. + // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection previous RegistrationScreenPrevious Screen *string `json:"screen,omitempty"` // Traits The identity's traits. Traits map[string]interface{} `json:"traits"` diff --git a/internal/httpclient/model_update_registration_flow_with_profile_method.go b/internal/httpclient/model_update_registration_flow_with_profile_method.go index 221e5ea82ada..eb9572c1f431 100644 --- a/internal/httpclient/model_update_registration_flow_with_profile_method.go +++ b/internal/httpclient/model_update_registration_flow_with_profile_method.go @@ -21,7 +21,7 @@ type UpdateRegistrationFlowWithProfileMethod struct { CsrfToken *string `json:"csrf_token,omitempty"` // Method Should be set to profile when trying to update a profile. Method string `json:"method"` - // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. + // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection previous RegistrationScreenPrevious Screen *string `json:"screen,omitempty"` // Traits The identity's traits. Traits map[string]interface{} `json:"traits"` diff --git a/selfservice/strategy/profile/.schema/registration.schema.json b/selfservice/strategy/profile/.schema/registration.schema.json index 4a4d5dba16c4..727b8f86c7d5 100644 --- a/selfservice/strategy/profile/.schema/registration.schema.json +++ b/selfservice/strategy/profile/.schema/registration.schema.json @@ -12,7 +12,8 @@ "screen": { "type": "string", "enum": [ - "credential-selection" + "credential-selection", + "previous" ] }, "method": { diff --git a/selfservice/strategy/profile/.schema/settings.schema.json b/selfservice/strategy/profile/.schema/settings.schema.json index 5ae4ad70d94a..90246cbde523 100644 --- a/selfservice/strategy/profile/.schema/settings.schema.json +++ b/selfservice/strategy/profile/.schema/settings.schema.json @@ -13,6 +13,9 @@ "csrf_token": { "type": "string" }, + "action": { + "type": "string" + }, "transient_payload": { "type": "object", "additionalProperties": true diff --git a/selfservice/strategy/profile/two_step_registration.go b/selfservice/strategy/profile/two_step_registration.go index 800d04c1c40f..d6f037a54a92 100644 --- a/selfservice/strategy/profile/two_step_registration.go +++ b/selfservice/strategy/profile/two_step_registration.go @@ -9,6 +9,8 @@ import ( "encoding/json" "net/http" + "github.com/ory/x/otelx/semconv" + "go.opentelemetry.io/otel/attribute" "github.com/ory/x/otelx" @@ -65,6 +67,16 @@ func (s *Strategy) PopulateRegistrationMethod(r *http.Request, f *registration.F return nil } +// The RegistrationScreen +// swagger:enum RegistrationScreen +type RegistrationScreen string + +const ( + //nolint:gosec // not a credential + RegistrationScreenCredentialSelection RegistrationScreen = "credential-selection" + RegistrationScreenPrevious RegistrationScreen = "previous" +) + // Update Registration Flow with Profile Method // // swagger:model updateRegistrationFlowWithProfileMethod @@ -92,7 +104,7 @@ type updateRegistrationFlowWithProfileMethod struct { // selection screen. // // required: false - Screen string `json:"screen" form:"screen"` + Screen RegistrationScreen `json:"screen" form:"screen"` // FlowIDRequestID is the flow ID. // @@ -129,16 +141,16 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, regFlow *reg return s.handleRegistrationError(r, regFlow, params, err) } - if params.Screen == "credential-selection" { - params.Method = "profile" - } - - switch params.Method { - case "profile": + if params.Method == "profile" || params.Screen == RegistrationScreenCredentialSelection { return s.displayStepTwoNodes(ctx, w, r, regFlow, i, params) - case "profile:back": + } else if params.Method == "profile:back" { + // "profile:back" is kept for backwards compatibility. + span.AddEvent(semconv.NewDeprecatedFeatureUsedEvent(ctx, "profile:back")) + return s.displayStepOneNodes(ctx, w, r, regFlow, params) + } else if params.Screen == RegistrationScreenPrevious { return s.displayStepOneNodes(ctx, w, r, regFlow, params) } + // Default case span.SetAttributes(attribute.String("not_responsible_reason", "method mismatch")) return flow.ErrStrategyNotResponsible @@ -194,8 +206,8 @@ func (s *Strategy) displayStepTwoNodes(ctx context.Context, w http.ResponseWrite regFlow.UI.Messages.Add(text.NewInfoSelfServiceChooseCredentials()) regFlow.UI.Nodes.Append(node.NewInputField( - "method", - "profile:back", + "screen", + "previous", node.ProfileGroup, node.InputAttributeTypeSubmit, ).WithMetaLabel(text.NewInfoRegistrationBack())) diff --git a/spec/api.json b/spec/api.json index f0331bee92e5..2c03f420226f 100644 --- a/spec/api.json +++ b/spec/api.json @@ -3244,8 +3244,13 @@ "type": "string" }, "screen": { - "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.", - "type": "string" + "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.\ncredential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious", + "enum": [ + "credential-selection", + "previous" + ], + "type": "string", + "x-go-enum-desc": "credential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious" }, "traits": { "description": "Traits\n\nThe identity's traits.", diff --git a/spec/swagger.json b/spec/swagger.json index 12ba5f44a9e6..3085b3a054c4 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -6228,8 +6228,13 @@ "type": "string" }, "screen": { - "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.", - "type": "string" + "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.\ncredential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious", + "type": "string", + "enum": [ + "credential-selection", + "previous" + ], + "x-go-enum-desc": "credential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious" }, "traits": { "description": "Traits\n\nThe identity's traits.", diff --git a/test/e2e/cypress/integration/profiles/two-steps/registration/password.spec.ts b/test/e2e/cypress/integration/profiles/two-steps/registration/password.spec.ts index 975306f8c857..8621b7a69483 100644 --- a/test/e2e/cypress/integration/profiles/two-steps/registration/password.spec.ts +++ b/test/e2e/cypress/integration/profiles/two-steps/registration/password.spec.ts @@ -46,7 +46,7 @@ context("Registration success with two-step signup", () => { cy.get('[name="method"][value="profile"]').click() // navigate back, fill traits again - cy.get('[name="method"][value="profile:back"]').click() + cy.get('[name="screen"][value="previous"]').click() cy.get('input[name="traits.email"]').type( "{selectall}{backspace}" + email, ) From 30dd9c0746baab300d5a72dfafbd3a2a3cc4f145 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 25 Sep 2024 12:28:12 +0000 Subject: [PATCH 009/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- .../model_update_registration_flow_with_profile_method.go | 2 +- .../model_update_registration_flow_with_profile_method.go | 2 +- spec/api.json | 4 ++-- spec/swagger.json | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/client-go/model_update_registration_flow_with_profile_method.go b/internal/client-go/model_update_registration_flow_with_profile_method.go index eb9572c1f431..8cdbb2eab764 100644 --- a/internal/client-go/model_update_registration_flow_with_profile_method.go +++ b/internal/client-go/model_update_registration_flow_with_profile_method.go @@ -21,7 +21,7 @@ type UpdateRegistrationFlowWithProfileMethod struct { CsrfToken *string `json:"csrf_token,omitempty"` // Method Should be set to profile when trying to update a profile. Method string `json:"method"` - // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection previous RegistrationScreenPrevious + // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential previous RegistrationScreenPrevious Screen *string `json:"screen,omitempty"` // Traits The identity's traits. Traits map[string]interface{} `json:"traits"` diff --git a/internal/httpclient/model_update_registration_flow_with_profile_method.go b/internal/httpclient/model_update_registration_flow_with_profile_method.go index eb9572c1f431..8cdbb2eab764 100644 --- a/internal/httpclient/model_update_registration_flow_with_profile_method.go +++ b/internal/httpclient/model_update_registration_flow_with_profile_method.go @@ -21,7 +21,7 @@ type UpdateRegistrationFlowWithProfileMethod struct { CsrfToken *string `json:"csrf_token,omitempty"` // Method Should be set to profile when trying to update a profile. Method string `json:"method"` - // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection previous RegistrationScreenPrevious + // Screen requests navigation to a previous screen. This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential previous RegistrationScreenPrevious Screen *string `json:"screen,omitempty"` // Traits The identity's traits. Traits map[string]interface{} `json:"traits"` diff --git a/spec/api.json b/spec/api.json index 2c03f420226f..19726c4c1a5d 100644 --- a/spec/api.json +++ b/spec/api.json @@ -3244,13 +3244,13 @@ "type": "string" }, "screen": { - "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.\ncredential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious", + "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.\ncredential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\nprevious RegistrationScreenPrevious", "enum": [ "credential-selection", "previous" ], "type": "string", - "x-go-enum-desc": "credential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious" + "x-go-enum-desc": "credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\nprevious RegistrationScreenPrevious" }, "traits": { "description": "Traits\n\nThe identity's traits.", diff --git a/spec/swagger.json b/spec/swagger.json index 3085b3a054c4..6b751e9256c7 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -6228,13 +6228,13 @@ "type": "string" }, "screen": { - "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.\ncredential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious", + "description": "Screen requests navigation to a previous screen.\n\nThis must be set to credential-selection to go back to the credential\nselection screen.\ncredential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\nprevious RegistrationScreenPrevious", "type": "string", "enum": [ "credential-selection", "previous" ], - "x-go-enum-desc": "credential-selection RegistrationScreenCredentialSelection\nprevious RegistrationScreenPrevious" + "x-go-enum-desc": "credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\nprevious RegistrationScreenPrevious" }, "traits": { "description": "Traits\n\nThe identity's traits.", From 358521a34a09a572d7d5fc90c0f231a8c9aefff3 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:21:10 +0000 Subject: [PATCH 010/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 117 +++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 404dd7ee8b98..6e15a677be72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,29 +6,31 @@ **Table of Contents** - [ (2024-09-25)](#2024-09-25) - - [Tests](#tests) -- [1.3.0-pre.0 (2024-09-25)](#130-pre0-2024-09-25) - [Breaking Changes](#breaking-changes) + - [Features](#features) + - [Tests](#tests) +- [1.3.0-pre.0 (2024-09-25)](#130-pre0-2024-09-25) + - [Breaking Changes](#breaking-changes-1) - [Bug Fixes](#bug-fixes) - [Code Generation](#code-generation) - [Documentation](#documentation) - - [Features](#features) + - [Features](#features-1) - [Tests](#tests-1) - [Unclassified](#unclassified) - [1.2.0 (2024-06-05)](#120-2024-06-05) - - [Breaking Changes](#breaking-changes-1) + - [Breaking Changes](#breaking-changes-2) - [Bug Fixes](#bug-fixes-1) - [Code Generation](#code-generation-1) - [Documentation](#documentation-1) - - [Features](#features-1) + - [Features](#features-2) - [Tests](#tests-2) - [Unclassified](#unclassified-1) - [1.1.0 (2024-02-20)](#110-2024-02-20) - - [Breaking Changes](#breaking-changes-2) + - [Breaking Changes](#breaking-changes-3) - [Bug Fixes](#bug-fixes-2) - [Code Generation](#code-generation-2) - [Documentation](#documentation-2) - - [Features](#features-2) + - [Features](#features-3) - [Reverts](#reverts) - [Tests](#tests-3) - [Unclassified](#unclassified-2) @@ -36,35 +38,35 @@ - [Bug Fixes](#bug-fixes-3) - [Code Generation](#code-generation-3) - [Documentation](#documentation-3) - - [Features](#features-3) + - [Features](#features-4) - [Tests](#tests-4) - [Unclassified](#unclassified-3) - [0.13.0 (2023-04-18)](#0130-2023-04-18) - - [Breaking Changes](#breaking-changes-3) + - [Breaking Changes](#breaking-changes-4) - [Bug Fixes](#bug-fixes-4) - [Code Generation](#code-generation-4) - [Code Refactoring](#code-refactoring) - [Documentation](#documentation-4) - - [Features](#features-4) + - [Features](#features-5) - [Tests](#tests-5) - [Unclassified](#unclassified-4) - [0.11.1 (2023-01-14)](#0111-2023-01-14) - - [Breaking Changes](#breaking-changes-4) + - [Breaking Changes](#breaking-changes-5) - [Bug Fixes](#bug-fixes-5) - [Code Generation](#code-generation-5) - [Documentation](#documentation-5) - - [Features](#features-5) + - [Features](#features-6) - [Tests](#tests-6) - [0.11.0 (2022-12-02)](#0110-2022-12-02) - [Code Generation](#code-generation-6) - - [Features](#features-6) + - [Features](#features-7) - [0.11.0-alpha.0.pre.2 (2022-11-28)](#0110-alpha0pre2-2022-11-28) - - [Breaking Changes](#breaking-changes-5) + - [Breaking Changes](#breaking-changes-6) - [Bug Fixes](#bug-fixes-6) - [Code Generation](#code-generation-7) - [Code Refactoring](#code-refactoring-1) - [Documentation](#documentation-6) - - [Features](#features-7) + - [Features](#features-8) - [Reverts](#reverts-1) - [Tests](#tests-7) - [Unclassified](#unclassified-5) @@ -72,16 +74,16 @@ - [Bug Fixes](#bug-fixes-7) - [Code Generation](#code-generation-8) - [0.10.0 (2022-05-30)](#0100-2022-05-30) - - [Breaking Changes](#breaking-changes-6) + - [Breaking Changes](#breaking-changes-7) - [Bug Fixes](#bug-fixes-8) - [Code Generation](#code-generation-9) - [Code Refactoring](#code-refactoring-2) - [Documentation](#documentation-7) - - [Features](#features-8) + - [Features](#features-9) - [Tests](#tests-8) - [Unclassified](#unclassified-6) - [0.9.0-alpha.3 (2022-03-25)](#090-alpha3-2022-03-25) - - [Breaking Changes](#breaking-changes-7) + - [Breaking Changes](#breaking-changes-8) - [Bug Fixes](#bug-fixes-9) - [Code Generation](#code-generation-10) - [Documentation](#documentation-8) @@ -89,21 +91,21 @@ - [Bug Fixes](#bug-fixes-10) - [Code Generation](#code-generation-11) - [0.9.0-alpha.1 (2022-03-21)](#090-alpha1-2022-03-21) - - [Breaking Changes](#breaking-changes-8) + - [Breaking Changes](#breaking-changes-9) - [Bug Fixes](#bug-fixes-11) - [Code Generation](#code-generation-12) - [Code Refactoring](#code-refactoring-3) - [Documentation](#documentation-9) - - [Features](#features-9) + - [Features](#features-10) - [Tests](#tests-9) - [Unclassified](#unclassified-7) - [0.8.3-alpha.1.pre.0 (2022-01-21)](#083-alpha1pre0-2022-01-21) - - [Breaking Changes](#breaking-changes-9) + - [Breaking Changes](#breaking-changes-10) - [Bug Fixes](#bug-fixes-12) - [Code Generation](#code-generation-13) - [Code Refactoring](#code-refactoring-4) - [Documentation](#documentation-10) - - [Features](#features-10) + - [Features](#features-11) - [Tests](#tests-10) - [0.8.2-alpha.1 (2021-12-17)](#082-alpha1-2021-12-17) - [Bug Fixes](#bug-fixes-13) @@ -113,14 +115,14 @@ - [Bug Fixes](#bug-fixes-14) - [Code Generation](#code-generation-15) - [Documentation](#documentation-12) - - [Features](#features-11) + - [Features](#features-12) - [Tests](#tests-11) - [0.8.0-alpha.4.pre.0 (2021-11-09)](#080-alpha4pre0-2021-11-09) - - [Breaking Changes](#breaking-changes-10) + - [Breaking Changes](#breaking-changes-11) - [Bug Fixes](#bug-fixes-15) - [Code Generation](#code-generation-16) - [Documentation](#documentation-13) - - [Features](#features-12) + - [Features](#features-13) - [Tests](#tests-12) - [0.8.0-alpha.3 (2021-10-28)](#080-alpha3-2021-10-28) - [Bug Fixes](#bug-fixes-16) @@ -128,12 +130,12 @@ - [0.8.0-alpha.2 (2021-10-28)](#080-alpha2-2021-10-28) - [Code Generation](#code-generation-18) - [0.8.0-alpha.1 (2021-10-27)](#080-alpha1-2021-10-27) - - [Breaking Changes](#breaking-changes-11) + - [Breaking Changes](#breaking-changes-12) - [Bug Fixes](#bug-fixes-17) - [Code Generation](#code-generation-19) - [Code Refactoring](#code-refactoring-5) - [Documentation](#documentation-14) - - [Features](#features-13) + - [Features](#features-14) - [Reverts](#reverts-2) - [Tests](#tests-13) - [Unclassified](#unclassified-8) @@ -145,29 +147,29 @@ - [Bug Fixes](#bug-fixes-18) - [Code Generation](#code-generation-22) - [Documentation](#documentation-15) - - [Features](#features-14) + - [Features](#features-15) - [Tests](#tests-14) - [0.7.3-alpha.1 (2021-08-28)](#073-alpha1-2021-08-28) - [Bug Fixes](#bug-fixes-19) - [Code Generation](#code-generation-23) - [Documentation](#documentation-16) - - [Features](#features-15) + - [Features](#features-16) - [0.7.1-alpha.1 (2021-07-22)](#071-alpha1-2021-07-22) - [Bug Fixes](#bug-fixes-20) - [Code Generation](#code-generation-24) - [Documentation](#documentation-17) - [Tests](#tests-15) - [0.7.0-alpha.1 (2021-07-13)](#070-alpha1-2021-07-13) - - [Breaking Changes](#breaking-changes-12) + - [Breaking Changes](#breaking-changes-13) - [Bug Fixes](#bug-fixes-21) - [Code Generation](#code-generation-25) - [Code Refactoring](#code-refactoring-6) - [Documentation](#documentation-18) - - [Features](#features-16) + - [Features](#features-17) - [Tests](#tests-16) - [Unclassified](#unclassified-9) - [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17) - - [Breaking Changes](#breaking-changes-13) + - [Breaking Changes](#breaking-changes-14) - [Bug Fixes](#bug-fixes-22) - [Code Generation](#code-generation-26) - [Code Refactoring](#code-refactoring-7) @@ -176,25 +178,25 @@ - [Documentation](#documentation-19) - [0.6.1-alpha.1 (2021-05-11)](#061-alpha1-2021-05-11) - [Code Generation](#code-generation-28) - - [Features](#features-17) + - [Features](#features-18) - [0.6.0-alpha.2 (2021-05-07)](#060-alpha2-2021-05-07) - [Bug Fixes](#bug-fixes-23) - [Code Generation](#code-generation-29) - - [Features](#features-18) + - [Features](#features-19) - [0.6.0-alpha.1 (2021-05-05)](#060-alpha1-2021-05-05) - - [Breaking Changes](#breaking-changes-14) + - [Breaking Changes](#breaking-changes-15) - [Bug Fixes](#bug-fixes-24) - [Code Generation](#code-generation-30) - [Code Refactoring](#code-refactoring-8) - [Documentation](#documentation-20) - - [Features](#features-19) + - [Features](#features-20) - [Tests](#tests-17) - [Unclassified](#unclassified-10) - [0.5.5-alpha.1 (2020-12-09)](#055-alpha1-2020-12-09) - [Bug Fixes](#bug-fixes-25) - [Code Generation](#code-generation-31) - [Documentation](#documentation-21) - - [Features](#features-20) + - [Features](#features-21) - [Tests](#tests-18) - [Unclassified](#unclassified-11) - [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11) @@ -202,12 +204,12 @@ - [Code Generation](#code-generation-32) - [Code Refactoring](#code-refactoring-9) - [Documentation](#documentation-22) - - [Features](#features-21) + - [Features](#features-22) - [0.5.3-alpha.1 (2020-10-27)](#053-alpha1-2020-10-27) - [Bug Fixes](#bug-fixes-27) - [Code Generation](#code-generation-33) - [Documentation](#documentation-23) - - [Features](#features-22) + - [Features](#features-23) - [Tests](#tests-19) - [0.5.2-alpha.1 (2020-10-22)](#052-alpha1-2020-10-22) - [Bug Fixes](#bug-fixes-28) @@ -218,16 +220,16 @@ - [Bug Fixes](#bug-fixes-29) - [Code Generation](#code-generation-35) - [Documentation](#documentation-25) - - [Features](#features-23) + - [Features](#features-24) - [Tests](#tests-21) - [Unclassified](#unclassified-12) - [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15) - - [Breaking Changes](#breaking-changes-15) + - [Breaking Changes](#breaking-changes-16) - [Bug Fixes](#bug-fixes-30) - [Code Generation](#code-generation-36) - [Code Refactoring](#code-refactoring-10) - [Documentation](#documentation-26) - - [Features](#features-24) + - [Features](#features-25) - [Tests](#tests-22) - [Unclassified](#unclassified-13) - [0.4.6-alpha.1 (2020-07-13)](#046-alpha1-2020-07-13) @@ -247,31 +249,31 @@ - [Bug Fixes](#bug-fixes-35) - [Code Generation](#code-generation-41) - [0.4.0-alpha.1 (2020-07-08)](#040-alpha1-2020-07-08) - - [Breaking Changes](#breaking-changes-16) + - [Breaking Changes](#breaking-changes-17) - [Bug Fixes](#bug-fixes-36) - [Code Generation](#code-generation-42) - [Code Refactoring](#code-refactoring-11) - [Documentation](#documentation-28) - - [Features](#features-25) + - [Features](#features-26) - [Unclassified](#unclassified-14) - [0.3.0-alpha.1 (2020-05-15)](#030-alpha1-2020-05-15) - - [Breaking Changes](#breaking-changes-17) + - [Breaking Changes](#breaking-changes-18) - [Bug Fixes](#bug-fixes-37) - [Chores](#chores) - [Code Refactoring](#code-refactoring-12) - [Documentation](#documentation-29) - - [Features](#features-26) + - [Features](#features-27) - [Unclassified](#unclassified-15) - [0.2.1-alpha.1 (2020-05-05)](#021-alpha1-2020-05-05) - [Chores](#chores-1) - [Documentation](#documentation-30) - [0.2.0-alpha.2 (2020-05-04)](#020-alpha2-2020-05-04) - - [Breaking Changes](#breaking-changes-18) + - [Breaking Changes](#breaking-changes-19) - [Bug Fixes](#bug-fixes-38) - [Chores](#chores-2) - [Code Refactoring](#code-refactoring-13) - [Documentation](#documentation-31) - - [Features](#features-27) + - [Features](#features-28) - [Unclassified](#unclassified-16) - [0.1.1-alpha.1 (2020-02-18)](#011-alpha1-2020-02-18) - [Bug Fixes](#bug-fixes-39) @@ -281,10 +283,10 @@ - [Bug Fixes](#bug-fixes-40) - [Code Refactoring](#code-refactoring-15) - [Documentation](#documentation-33) - - [Features](#features-28) + - [Features](#features-29) - [0.1.0-alpha.5 (2020-02-06)](#010-alpha5-2020-02-06) - [Documentation](#documentation-34) - - [Features](#features-29) + - [Features](#features-30) - [0.1.0-alpha.4 (2020-02-06)](#010-alpha4-2020-02-06) - [Continuous Integration](#continuous-integration) - [Documentation](#documentation-35) @@ -293,7 +295,7 @@ - [0.1.0-alpha.2 (2020-02-03)](#010-alpha2-2020-02-03) - [Bug Fixes](#bug-fixes-41) - [Documentation](#documentation-36) - - [Features](#features-30) + - [Features](#features-31) - [Unclassified](#unclassified-17) - [0.1.0-alpha.1 (2020-01-31)](#010-alpha1-2020-01-31) - [Documentation](#documentation-37) @@ -336,6 +338,19 @@ # [](https://github.com/ory/kratos/compare/v1.3.0-pre.0...v) (2024-09-25) +## Breaking Changes + +When using two-step registration, it was previously possible to send +`method=profile:back` to get to the previous screen. This feature was not +documented in the SDK API yet. Going forward, please instead use +`screen=previous`. + +### Features + +- Change `method=profile:back` to `screen=previous` + ([#4119](https://github.com/ory/kratos/issues/4119)) + ([2cd8483](https://github.com/ory/kratos/commit/2cd8483e809170d0524fe6a5d13837108d29fa54)) + ### Tests - Additional code credential test case From ad1acd51d8dd7582b05a3078b92f73970e1e2715 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Wed, 25 Sep 2024 19:26:39 +0200 Subject: [PATCH 011/158] fix: passthrough correct organization ID to CompletedLoginForWithProvider (#4124) --- go.mod | 2 +- go.sum | 4 ++-- selfservice/flow/registration/handler.go | 2 +- selfservice/flow/registration/hook.go | 6 ++---- selfservice/flow/registration/hook_test.go | 2 +- selfservice/strategy/oidc/strategy_registration.go | 2 +- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 043213cc7acc..ac36ab25892a 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.656-0.20240924084701-ce6822d48829 + github.com/ory/x v0.0.660 github.com/peterhellberg/link v1.2.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 60c01f878cb6..e33a12499ae0 100644 --- a/go.sum +++ b/go.sum @@ -645,8 +645,8 @@ github.com/ory/pop/v6 v6.2.0 h1:hRFOGAOEHw91kUHQ32k5NHqCkcHrRou/romvrJP1w0E= github.com/ory/pop/v6 v6.2.0/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/ory/x v0.0.656-0.20240924084701-ce6822d48829 h1:y9BraWW+kjp/yYeuRLKBu951WVaLe2Z7lTqb4mPMlFk= -github.com/ory/x v0.0.656-0.20240924084701-ce6822d48829/go.mod h1:M+0EAXo7DT7Z2/Yrzvh4mgxOoV1fGI1jOKyAJ72d4Qs= +github.com/ory/x v0.0.660 h1:mEZjmVtPY5grN3bmuSPkJBTK7xSNepzy0bCmVOCLZxU= +github.com/ory/x v0.0.660/go.mod h1:tS0FyZXpVeKd1lCcFgV/Rb1GlccI/Xq8DraFS+lmIt8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= diff --git a/selfservice/flow/registration/handler.go b/selfservice/flow/registration/handler.go index 8cfe59e4d6d9..ee3e23ba144f 100644 --- a/selfservice/flow/registration/handler.go +++ b/selfservice/flow/registration/handler.go @@ -663,7 +663,7 @@ func (h *Handler) updateRegistrationFlow(w http.ResponseWriter, r *http.Request, return } - if err := h.d.RegistrationExecutor().PostRegistrationHook(w, r, s.ID(), "", f, i); err != nil { + if err := h.d.RegistrationExecutor().PostRegistrationHook(w, r, s.ID(), "", "", f, i); err != nil { h.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, f, s.NodeGroup(), err) return } diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index c1c7b7ed4b2c..01ca1847d5d5 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -9,7 +9,6 @@ import ( "net/http" "time" - "github.com/julienschmidt/httprouter" "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" @@ -101,7 +100,7 @@ func NewHookExecutor(d executorDependencies) *HookExecutor { return &HookExecutor{d: d} } -func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Request, ct identity.CredentialsType, provider string, registrationFlow *Flow, i *identity.Identity) (err error) { +func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Request, ct identity.CredentialsType, provider, organizationID string, registrationFlow *Flow, i *identity.Identity) (err error) { ctx := r.Context() ctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, "HookExecutor.PostRegistrationHook") r = r.WithContext(ctx) @@ -212,8 +211,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque s := session.NewInactiveSession() - s.CompletedLoginForWithProvider(ct, identity.AuthenticatorAssuranceLevel1, provider, - httprouter.ParamsFromContext(r.Context()).ByName("organization")) + s.CompletedLoginForWithProvider(ct, identity.AuthenticatorAssuranceLevel1, provider, organizationID) if err := e.d.SessionManager().ActivateSession(r, s, i, time.Now().UTC()); err != nil { return err } diff --git a/selfservice/flow/registration/hook_test.go b/selfservice/flow/registration/hook_test.go index 9e60b33f1f52..9a65b05a0eeb 100644 --- a/selfservice/flow/registration/hook_test.go +++ b/selfservice/flow/registration/hook_test.go @@ -65,7 +65,7 @@ func TestRegistrationExecutor(t *testing.T) { for _, callback := range flowCallbacks { callback(regFlow) } - _ = handleErr(t, w, r, reg.RegistrationHookExecutor().PostRegistrationHook(w, r, identity.CredentialsType(strategy), "", regFlow, i)) + _ = handleErr(t, w, r, reg.RegistrationHookExecutor().PostRegistrationHook(w, r, identity.CredentialsType(strategy), "", "", regFlow, i)) }) ts := httptest.NewServer(router) diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index 50567452aa9d..4edab83a351e 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -346,7 +346,7 @@ func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWrite } i.SetCredentials(s.ID(), *creds) - if err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, identity.CredentialsTypeOIDC, provider.Config().ID, rf, i); err != nil { + if err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, identity.CredentialsTypeOIDC, provider.Config().ID, provider.Config().OrganizationID, rf, i); err != nil { return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err) } From 0a49fd05245f179501b117163cd574786f287fe8 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:12:42 +0200 Subject: [PATCH 012/158] autogen: pin v1.3.0 release commit --- .schemastore/config.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemastore/config.schema.json b/.schemastore/config.schema.json index 4b815e53d0f8..9d853c1e64f3 100644 --- a/.schemastore/config.schema.json +++ b/.schemastore/config.schema.json @@ -2536,7 +2536,7 @@ "additionalProperties": false }, "tracing": { - "$ref": "https://raw.githubusercontent.com/ory/x/v0.0.655/otelx/config.schema.json" + "$ref": "https://raw.githubusercontent.com/ory/x/v0.0.660/otelx/config.schema.json" }, "log": { "title": "Log", From 7ea6e4d04c9012e947cb9d3beb24d01e15f0b5b7 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:34:04 +0000 Subject: [PATCH 013/158] autogen(docs): generate and bump docs [skip ci] --- quickstart.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quickstart.yml b/quickstart.yml index 10a331c397b1..51db8310a494 100644 --- a/quickstart.yml +++ b/quickstart.yml @@ -1,7 +1,7 @@ version: '3.7' services: kratos-migrate: - image: oryd/kratos:v1.2.0 + image: oryd/kratos:v1.3.0 environment: - DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc volumes: @@ -17,7 +17,7 @@ services: networks: - intranet kratos-selfservice-ui-node: - image: oryd/kratos-selfservice-ui-node:v1.2.0 + image: oryd/kratos-selfservice-ui-node:v1.3.0 environment: - KRATOS_PUBLIC_URL=http://kratos:4433/ - KRATOS_BROWSER_URL=http://127.0.0.1:4433/ @@ -30,7 +30,7 @@ services: kratos: depends_on: - kratos-migrate - image: oryd/kratos:v1.2.0 + image: oryd/kratos:v1.3.0 ports: - '4433:4433' # public - '4434:4434' # admin From 4f1a2b7d78c17ab55a8ffd4be74c34f819bc44d1 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:34:25 +0000 Subject: [PATCH 014/158] autogen: add v1.3.0 to version.schema.json [skip ci] --- .schema/version.schema.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.schema/version.schema.json b/.schema/version.schema.json index 14192708b3c7..f82ab21fa546 100644 --- a/.schema/version.schema.json +++ b/.schema/version.schema.json @@ -2,6 +2,23 @@ "$id": "https://github.com/ory/kratos/.schema/versions.config.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "oneOf": [ + { + "allOf": [ + { + "properties": { + "version": { + "const": "v1.3.0" + } + }, + "required": [ + "version" + ] + }, + { + "$ref": "https://raw.githubusercontent.com/ory/kratos/v1.3.0/.schemastore/config.schema.json" + } + ] + }, { "allOf": [ { From 66afac173dc08b1d6666b107cf7050a2b0b27774 Mon Sep 17 00:00:00 2001 From: Anran Date: Thu, 3 Oct 2024 13:30:25 +0200 Subject: [PATCH 015/158] fix: explicity set updated_at field when updating identity (#4131) --- identity/manager_test.go | 16 ++++++++++++++++ persistence/sql/identity/persister_identity.go | 1 + 2 files changed, 17 insertions(+) diff --git a/identity/manager_test.go b/identity/manager_test.go index bd9e67782ea9..b7659eba68a1 100644 --- a/identity/manager_test.go +++ b/identity/manager_test.go @@ -677,6 +677,22 @@ func TestManager(t *testing.T) { // That is why we only check the identity in the store. checkExtensionFields(fromStore, "email-updatetraits-1@ory.sh")(t) }) + + t.Run("case=should always update updated_at field", func(t *testing.T) { + original := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID) + original.Traits = newTraits("email-updatetraits-3@ory.sh", "") + require.NoError(t, reg.IdentityManager().Create(ctx, original)) + + time.Sleep(time.Millisecond) + + require.NoError(t, reg.IdentityManager().UpdateTraits( + ctx, original.ID, newTraits("email-updatetraits-4@ory.sh", ""), + identity.ManagerAllowWriteProtectedTraits)) + + updated, err := reg.IdentityPool().GetIdentity(ctx, original.ID, identity.ExpandNothing) + require.NoError(t, err) + assert.NotEqual(t, original.UpdatedAt, updated.UpdatedAt, "UpdatedAt field should be updated") + }) }) t.Run("method=RefreshAvailableAAL", func(t *testing.T) { diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 4523ce7f2146..14d59cf5f445 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -1072,6 +1072,7 @@ func (p *IdentityPersister) UpdateIdentity(ctx context.Context, i *identity.Iden } i.NID = p.NetworkID(ctx) + i.UpdatedAt = time.Now().UTC() if err := sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { // This returns "ErrNoRows" if the identity does not exist if err := update.Generic(WithTransaction(ctx, tx), tx, p.r.Tracer(ctx).Tracer(), i); err != nil { From b22135fa05d7fb47dfeaccd7cdc183d16921a7ac Mon Sep 17 00:00:00 2001 From: David Wobrock Date: Thu, 3 Oct 2024 13:30:59 +0200 Subject: [PATCH 016/158] docs: add return_to query parameter to OAS Verification Flow for Native Apps (#4086) --- selfservice/flow/verification/handler.go | 14 ++++++++++++++ spec/api.json | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/selfservice/flow/verification/handler.go b/selfservice/flow/verification/handler.go index 8b0e832ad8e7..785f627e7f6a 100644 --- a/selfservice/flow/verification/handler.go +++ b/selfservice/flow/verification/handler.go @@ -123,6 +123,20 @@ func (h *Handler) NewVerificationFlow(w http.ResponseWriter, r *http.Request, ft return f, nil } +// Create Verification Flow Parameters for Native Apps +// +// swagger:parameters createNativeVerificationFlow +// +//nolint:deadcode,unused +//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions +type createNativeVerificationFlow struct { + // A URL contained in the return_to key of the verification flow. + // This piece of data has no effect on the actual logic of the flow and is purely informational. + // + // in: query + ReturnTo string `json:"return_to"` +} + // swagger:route GET /self-service/verification/api frontend createNativeVerificationFlow // // # Create Verification Flow for Native Apps diff --git a/spec/api.json b/spec/api.json index 19726c4c1a5d..d438cab3e7e5 100644 --- a/spec/api.json +++ b/spec/api.json @@ -7092,6 +7092,16 @@ "get": { "description": "This endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\n\nTo fetch an existing verification flow call `/self-service/verification/flows?flow=\u003cflow_id\u003e`.\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\nMore information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).", "operationId": "createNativeVerificationFlow", + "parameters": [ + { + "description": "A URL contained in the return_to key of the verification flow. This piece of data has no effect on the actual logic of the flow and is purely informational.", + "in": "query", + "name": "return_to", + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "content": { From d31c1eeea1a105dc2fc71b818423465c66ca05cb Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 3 Oct 2024 11:32:40 +0000 Subject: [PATCH 017/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/api_frontend.go | 9 +++++++++ internal/httpclient/api_frontend.go | 9 +++++++++ spec/api.json | 2 +- spec/swagger.json | 8 ++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/internal/client-go/api_frontend.go b/internal/client-go/api_frontend.go index 762df5b59fe9..18138c402c18 100644 --- a/internal/client-go/api_frontend.go +++ b/internal/client-go/api_frontend.go @@ -2511,6 +2511,12 @@ func (a *FrontendAPIService) CreateNativeSettingsFlowExecute(r FrontendAPIApiCre type FrontendAPIApiCreateNativeVerificationFlowRequest struct { ctx context.Context ApiService FrontendAPI + returnTo *string +} + +func (r FrontendAPIApiCreateNativeVerificationFlowRequest) ReturnTo(returnTo string) FrontendAPIApiCreateNativeVerificationFlowRequest { + r.returnTo = &returnTo + return r } func (r FrontendAPIApiCreateNativeVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) { @@ -2565,6 +2571,9 @@ func (a *FrontendAPIService) CreateNativeVerificationFlowExecute(r FrontendAPIAp localVarQueryParams := url.Values{} localVarFormParams := url.Values{} + if r.returnTo != nil { + localVarQueryParams.Add("return_to", parameterToString(*r.returnTo, "")) + } // to determine the Content-Type header localVarHTTPContentTypes := []string{} diff --git a/internal/httpclient/api_frontend.go b/internal/httpclient/api_frontend.go index 762df5b59fe9..18138c402c18 100644 --- a/internal/httpclient/api_frontend.go +++ b/internal/httpclient/api_frontend.go @@ -2511,6 +2511,12 @@ func (a *FrontendAPIService) CreateNativeSettingsFlowExecute(r FrontendAPIApiCre type FrontendAPIApiCreateNativeVerificationFlowRequest struct { ctx context.Context ApiService FrontendAPI + returnTo *string +} + +func (r FrontendAPIApiCreateNativeVerificationFlowRequest) ReturnTo(returnTo string) FrontendAPIApiCreateNativeVerificationFlowRequest { + r.returnTo = &returnTo + return r } func (r FrontendAPIApiCreateNativeVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) { @@ -2565,6 +2571,9 @@ func (a *FrontendAPIService) CreateNativeVerificationFlowExecute(r FrontendAPIAp localVarQueryParams := url.Values{} localVarFormParams := url.Values{} + if r.returnTo != nil { + localVarQueryParams.Add("return_to", parameterToString(*r.returnTo, "")) + } // to determine the Content-Type header localVarHTTPContentTypes := []string{} diff --git a/spec/api.json b/spec/api.json index d438cab3e7e5..442e8a4e785c 100644 --- a/spec/api.json +++ b/spec/api.json @@ -7094,7 +7094,7 @@ "operationId": "createNativeVerificationFlow", "parameters": [ { - "description": "A URL contained in the return_to key of the verification flow. This piece of data has no effect on the actual logic of the flow and is purely informational.", + "description": "A URL contained in the return_to key of the verification flow.\nThis piece of data has no effect on the actual logic of the flow and is purely informational.", "in": "query", "name": "return_to", "schema": { diff --git a/spec/swagger.json b/spec/swagger.json index 6b751e9256c7..3cbbf82b340b 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -2780,6 +2780,14 @@ ], "summary": "Create Verification Flow for Native Apps", "operationId": "createNativeVerificationFlow", + "parameters": [ + { + "type": "string", + "description": "A URL contained in the return_to key of the verification flow.\nThis piece of data has no effect on the actual logic of the flow and is purely informational.", + "name": "return_to", + "in": "query" + } + ], "responses": { "200": { "description": "verificationFlow", From 629d8675cd4838d23cbb67978344208a960cbf02 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:45:49 +0200 Subject: [PATCH 018/158] chore: synchronize workspaces (#4140) --- identity/pool.go | 13 +++++- .../sql/identity/persister_identity.go | 43 ++++++++++++++++--- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/identity/pool.go b/identity/pool.go index 30a7308245b4..e07a6b8ee83e 100644 --- a/identity/pool.go +++ b/identity/pool.go @@ -23,9 +23,11 @@ type ( CredentialsIdentifierSimilar string DeclassifyCredentials []CredentialsType KeySetPagination []keysetpagination.Option + ConsistencyLevel crdbx.ConsistencyLevel + StatementTransformer func(string) string + // DEPRECATED - PagePagination *x.Page - ConsistencyLevel crdbx.ConsistencyLevel + PagePagination *x.Page } Pool interface { @@ -114,3 +116,10 @@ type ( FindIdentityByWebauthnUserHandle(ctx context.Context, userHandle []byte) (*Identity, error) } ) + +func (p ListIdentityParameters) TransformStatement(statement string) string { + if p.StatementTransformer != nil { + return p.StatementTransformer(statement) + } + return statement +} diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 14d59cf5f445..de21fc1766e7 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -876,6 +876,23 @@ func paginationAttributes(params *identity.ListIdentityParameters, paginator *ke return attrs } +// getCredentialTypeIDs returns a map of credential types to their respective IDs. +// +// If a credential type is not found, an error is returned. +func (p *IdentityPersister) getCredentialTypeIDs(ctx context.Context, credentialTypes []identity.CredentialsType) (map[identity.CredentialsType]uuid.UUID, error) { + result := map[identity.CredentialsType]uuid.UUID{} + + for _, ct := range credentialTypes { + typeID, err := p.findIdentityCredentialsType(ctx, ct) + if err != nil { + return nil, err + } + result[ct] = typeID.ID + } + + return result, nil +} + func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity.ListIdentityParameters) (_ []identity.Identity, nextPage *keysetpagination.Paginator, err error) { paginator := keysetpagination.GetPaginator(append( params.KeySetPagination, @@ -925,22 +942,34 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. } if len(identifier) > 0 { + types, err := p.getCredentialTypeIDs(ctx, []identity.CredentialsType{ + identity.CredentialsTypeWebAuthn, + identity.CredentialsTypePassword, + identity.CredentialsTypeCodeAuth, + identity.CredentialsTypeOIDC, + }) + if err != nil { + return err + } + // When filtering by credentials identifier, we most likely are looking for a username or email. It is therefore // important to normalize the identifier before querying the database. - joins = ` + joins = params.TransformStatement(` INNER JOIN identity_credentials ic ON ic.identity_id = identities.id - INNER JOIN identity_credential_types ict ON ict.id = ic.identity_credential_type_id - INNER JOIN identity_credential_identifiers ici ON ici.identity_credential_id = ic.id` + INNER JOIN identity_credential_identifiers ici ON ici.identity_credential_id = ic.id`) + wheres += fmt.Sprintf(` AND ic.nid = ? AND ici.nid = ? - AND ((ict.name IN (?, ?, ?) AND ici.identifier %s ?) - OR (ict.name IN (?) AND ici.identifier %s ?)) + AND ((ic.identity_credential_type_id IN (?, ?, ?) AND ici.identifier %s ?) + OR (ic.identity_credential_type_id IN (?) AND ici.identifier %s ?)) `, identifierOperator, identifierOperator) args = append(args, nid, nid, - identity.CredentialsTypeWebAuthn, identity.CredentialsTypePassword, identity.CredentialsTypeCodeAuth, NormalizeIdentifier(identity.CredentialsTypePassword, identifier), - identity.CredentialsTypeOIDC, identifier) + types[identity.CredentialsTypeWebAuthn], types[identity.CredentialsTypePassword], types[identity.CredentialsTypeCodeAuth], + NormalizeIdentifier(identity.CredentialsTypePassword, identifier), + types[identity.CredentialsTypeOIDC], identifier, + ) } if params.IdsFilter != nil && len(params.IdsFilter) != 0 { From df2e1f09e9e601bbc52233b13a5fb2c36bbf925d Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 3 Oct 2024 15:40:39 +0000 Subject: [PATCH 019/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 451 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 274 insertions(+), 177 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e15a677be72..3cf2d4290239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,300 +5,299 @@ **Table of Contents** -- [ (2024-09-25)](#2024-09-25) +- [ (2024-10-03)](#2024-10-03) + - [Bug Fixes](#bug-fixes) + - [Documentation](#documentation) +- [1.3.0 (2024-09-26)](#130-2024-09-26) - [Breaking Changes](#breaking-changes) + - [Bug Fixes](#bug-fixes-1) + - [Code Generation](#code-generation) + - [Documentation](#documentation-1) - [Features](#features) - [Tests](#tests) -- [1.3.0-pre.0 (2024-09-25)](#130-pre0-2024-09-25) - - [Breaking Changes](#breaking-changes-1) - - [Bug Fixes](#bug-fixes) - - [Code Generation](#code-generation) - - [Documentation](#documentation) - - [Features](#features-1) - - [Tests](#tests-1) - [Unclassified](#unclassified) - [1.2.0 (2024-06-05)](#120-2024-06-05) - - [Breaking Changes](#breaking-changes-2) - - [Bug Fixes](#bug-fixes-1) + - [Breaking Changes](#breaking-changes-1) + - [Bug Fixes](#bug-fixes-2) - [Code Generation](#code-generation-1) - - [Documentation](#documentation-1) - - [Features](#features-2) - - [Tests](#tests-2) + - [Documentation](#documentation-2) + - [Features](#features-1) + - [Tests](#tests-1) - [Unclassified](#unclassified-1) - [1.1.0 (2024-02-20)](#110-2024-02-20) - - [Breaking Changes](#breaking-changes-3) - - [Bug Fixes](#bug-fixes-2) + - [Breaking Changes](#breaking-changes-2) + - [Bug Fixes](#bug-fixes-3) - [Code Generation](#code-generation-2) - - [Documentation](#documentation-2) - - [Features](#features-3) + - [Documentation](#documentation-3) + - [Features](#features-2) - [Reverts](#reverts) - - [Tests](#tests-3) + - [Tests](#tests-2) - [Unclassified](#unclassified-2) - [1.0.0 (2023-07-12)](#100-2023-07-12) - - [Bug Fixes](#bug-fixes-3) + - [Bug Fixes](#bug-fixes-4) - [Code Generation](#code-generation-3) - - [Documentation](#documentation-3) - - [Features](#features-4) - - [Tests](#tests-4) + - [Documentation](#documentation-4) + - [Features](#features-3) + - [Tests](#tests-3) - [Unclassified](#unclassified-3) - [0.13.0 (2023-04-18)](#0130-2023-04-18) - - [Breaking Changes](#breaking-changes-4) - - [Bug Fixes](#bug-fixes-4) + - [Breaking Changes](#breaking-changes-3) + - [Bug Fixes](#bug-fixes-5) - [Code Generation](#code-generation-4) - [Code Refactoring](#code-refactoring) - - [Documentation](#documentation-4) - - [Features](#features-5) - - [Tests](#tests-5) + - [Documentation](#documentation-5) + - [Features](#features-4) + - [Tests](#tests-4) - [Unclassified](#unclassified-4) - [0.11.1 (2023-01-14)](#0111-2023-01-14) - - [Breaking Changes](#breaking-changes-5) - - [Bug Fixes](#bug-fixes-5) + - [Breaking Changes](#breaking-changes-4) + - [Bug Fixes](#bug-fixes-6) - [Code Generation](#code-generation-5) - - [Documentation](#documentation-5) - - [Features](#features-6) - - [Tests](#tests-6) + - [Documentation](#documentation-6) + - [Features](#features-5) + - [Tests](#tests-5) - [0.11.0 (2022-12-02)](#0110-2022-12-02) - [Code Generation](#code-generation-6) - - [Features](#features-7) + - [Features](#features-6) - [0.11.0-alpha.0.pre.2 (2022-11-28)](#0110-alpha0pre2-2022-11-28) - - [Breaking Changes](#breaking-changes-6) - - [Bug Fixes](#bug-fixes-6) + - [Breaking Changes](#breaking-changes-5) + - [Bug Fixes](#bug-fixes-7) - [Code Generation](#code-generation-7) - [Code Refactoring](#code-refactoring-1) - - [Documentation](#documentation-6) - - [Features](#features-8) + - [Documentation](#documentation-7) + - [Features](#features-7) - [Reverts](#reverts-1) - - [Tests](#tests-7) + - [Tests](#tests-6) - [Unclassified](#unclassified-5) - [0.10.1 (2022-06-01)](#0101-2022-06-01) - - [Bug Fixes](#bug-fixes-7) + - [Bug Fixes](#bug-fixes-8) - [Code Generation](#code-generation-8) - [0.10.0 (2022-05-30)](#0100-2022-05-30) - - [Breaking Changes](#breaking-changes-7) - - [Bug Fixes](#bug-fixes-8) + - [Breaking Changes](#breaking-changes-6) + - [Bug Fixes](#bug-fixes-9) - [Code Generation](#code-generation-9) - [Code Refactoring](#code-refactoring-2) - - [Documentation](#documentation-7) - - [Features](#features-9) - - [Tests](#tests-8) + - [Documentation](#documentation-8) + - [Features](#features-8) + - [Tests](#tests-7) - [Unclassified](#unclassified-6) - [0.9.0-alpha.3 (2022-03-25)](#090-alpha3-2022-03-25) - - [Breaking Changes](#breaking-changes-8) - - [Bug Fixes](#bug-fixes-9) + - [Breaking Changes](#breaking-changes-7) + - [Bug Fixes](#bug-fixes-10) - [Code Generation](#code-generation-10) - - [Documentation](#documentation-8) + - [Documentation](#documentation-9) - [0.9.0-alpha.2 (2022-03-22)](#090-alpha2-2022-03-22) - - [Bug Fixes](#bug-fixes-10) + - [Bug Fixes](#bug-fixes-11) - [Code Generation](#code-generation-11) - [0.9.0-alpha.1 (2022-03-21)](#090-alpha1-2022-03-21) - - [Breaking Changes](#breaking-changes-9) - - [Bug Fixes](#bug-fixes-11) + - [Breaking Changes](#breaking-changes-8) + - [Bug Fixes](#bug-fixes-12) - [Code Generation](#code-generation-12) - [Code Refactoring](#code-refactoring-3) - - [Documentation](#documentation-9) - - [Features](#features-10) - - [Tests](#tests-9) + - [Documentation](#documentation-10) + - [Features](#features-9) + - [Tests](#tests-8) - [Unclassified](#unclassified-7) - [0.8.3-alpha.1.pre.0 (2022-01-21)](#083-alpha1pre0-2022-01-21) - - [Breaking Changes](#breaking-changes-10) - - [Bug Fixes](#bug-fixes-12) + - [Breaking Changes](#breaking-changes-9) + - [Bug Fixes](#bug-fixes-13) - [Code Generation](#code-generation-13) - [Code Refactoring](#code-refactoring-4) - - [Documentation](#documentation-10) - - [Features](#features-11) - - [Tests](#tests-10) + - [Documentation](#documentation-11) + - [Features](#features-10) + - [Tests](#tests-9) - [0.8.2-alpha.1 (2021-12-17)](#082-alpha1-2021-12-17) - - [Bug Fixes](#bug-fixes-13) + - [Bug Fixes](#bug-fixes-14) - [Code Generation](#code-generation-14) - - [Documentation](#documentation-11) + - [Documentation](#documentation-12) - [0.8.1-alpha.1 (2021-12-13)](#081-alpha1-2021-12-13) - - [Bug Fixes](#bug-fixes-14) + - [Bug Fixes](#bug-fixes-15) - [Code Generation](#code-generation-15) - - [Documentation](#documentation-12) - - [Features](#features-12) - - [Tests](#tests-11) + - [Documentation](#documentation-13) + - [Features](#features-11) + - [Tests](#tests-10) - [0.8.0-alpha.4.pre.0 (2021-11-09)](#080-alpha4pre0-2021-11-09) - - [Breaking Changes](#breaking-changes-11) - - [Bug Fixes](#bug-fixes-15) + - [Breaking Changes](#breaking-changes-10) + - [Bug Fixes](#bug-fixes-16) - [Code Generation](#code-generation-16) - - [Documentation](#documentation-13) - - [Features](#features-13) - - [Tests](#tests-12) + - [Documentation](#documentation-14) + - [Features](#features-12) + - [Tests](#tests-11) - [0.8.0-alpha.3 (2021-10-28)](#080-alpha3-2021-10-28) - - [Bug Fixes](#bug-fixes-16) + - [Bug Fixes](#bug-fixes-17) - [Code Generation](#code-generation-17) - [0.8.0-alpha.2 (2021-10-28)](#080-alpha2-2021-10-28) - [Code Generation](#code-generation-18) - [0.8.0-alpha.1 (2021-10-27)](#080-alpha1-2021-10-27) - - [Breaking Changes](#breaking-changes-12) - - [Bug Fixes](#bug-fixes-17) + - [Breaking Changes](#breaking-changes-11) + - [Bug Fixes](#bug-fixes-18) - [Code Generation](#code-generation-19) - [Code Refactoring](#code-refactoring-5) - - [Documentation](#documentation-14) - - [Features](#features-14) + - [Documentation](#documentation-15) + - [Features](#features-13) - [Reverts](#reverts-2) - - [Tests](#tests-13) + - [Tests](#tests-12) - [Unclassified](#unclassified-8) - [0.7.6-alpha.1 (2021-09-12)](#076-alpha1-2021-09-12) - [Code Generation](#code-generation-20) - [0.7.5-alpha.1 (2021-09-11)](#075-alpha1-2021-09-11) - [Code Generation](#code-generation-21) - [0.7.4-alpha.1 (2021-09-09)](#074-alpha1-2021-09-09) - - [Bug Fixes](#bug-fixes-18) + - [Bug Fixes](#bug-fixes-19) - [Code Generation](#code-generation-22) - - [Documentation](#documentation-15) - - [Features](#features-15) - - [Tests](#tests-14) + - [Documentation](#documentation-16) + - [Features](#features-14) + - [Tests](#tests-13) - [0.7.3-alpha.1 (2021-08-28)](#073-alpha1-2021-08-28) - - [Bug Fixes](#bug-fixes-19) + - [Bug Fixes](#bug-fixes-20) - [Code Generation](#code-generation-23) - - [Documentation](#documentation-16) - - [Features](#features-16) + - [Documentation](#documentation-17) + - [Features](#features-15) - [0.7.1-alpha.1 (2021-07-22)](#071-alpha1-2021-07-22) - - [Bug Fixes](#bug-fixes-20) + - [Bug Fixes](#bug-fixes-21) - [Code Generation](#code-generation-24) - - [Documentation](#documentation-17) - - [Tests](#tests-15) + - [Documentation](#documentation-18) + - [Tests](#tests-14) - [0.7.0-alpha.1 (2021-07-13)](#070-alpha1-2021-07-13) - - [Breaking Changes](#breaking-changes-13) - - [Bug Fixes](#bug-fixes-21) + - [Breaking Changes](#breaking-changes-12) + - [Bug Fixes](#bug-fixes-22) - [Code Generation](#code-generation-25) - [Code Refactoring](#code-refactoring-6) - - [Documentation](#documentation-18) - - [Features](#features-17) - - [Tests](#tests-16) + - [Documentation](#documentation-19) + - [Features](#features-16) + - [Tests](#tests-15) - [Unclassified](#unclassified-9) - [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17) - - [Breaking Changes](#breaking-changes-14) - - [Bug Fixes](#bug-fixes-22) + - [Breaking Changes](#breaking-changes-13) + - [Bug Fixes](#bug-fixes-23) - [Code Generation](#code-generation-26) - [Code Refactoring](#code-refactoring-7) - [0.6.2-alpha.1 (2021-05-14)](#062-alpha1-2021-05-14) - [Code Generation](#code-generation-27) - - [Documentation](#documentation-19) + - [Documentation](#documentation-20) - [0.6.1-alpha.1 (2021-05-11)](#061-alpha1-2021-05-11) - [Code Generation](#code-generation-28) - - [Features](#features-18) + - [Features](#features-17) - [0.6.0-alpha.2 (2021-05-07)](#060-alpha2-2021-05-07) - - [Bug Fixes](#bug-fixes-23) + - [Bug Fixes](#bug-fixes-24) - [Code Generation](#code-generation-29) - - [Features](#features-19) + - [Features](#features-18) - [0.6.0-alpha.1 (2021-05-05)](#060-alpha1-2021-05-05) - - [Breaking Changes](#breaking-changes-15) - - [Bug Fixes](#bug-fixes-24) + - [Breaking Changes](#breaking-changes-14) + - [Bug Fixes](#bug-fixes-25) - [Code Generation](#code-generation-30) - [Code Refactoring](#code-refactoring-8) - - [Documentation](#documentation-20) - - [Features](#features-20) - - [Tests](#tests-17) + - [Documentation](#documentation-21) + - [Features](#features-19) + - [Tests](#tests-16) - [Unclassified](#unclassified-10) - [0.5.5-alpha.1 (2020-12-09)](#055-alpha1-2020-12-09) - - [Bug Fixes](#bug-fixes-25) + - [Bug Fixes](#bug-fixes-26) - [Code Generation](#code-generation-31) - - [Documentation](#documentation-21) - - [Features](#features-21) - - [Tests](#tests-18) + - [Documentation](#documentation-22) + - [Features](#features-20) + - [Tests](#tests-17) - [Unclassified](#unclassified-11) - [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11) - - [Bug Fixes](#bug-fixes-26) + - [Bug Fixes](#bug-fixes-27) - [Code Generation](#code-generation-32) - [Code Refactoring](#code-refactoring-9) - - [Documentation](#documentation-22) - - [Features](#features-22) + - [Documentation](#documentation-23) + - [Features](#features-21) - [0.5.3-alpha.1 (2020-10-27)](#053-alpha1-2020-10-27) - - [Bug Fixes](#bug-fixes-27) + - [Bug Fixes](#bug-fixes-28) - [Code Generation](#code-generation-33) - - [Documentation](#documentation-23) - - [Features](#features-23) - - [Tests](#tests-19) + - [Documentation](#documentation-24) + - [Features](#features-22) + - [Tests](#tests-18) - [0.5.2-alpha.1 (2020-10-22)](#052-alpha1-2020-10-22) - - [Bug Fixes](#bug-fixes-28) + - [Bug Fixes](#bug-fixes-29) - [Code Generation](#code-generation-34) - - [Documentation](#documentation-24) - - [Tests](#tests-20) + - [Documentation](#documentation-25) + - [Tests](#tests-19) - [0.5.1-alpha.1 (2020-10-20)](#051-alpha1-2020-10-20) - - [Bug Fixes](#bug-fixes-29) + - [Bug Fixes](#bug-fixes-30) - [Code Generation](#code-generation-35) - - [Documentation](#documentation-25) - - [Features](#features-24) - - [Tests](#tests-21) + - [Documentation](#documentation-26) + - [Features](#features-23) + - [Tests](#tests-20) - [Unclassified](#unclassified-12) - [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15) - - [Breaking Changes](#breaking-changes-16) - - [Bug Fixes](#bug-fixes-30) + - [Breaking Changes](#breaking-changes-15) + - [Bug Fixes](#bug-fixes-31) - [Code Generation](#code-generation-36) - [Code Refactoring](#code-refactoring-10) - - [Documentation](#documentation-26) - - [Features](#features-25) - - [Tests](#tests-22) + - [Documentation](#documentation-27) + - [Features](#features-24) + - [Tests](#tests-21) - [Unclassified](#unclassified-13) - [0.4.6-alpha.1 (2020-07-13)](#046-alpha1-2020-07-13) - - [Bug Fixes](#bug-fixes-31) + - [Bug Fixes](#bug-fixes-32) - [Code Generation](#code-generation-37) - [0.4.5-alpha.1 (2020-07-13)](#045-alpha1-2020-07-13) - - [Bug Fixes](#bug-fixes-32) + - [Bug Fixes](#bug-fixes-33) - [Code Generation](#code-generation-38) - [0.4.4-alpha.1 (2020-07-10)](#044-alpha1-2020-07-10) - - [Bug Fixes](#bug-fixes-33) + - [Bug Fixes](#bug-fixes-34) - [Code Generation](#code-generation-39) - - [Documentation](#documentation-27) + - [Documentation](#documentation-28) - [0.4.3-alpha.1 (2020-07-08)](#043-alpha1-2020-07-08) - - [Bug Fixes](#bug-fixes-34) + - [Bug Fixes](#bug-fixes-35) - [Code Generation](#code-generation-40) - [0.4.2-alpha.1 (2020-07-08)](#042-alpha1-2020-07-08) - - [Bug Fixes](#bug-fixes-35) + - [Bug Fixes](#bug-fixes-36) - [Code Generation](#code-generation-41) - [0.4.0-alpha.1 (2020-07-08)](#040-alpha1-2020-07-08) - - [Breaking Changes](#breaking-changes-17) - - [Bug Fixes](#bug-fixes-36) + - [Breaking Changes](#breaking-changes-16) + - [Bug Fixes](#bug-fixes-37) - [Code Generation](#code-generation-42) - [Code Refactoring](#code-refactoring-11) - - [Documentation](#documentation-28) - - [Features](#features-26) + - [Documentation](#documentation-29) + - [Features](#features-25) - [Unclassified](#unclassified-14) - [0.3.0-alpha.1 (2020-05-15)](#030-alpha1-2020-05-15) - - [Breaking Changes](#breaking-changes-18) - - [Bug Fixes](#bug-fixes-37) + - [Breaking Changes](#breaking-changes-17) + - [Bug Fixes](#bug-fixes-38) - [Chores](#chores) - [Code Refactoring](#code-refactoring-12) - - [Documentation](#documentation-29) - - [Features](#features-27) + - [Documentation](#documentation-30) + - [Features](#features-26) - [Unclassified](#unclassified-15) - [0.2.1-alpha.1 (2020-05-05)](#021-alpha1-2020-05-05) - [Chores](#chores-1) - - [Documentation](#documentation-30) + - [Documentation](#documentation-31) - [0.2.0-alpha.2 (2020-05-04)](#020-alpha2-2020-05-04) - - [Breaking Changes](#breaking-changes-19) - - [Bug Fixes](#bug-fixes-38) + - [Breaking Changes](#breaking-changes-18) + - [Bug Fixes](#bug-fixes-39) - [Chores](#chores-2) - [Code Refactoring](#code-refactoring-13) - - [Documentation](#documentation-31) - - [Features](#features-28) + - [Documentation](#documentation-32) + - [Features](#features-27) - [Unclassified](#unclassified-16) - [0.1.1-alpha.1 (2020-02-18)](#011-alpha1-2020-02-18) - - [Bug Fixes](#bug-fixes-39) + - [Bug Fixes](#bug-fixes-40) - [Code Refactoring](#code-refactoring-14) - - [Documentation](#documentation-32) + - [Documentation](#documentation-33) - [0.1.0-alpha.6 (2020-02-16)](#010-alpha6-2020-02-16) - - [Bug Fixes](#bug-fixes-40) + - [Bug Fixes](#bug-fixes-41) - [Code Refactoring](#code-refactoring-15) - - [Documentation](#documentation-33) - - [Features](#features-29) -- [0.1.0-alpha.5 (2020-02-06)](#010-alpha5-2020-02-06) - [Documentation](#documentation-34) - - [Features](#features-30) + - [Features](#features-28) +- [0.1.0-alpha.5 (2020-02-06)](#010-alpha5-2020-02-06) + - [Documentation](#documentation-35) + - [Features](#features-29) - [0.1.0-alpha.4 (2020-02-06)](#010-alpha4-2020-02-06) - [Continuous Integration](#continuous-integration) - - [Documentation](#documentation-35) + - [Documentation](#documentation-36) - [0.1.0-alpha.3 (2020-02-06)](#010-alpha3-2020-02-06) - [Continuous Integration](#continuous-integration-1) - [0.1.0-alpha.2 (2020-02-03)](#010-alpha2-2020-02-03) - - [Bug Fixes](#bug-fixes-41) - - [Documentation](#documentation-36) - - [Features](#features-31) + - [Bug Fixes](#bug-fixes-42) + - [Documentation](#documentation-37) + - [Features](#features-30) - [Unclassified](#unclassified-17) - [0.1.0-alpha.1 (2020-01-31)](#010-alpha1-2020-01-31) - - [Documentation](#documentation-37) + - [Documentation](#documentation-38) - [0.0.3-alpha.15 (2020-01-31)](#003-alpha15-2020-01-31) - [Unclassified](#unclassified-18) - [0.0.3-alpha.14 (2020-01-31)](#003-alpha14-2020-01-31) @@ -331,12 +330,119 @@ - [Unclassified](#unclassified-29) - [0.0.1-alpha.3 (2020-01-28)](#001-alpha3-2020-01-28) - [Continuous Integration](#continuous-integration-6) - - [Documentation](#documentation-38) + - [Documentation](#documentation-39) - [Unclassified](#unclassified-30) -# [](https://github.com/ory/kratos/compare/v1.3.0-pre.0...v) (2024-09-25) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-03) + +### Bug Fixes + +- Explicity set updated_at field when updating identity + ([#4131](https://github.com/ory/kratos/issues/4131)) + ([66afac1](https://github.com/ory/kratos/commit/66afac173dc08b1d6666b107cf7050a2b0b27774)) + +### Documentation + +- Add return_to query parameter to OAS Verification Flow for Native Apps + ([#4086](https://github.com/ory/kratos/issues/4086)) + ([b22135f](https://github.com/ory/kratos/commit/b22135fa05d7fb47dfeaccd7cdc183d16921a7ac)) + +# [1.3.0](https://github.com/ory/kratos/compare/v1.2.0...v1.3.0) (2024-09-26) + +We are thrilled to announce the release +of [Ory Kratos v1.3.0](https://www.ory.sh/kratos)! This release includes +significant updates, enhancements, and fixes to improve your experience with Ory +Kratos. + +![Ory Kratos 1.3.0 Release](https://www.ory.sh/images/newsletter/kratos-1.3.0/kratos-1.3-release.png) + +Enhance your sign-in experience with Identifier First Authentication. This +feature allows users to first identify themselves (e.g., by providing their +email or username) and then proceed with the chosen authentication method, +whether it be OTP code, passkeys, passwords, or social login. By streamlining +the sign-in process, users can select the authentication method that best suits +their needs, reducing friction and enhancing security. Identifier First +Authentication improves user flow and reduces the likelihood of errors, +resulting in a more user-friendly and efficient login experience. + +![Identifier First Authentication](https://www.ory.sh/images/newsletter/kratos-1.3.0/identifier-first-demo.png) + +The UI for OpenID Connect (OIDC) account linking has been improved to provide +better user guidance and error messages during the linking process. As a result, +account linking error rates have dropped significantly, making it easier for +users to link multiple identities (e.g., social login and email-based accounts) +to the same profile. This improvement enhances user convenience, reduces support +inquiries, and offers a seamless multi-account experience. + +You can now use Salesforce as an identity provider, expanding the range of +supported identity providers. This integration allows organizations already +using Salesforce for identity management to leverage their existing +infrastructure, simplifying user management and enhancing the authentication +experience. + +Social sign-in has been enhanced with better detection and handling of +double-submit issues, especially for platforms like Facebook and Apple mobile +login. These changes make the social login process more reliable, reducing +errors and improving the user experience. Additionally, Ory Kratos now supports +social providers in credential discovery, offering more flexibility during +sign-up and sign-in flows. + +One-Time Password (OTP) MFA has been improved with more robust handling of +code-based authentication. The enhancements ensure a smoother flow when using +OTP for multi-factor authentication (MFA), providing clearer guidance to users +and improving fallback mechanisms. These updates help to prevent users from +being locked out due to misconfigurations or errors during the MFA process, +increasing security without compromising user convenience. + +- **Deprecated `via` Parameter for SMS 2FA**: The `via` parameter is now + deprecated when performing SMS 2FA. If not included, users will see all their + phone/email addresses to perform the flow. This parameter will be removed in a + future version. Ensure your identity schema has the appropriate code + configuration for passwordless or 2FA login. +- **Endpoint Change**: The `/admin/session/.../extend` endpoint will now return + 204 No Content for new Ory Network projects. Returning 200 with the session + body will be deprecated in future versions. + +- **SDK Enhancements**: Added new methods and support for additional actions in + the SDK, improving integration capabilities. +- **Password Migration Hook**: Added a password migration hook to facilitate + migrating passwords where the hash is unavailable, easing the transition to + Ory Kratos. +- **Partially Failing Batch Inserts:** When batch-inserting multiple identities, + conflicts or validation errors of a subset of identities in the batch still + allow the rest of the identities to be inserted. The returned JSON contains + the error details that led to the failure. + +- **Security Fixes**: Fixed a security vulnerability where the `code` method did + not respect the `highest_available`setting. Refer to + the [security advisory](https://github.com/ory/kratos/security/advisories/GHSA-wc43-73w7-x2f5) for + more details. +- **Session Extension Issues**: Fixed issues related to session extension to + prevent long response times on `/session/whoami` when extending sessions + simultaneously. +- **OIDC and Social Sign-In**: Fixed UI and error handling for OpenID Connect + and social sign-in flows, improving the overall experience. +- **Credential Identifier Handling**: Corrected handling of code credential + identifiers, ensuring proper detection of phone numbers and correct + functioning of SMS/email MFA. +- **Concurrent Updates for Webhooks**: Fixed concurrent map update issues for + webhook headers, improving webhook reliability. + +- **Passwordless & 2FA Login**: Before upgrading, ensure your identity schema + has the appropriate code configuration when using the code method for + passwordless or 2FA login. +- **Code Method for 2FA**: If you use the code method for 2FA or 1FA login but + haven't configured the code identifier, + set `selfservice.methods.code.config.missing_credential_fallback_enabled` to `true` to + avoid user lockouts. + +We hope you enjoy the new features and improvements in Ory Kratos v1.3.0. Please +remember to leave a [GitHub star](https://github.com/ory/kratos) and check out +our other [open-source projects](https://github.com/ory). Your feedback is +valuable to us, so join the [Ory community](https://slack.ory.sh/) and help us +shape the future of identity management. ## Breaking Changes @@ -345,24 +451,6 @@ When using two-step registration, it was previously possible to send documented in the SDK API yet. Going forward, please instead use `screen=previous`. -### Features - -- Change `method=profile:back` to `screen=previous` - ([#4119](https://github.com/ory/kratos/issues/4119)) - ([2cd8483](https://github.com/ory/kratos/commit/2cd8483e809170d0524fe6a5d13837108d29fa54)) - -### Tests - -- Additional code credential test case - ([#4122](https://github.com/ory/kratos/issues/4122)) - ([4f2c854](https://github.com/ory/kratos/commit/4f2c8542ab04b88c7112d7b564d91bcfd8f5791a)) - -# [1.3.0-pre.0](https://github.com/ory/kratos/compare/v1.2.0...v1.3.0-pre.0) (2024-09-25) - -autogen: pin v1.3.0-pre.0 release commit - -## Breaking Changes - Please note that the `via` parameter is deprecated when performing SMS 2FA. It will be removed in a future version. If the parameter is not included in the request, the user will see all their phone/email addresses from which to perform @@ -472,6 +560,9 @@ body in the future. detect the user's phone number, and where SMS/email MFA would not properly work with the `highest_available` setting. +- Passthrough correct organization ID to CompletedLoginForWithProvider + ([#4124](https://github.com/ory/kratos/issues/4124)) + ([ad1acd5](https://github.com/ory/kratos/commit/ad1acd51d8dd7582b05a3078b92f73970e1e2715)) - Password migration hook config ([#4001](https://github.com/ory/kratos/issues/4001)) ([50deedf](https://github.com/ory/kratos/commit/50deedfeecf7adbc948521371b181306a0c26cf1)): @@ -527,8 +618,8 @@ body in the future. ### Code Generation -- Pin v1.3.0-pre.0 release commit - ([72aae5b](https://github.com/ory/kratos/commit/72aae5b625ca15552a81bcf956c96994fb716198)) +- Pin v1.3.0 release commit + ([0a49fd0](https://github.com/ory/kratos/commit/0a49fd05245f179501b117163cd574786f287fe8)) ### Documentation @@ -593,6 +684,9 @@ body in the future. - Better detection if credentials exist on identifier first login ([#3963](https://github.com/ory/kratos/issues/3963)) ([42ade94](https://github.com/ory/kratos/commit/42ade94e32a9a7ad6c0bda785e86d7209c46d8bb)) +- Change `method=profile:back` to `screen=previous` + ([#4119](https://github.com/ory/kratos/issues/4119)) + ([2cd8483](https://github.com/ory/kratos/commit/2cd8483e809170d0524fe6a5d13837108d29fa54)) - Clarify session extend behavior ([#3962](https://github.com/ory/kratos/issues/3962)) ([af5ea35](https://github.com/ory/kratos/commit/af5ea35759e74d7a1637823abcc21dc8e3e39a9d)) @@ -718,6 +812,9 @@ body in the future. ([8b68163](https://github.com/ory/kratos/commit/8b68163a3f293f7dceb58397f0ef555f1d8fd7c3)) - Add tests for idfirst ([5f76c15](https://github.com/ory/kratos/commit/5f76c1565e89bfb99f23c3f0f3a9beadbdfa270c)) +- Additional code credential test case + ([#4122](https://github.com/ory/kratos/issues/4122)) + ([4f2c854](https://github.com/ory/kratos/commit/4f2c8542ab04b88c7112d7b564d91bcfd8f5791a)) - Deflake and parallelize persister tests ([#3953](https://github.com/ory/kratos/issues/3953)) ([61f87d9](https://github.com/ory/kratos/commit/61f87d90bd67e5bb1f00ee110d986e4f72fc4c91)) From 462cea91448a00a0db21e20c2c347bf74957dc8f Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Fri, 4 Oct 2024 12:00:11 +0200 Subject: [PATCH 020/158] feat: passwordless SMS and expiry notice in code / link templates (#4104) This feature allows Ory Kratos to use the SMS gateway for login and registration with code via SMS. Additionally, the default email and sms templates have been updated. We now also expose `ExpiresInMinutes` / `expires_in_minutes` in the templates, making it easier to remind the user how long the code or link is valid for. Closes https://github.com/ory/kratos/issues/1570 Closes https://github.com/ory/kratos/issues/3779 --- cmd/clidoc/main.go | 2 +- courier/courier.go | 36 +-- courier/courier_dispatcher.go | 36 ++- courier/sms_templates.go | 6 + .../login_code/valid/email.body.gotmpl | 6 +- .../valid/email.body.plaintext.gotmpl | 6 +- .../login_code/valid/email.subject.gotmpl | 2 +- .../login_code/valid/sms.body.gotmpl | 2 + .../recovery/valid/email.body.gotmpl | 6 +- .../valid/email.body.plaintext.gotmpl | 7 +- .../recovery_code/valid/email.body.gotmpl | 6 +- .../valid/email.body.plaintext.gotmpl | 6 +- .../recovery_code/valid/email.subject.gotmpl | 2 +- .../registration_code/valid/email.body.gotmpl | 6 +- .../valid/email.body.plaintext.gotmpl | 6 +- .../valid/email.subject.gotmpl | 2 +- .../registration_code/valid/sms.body.gotmpl | 3 + .../verification/invalid/email.body.gotmpl | 4 +- .../invalid/email.body.plaintext.gotmpl | 4 +- .../verification/valid/email.body.gotmpl | 4 +- .../valid/email.body.plaintext.gotmpl | 3 +- .../verification_code/valid/email.body.gotmpl | 6 +- .../valid/email.body.plaintext.gotmpl | 6 +- .../valid/email.subject.gotmpl | 2 +- .../verification_code/valid/sms.body.gotmpl | 2 + courier/template/email/login_code_valid.go | 1 + courier/template/email/recovery_code_valid.go | 1 + courier/template/email/recovery_valid.go | 1 + .../template/email/registration_code_valid.go | 1 + .../template/email/verification_code_valid.go | 1 + courier/template/email/verification_valid.go | 1 + courier/template/sms/login_code_valid.go | 1 + courier/template/sms/login_code_valid_test.go | 2 +- .../template/sms/registration_code_valid.go | 55 +++++ .../sms/registration_code_valid_test.go | 37 +++ courier/template/sms/verification_code.go | 1 + .../template/sms/verification_code_test.go | 2 +- driver/config/config.go | 6 + embedx/config.schema.json | 3 + internal/client-go/go.sum | 1 + internal/testhelpers/courier.go | 3 +- .../testdata/20200402142539_testdata.sql | 8 +- .../testdata/20210307130558_testdata.sql | 2 +- .../testdata/20221205095201_testdata.sql | 4 +- selfservice/strategy/code/code_sender.go | 37 ++- selfservice/strategy/code/code_sender_test.go | 4 +- selfservice/strategy/code/strategy.go | 2 +- .../strategy/code/strategy_login_test.go | 56 ++--- .../strategy/code/strategy_recovery_test.go | 52 ++-- .../code/strategy_registration_test.go | 34 +-- .../code/strategy_verification_test.go | 26 +- selfservice/strategy/link/sender.go | 4 +- .../strategy/link/strategy_recovery_test.go | 4 +- .../link/strategy_verification_test.go | 4 +- selfservice/strategy/profile/strategy_test.go | 2 +- test/e2e/cypress/helpers/index.ts | 2 +- .../profiles/code/login/error.spec.ts | 5 +- .../profiles/code/registration/error.spec.ts | 20 +- .../code/registration/success.spec.ts | 20 +- .../profiles/oidc-provider/login.spec.ts | 16 +- .../profiles/recovery/code/errors.spec.ts | 4 +- .../two-steps/registration/code.spec.ts | 20 +- .../verification/registration/errors.spec.ts | 2 +- .../verification/settings/error.spec.ts | 2 +- .../verification/verify/errors.spec.ts | 7 +- test/e2e/cypress/support/commands.ts | 27 +- test/e2e/cypress/support/index.d.ts | 1 + test/e2e/mock/httptarget/go.mod | 10 + test/e2e/mock/httptarget/go.sum | 16 ++ test/e2e/mock/httptarget/main.go | 79 ++++++ test/e2e/package-lock.json | 230 +++++++++++++++--- test/e2e/package.json | 4 +- test/e2e/playwright.config.ts | 6 + test/e2e/playwright/actions/identity.ts | 61 +++++ test/e2e/playwright/actions/webhook.ts | 57 +++++ test/e2e/playwright/fixtures/index.ts | 32 +-- test/e2e/playwright/fixtures/schemas/sms.ts | 35 +++ test/e2e/playwright/lib/config.ts | 14 ++ .../models/elements/registration.ts | 45 ++++ .../playwright/tests/desktop/code/sms.spec.ts | 116 +++++++++ text/id.go | 2 +- text/message_login.go | 6 +- text/message_registration.go | 2 +- 83 files changed, 1042 insertions(+), 326 deletions(-) create mode 100644 courier/template/courier/builtin/templates/registration_code/valid/sms.body.gotmpl create mode 100644 courier/template/sms/registration_code_valid.go create mode 100644 courier/template/sms/registration_code_valid_test.go create mode 100644 test/e2e/mock/httptarget/go.mod create mode 100644 test/e2e/mock/httptarget/go.sum create mode 100644 test/e2e/mock/httptarget/main.go create mode 100644 test/e2e/playwright/actions/identity.ts create mode 100644 test/e2e/playwright/actions/webhook.ts create mode 100644 test/e2e/playwright/fixtures/schemas/sms.ts create mode 100644 test/e2e/playwright/lib/config.ts create mode 100644 test/e2e/playwright/models/elements/registration.ts create mode 100644 test/e2e/playwright/tests/desktop/code/sms.spec.ts diff --git a/cmd/clidoc/main.go b/cmd/clidoc/main.go index f7658bd6e086..a2b1850a87fa 100644 --- a/cmd/clidoc/main.go +++ b/cmd/clidoc/main.go @@ -162,7 +162,7 @@ func init() { "NewInfoSelfServiceLoginContinue": text.NewInfoSelfServiceLoginContinue(), "NewErrorValidationSuchNoWebAuthnUser": text.NewErrorValidationSuchNoWebAuthnUser(), "NewRegistrationEmailWithCodeSent": text.NewRegistrationEmailWithCodeSent(), - "NewLoginEmailWithCodeSent": text.NewLoginEmailWithCodeSent(), + "NewLoginCodeSent": text.NewLoginCodeSent(), "NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed": text.NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed(), "NewErrorValidationLoginCodeInvalidOrAlreadyUsed": text.NewErrorValidationLoginCodeInvalidOrAlreadyUsed(), "NewErrorValidationNoCodeUser": text.NewErrorValidationNoCodeUser(), diff --git a/courier/courier.go b/courier/courier.go index 231b4007060f..cb5acede6aee 100644 --- a/courier/courier.go +++ b/courier/courier.go @@ -47,10 +47,10 @@ type ( } courier struct { - courierChannels map[string]Channel - deps Dependencies - failOnDispatchError bool - backoff backoff.BackOff + deps Dependencies + failOnDispatchError bool + backoff backoff.BackOff + newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error) } ) @@ -58,31 +58,11 @@ func NewCourier(ctx context.Context, deps Dependencies) (Courier, error) { return NewCourierWithCustomTemplates(ctx, deps, NewEmailTemplateFromMessage) } -func NewCourierWithCustomTemplates(ctx context.Context, deps Dependencies, newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)) (Courier, error) { - cs, err := deps.CourierConfig().CourierChannels(ctx) - if err != nil { - return nil, err - } - channels := make(map[string]Channel, len(cs)) - for _, c := range cs { - switch c.Type { - case "smtp": - ch, err := NewSMTPChannelWithCustomTemplates(deps, c.SMTPConfig, newEmailTemplateFromMessage) - if err != nil { - return nil, err - } - channels[ch.ID()] = ch - case "http": - channels[c.ID] = newHttpChannel(c.ID, c.RequestConfig, deps) - default: - return nil, errors.Errorf("unknown courier channel type: %s", c.Type) - } - } - +func NewCourierWithCustomTemplates(_ context.Context, deps Dependencies, newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)) (Courier, error) { return &courier{ - deps: deps, - backoff: backoff.NewExponentialBackOff(), - courierChannels: channels, + deps: deps, + backoff: backoff.NewExponentialBackOff(), + newEmailTemplateFromMessage: newEmailTemplateFromMessage, }, nil } diff --git a/courier/courier_dispatcher.go b/courier/courier_dispatcher.go index 47399369c3c3..a0f75954d68e 100644 --- a/courier/courier_dispatcher.go +++ b/courier/courier_dispatcher.go @@ -9,6 +9,33 @@ import ( "github.com/pkg/errors" ) +func (c *courier) channels(ctx context.Context, id string) (Channel, error) { + cs, err := c.deps.CourierConfig().CourierChannels(ctx) + if err != nil { + return nil, err + } + + for _, channel := range cs { + if channel.ID != id { + continue + } + switch channel.Type { + case "smtp": + courierChannel, err := NewSMTPChannelWithCustomTemplates(c.deps, channel.SMTPConfig, c.newEmailTemplateFromMessage) + if err != nil { + return nil, err + } + return courierChannel, nil + case "http": + return newHttpChannel(channel.ID, channel.RequestConfig, c.deps), nil + default: + return nil, errors.Errorf("unknown courier channel type: %s", channel.Type) + } + } + + return nil, errors.Errorf("no courier channels configured") +} + func (c *courier) DispatchMessage(ctx context.Context, msg Message) error { logger := c.deps.Logger(). WithField("message_id", msg.ID). @@ -24,9 +51,9 @@ func (c *courier) DispatchMessage(ctx context.Context, msg Message) error { return err } - channel, ok := c.courierChannels[msg.Channel.String()] - if !ok { - return errors.Errorf("message %s has unknown channel %q", msg.ID.String(), msg.Channel) + channel, err := c.channels(ctx, msg.Channel.String()) + if err != nil { + return err } logger = logger. @@ -80,6 +107,9 @@ func (c *courier) DispatchQueue(ctx context.Context) error { logger. Warnf(`Message was abandoned because it did not deliver after %d attempts`, msg.SendCount) } else if err := c.DispatchMessage(ctx, msg); err != nil { + logger. + WithError(err). + Warn(`Unable to dispatch message.`) if err := c.deps.CourierPersister().RecordDispatch(ctx, msg.ID, CourierMessageDispatchStatusFailed, err); err != nil { logger. WithError(err). diff --git a/courier/sms_templates.go b/courier/sms_templates.go index b560542d53c9..12c55ceed751 100644 --- a/courier/sms_templates.go +++ b/courier/sms_templates.go @@ -40,6 +40,12 @@ func NewSMSTemplateFromMessage(d template.Dependencies, m Message) (SMSTemplate, return nil, err } return sms.NewLoginCodeValid(d, &t), nil + case template.TypeRegistrationCodeValid: + var t sms.RegistrationCodeValidModel + if err := json.Unmarshal(m.TemplateData, &t); err != nil { + return nil, err + } + return sms.NewRegistrationCodeValid(d, &t), nil default: return nil, errors.Errorf("received unexpected message template type: %s", m.TemplateType) diff --git a/courier/template/courier/builtin/templates/login_code/valid/email.body.gotmpl b/courier/template/courier/builtin/templates/login_code/valid/email.body.gotmpl index 505684b9849b..0b1b3abd30b8 100644 --- a/courier/template/courier/builtin/templates/login_code/valid/email.body.gotmpl +++ b/courier/template/courier/builtin/templates/login_code/valid/email.body.gotmpl @@ -1,5 +1,5 @@ -Hi, - -please login to your account by entering the following code: +Login to your account with the following code: {{ .LoginCode }} + +It expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/login_code/valid/email.body.plaintext.gotmpl b/courier/template/courier/builtin/templates/login_code/valid/email.body.plaintext.gotmpl index 505684b9849b..0b1b3abd30b8 100644 --- a/courier/template/courier/builtin/templates/login_code/valid/email.body.plaintext.gotmpl +++ b/courier/template/courier/builtin/templates/login_code/valid/email.body.plaintext.gotmpl @@ -1,5 +1,5 @@ -Hi, - -please login to your account by entering the following code: +Login to your account with the following code: {{ .LoginCode }} + +It expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/login_code/valid/email.subject.gotmpl b/courier/template/courier/builtin/templates/login_code/valid/email.subject.gotmpl index 19d7bfd57d49..0015e66086c9 100644 --- a/courier/template/courier/builtin/templates/login_code/valid/email.subject.gotmpl +++ b/courier/template/courier/builtin/templates/login_code/valid/email.subject.gotmpl @@ -1 +1 @@ -Login to your account +Use code {{ .LoginCode }} to log in diff --git a/courier/template/courier/builtin/templates/login_code/valid/sms.body.gotmpl b/courier/template/courier/builtin/templates/login_code/valid/sms.body.gotmpl index 5b88dde9a382..ca6d35ca9eac 100644 --- a/courier/template/courier/builtin/templates/login_code/valid/sms.body.gotmpl +++ b/courier/template/courier/builtin/templates/login_code/valid/sms.body.gotmpl @@ -1 +1,3 @@ Your login code is: {{ .LoginCode }} + +It expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/recovery/valid/email.body.gotmpl b/courier/template/courier/builtin/templates/recovery/valid/email.body.gotmpl index 2dbc8d53550b..c4ac7a1d6404 100644 --- a/courier/template/courier/builtin/templates/recovery/valid/email.body.gotmpl +++ b/courier/template/courier/builtin/templates/recovery/valid/email.body.gotmpl @@ -1,5 +1,5 @@ -Hi, - -please recover access to your account by clicking the following link: +Recover access to your account by clicking the following link: {{ .RecoveryURL }} + +If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/recovery/valid/email.body.plaintext.gotmpl b/courier/template/courier/builtin/templates/recovery/valid/email.body.plaintext.gotmpl index d41e8963cc50..7bea438ffffa 100644 --- a/courier/template/courier/builtin/templates/recovery/valid/email.body.plaintext.gotmpl +++ b/courier/template/courier/builtin/templates/recovery/valid/email.body.plaintext.gotmpl @@ -1,5 +1,6 @@ -Hi, - -please recover access to your account by clicking the following link: +Recover access to your account by clicking the following link: {{ .RecoveryURL }} + +If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes. + diff --git a/courier/template/courier/builtin/templates/recovery_code/valid/email.body.gotmpl b/courier/template/courier/builtin/templates/recovery_code/valid/email.body.gotmpl index 5037d753998e..8427467b81c8 100644 --- a/courier/template/courier/builtin/templates/recovery_code/valid/email.body.gotmpl +++ b/courier/template/courier/builtin/templates/recovery_code/valid/email.body.gotmpl @@ -1,5 +1,5 @@ -Hi, - -please recover access to your account by entering the following code: +Recover access to your account by entering the following code: {{ .RecoveryCode }} + +If this was not you, do nothing. This code expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/recovery_code/valid/email.body.plaintext.gotmpl b/courier/template/courier/builtin/templates/recovery_code/valid/email.body.plaintext.gotmpl index 5037d753998e..8427467b81c8 100644 --- a/courier/template/courier/builtin/templates/recovery_code/valid/email.body.plaintext.gotmpl +++ b/courier/template/courier/builtin/templates/recovery_code/valid/email.body.plaintext.gotmpl @@ -1,5 +1,5 @@ -Hi, - -please recover access to your account by entering the following code: +Recover access to your account by entering the following code: {{ .RecoveryCode }} + +If this was not you, do nothing. This code expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/recovery_code/valid/email.subject.gotmpl b/courier/template/courier/builtin/templates/recovery_code/valid/email.subject.gotmpl index 9a47d5f5814a..0890daa9df87 100644 --- a/courier/template/courier/builtin/templates/recovery_code/valid/email.subject.gotmpl +++ b/courier/template/courier/builtin/templates/recovery_code/valid/email.subject.gotmpl @@ -1 +1 @@ -Recover access to your account +Use code {{ .RecoveryCode }} to recover access to your account diff --git a/courier/template/courier/builtin/templates/registration_code/valid/email.body.gotmpl b/courier/template/courier/builtin/templates/registration_code/valid/email.body.gotmpl index 6b9c31799995..b839053d8afb 100644 --- a/courier/template/courier/builtin/templates/registration_code/valid/email.body.gotmpl +++ b/courier/template/courier/builtin/templates/registration_code/valid/email.body.gotmpl @@ -1,5 +1,5 @@ -Hi, - -please complete your account registration by entering the following code: +Complete your account registration with the following code: {{ .RegistrationCode }} + +This code expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/registration_code/valid/email.body.plaintext.gotmpl b/courier/template/courier/builtin/templates/registration_code/valid/email.body.plaintext.gotmpl index 6b9c31799995..b839053d8afb 100644 --- a/courier/template/courier/builtin/templates/registration_code/valid/email.body.plaintext.gotmpl +++ b/courier/template/courier/builtin/templates/registration_code/valid/email.body.plaintext.gotmpl @@ -1,5 +1,5 @@ -Hi, - -please complete your account registration by entering the following code: +Complete your account registration with the following code: {{ .RegistrationCode }} + +This code expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/registration_code/valid/email.subject.gotmpl b/courier/template/courier/builtin/templates/registration_code/valid/email.subject.gotmpl index 0f36292619ef..fd48d99a2b19 100644 --- a/courier/template/courier/builtin/templates/registration_code/valid/email.subject.gotmpl +++ b/courier/template/courier/builtin/templates/registration_code/valid/email.subject.gotmpl @@ -1 +1 @@ -Complete your account registration +Use code {{ .RegistrationCode }} to complete your account registration diff --git a/courier/template/courier/builtin/templates/registration_code/valid/sms.body.gotmpl b/courier/template/courier/builtin/templates/registration_code/valid/sms.body.gotmpl new file mode 100644 index 000000000000..24b79fff0f8a --- /dev/null +++ b/courier/template/courier/builtin/templates/registration_code/valid/sms.body.gotmpl @@ -0,0 +1,3 @@ +Your registration code is: {{ .RegistrationCode }} + +It expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/verification/invalid/email.body.gotmpl b/courier/template/courier/builtin/templates/verification/invalid/email.body.gotmpl index bb3d3c1b3d90..18565a8de01d 100644 --- a/courier/template/courier/builtin/templates/verification/invalid/email.body.gotmpl +++ b/courier/template/courier/builtin/templates/verification/invalid/email.body.gotmpl @@ -1,6 +1,4 @@ -Hi, - -someone asked to verify this email address, but we were unable to find an account for this address. +Someone asked to verify this email address, but we were unable to find an account for this address. If this was you, check if you signed up using a different address. diff --git a/courier/template/courier/builtin/templates/verification/invalid/email.body.plaintext.gotmpl b/courier/template/courier/builtin/templates/verification/invalid/email.body.plaintext.gotmpl index bb3d3c1b3d90..18565a8de01d 100644 --- a/courier/template/courier/builtin/templates/verification/invalid/email.body.plaintext.gotmpl +++ b/courier/template/courier/builtin/templates/verification/invalid/email.body.plaintext.gotmpl @@ -1,6 +1,4 @@ -Hi, - -someone asked to verify this email address, but we were unable to find an account for this address. +Someone asked to verify this email address, but we were unable to find an account for this address. If this was you, check if you signed up using a different address. diff --git a/courier/template/courier/builtin/templates/verification/valid/email.body.gotmpl b/courier/template/courier/builtin/templates/verification/valid/email.body.gotmpl index d8e3168e5a78..d111cc47dd32 100644 --- a/courier/template/courier/builtin/templates/verification/valid/email.body.gotmpl +++ b/courier/template/courier/builtin/templates/verification/valid/email.body.gotmpl @@ -1,3 +1,5 @@ -Hi, please verify your account by clicking the following link: +Verify your account by opening the following link: {{ .VerificationURL }} + +If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/verification/valid/email.body.plaintext.gotmpl b/courier/template/courier/builtin/templates/verification/valid/email.body.plaintext.gotmpl index 4d915646c61e..ac28e51a4878 100644 --- a/courier/template/courier/builtin/templates/verification/valid/email.body.plaintext.gotmpl +++ b/courier/template/courier/builtin/templates/verification/valid/email.body.plaintext.gotmpl @@ -1,4 +1,5 @@ -Hi, please verify your account by clicking the following link: +Verify your account by opening the following link: {{ .VerificationURL }} +If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/verification_code/valid/email.body.gotmpl b/courier/template/courier/builtin/templates/verification_code/valid/email.body.gotmpl index f564f53378af..fc8d55ab5c0a 100644 --- a/courier/template/courier/builtin/templates/verification_code/valid/email.body.gotmpl +++ b/courier/template/courier/builtin/templates/verification_code/valid/email.body.gotmpl @@ -1,9 +1,9 @@ -Hi, - -please verify your account by entering the following code: +Verify your account with the following code: {{ .VerificationCode }} or clicking the following link: {{ .VerificationURL }} + +If this was not you, do nothing. This code / link expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/verification_code/valid/email.body.plaintext.gotmpl b/courier/template/courier/builtin/templates/verification_code/valid/email.body.plaintext.gotmpl index 58a5f39d4410..b6dde063b3c1 100644 --- a/courier/template/courier/builtin/templates/verification_code/valid/email.body.plaintext.gotmpl +++ b/courier/template/courier/builtin/templates/verification_code/valid/email.body.plaintext.gotmpl @@ -1,9 +1,9 @@ -Hi, - -please verify your account by entering the following code: +Verify your account with the following code: {{ .VerificationCode }} or clicking the following link: {{ .VerificationURL }} + +If this was not you, do nothing. This code / link expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/courier/builtin/templates/verification_code/valid/email.subject.gotmpl b/courier/template/courier/builtin/templates/verification_code/valid/email.subject.gotmpl index 3f0aceec1a72..b71c78404bde 100644 --- a/courier/template/courier/builtin/templates/verification_code/valid/email.subject.gotmpl +++ b/courier/template/courier/builtin/templates/verification_code/valid/email.subject.gotmpl @@ -1 +1 @@ -Please verify your email address +Use code {{ .VerificationCode }} to verify your account diff --git a/courier/template/courier/builtin/templates/verification_code/valid/sms.body.gotmpl b/courier/template/courier/builtin/templates/verification_code/valid/sms.body.gotmpl index 0469598d2d58..60474c5eb57b 100644 --- a/courier/template/courier/builtin/templates/verification_code/valid/sms.body.gotmpl +++ b/courier/template/courier/builtin/templates/verification_code/valid/sms.body.gotmpl @@ -1 +1,3 @@ Your verification code is: {{ .VerificationCode }} + +If this was not you, do nothing. It expires in {{ .ExpiresInMinutes }} minutes. diff --git a/courier/template/email/login_code_valid.go b/courier/template/email/login_code_valid.go index b09f70f8625e..1863ea39fbe6 100644 --- a/courier/template/email/login_code_valid.go +++ b/courier/template/email/login_code_valid.go @@ -23,6 +23,7 @@ type ( Identity map[string]interface{} `json:"identity"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/email/recovery_code_valid.go b/courier/template/email/recovery_code_valid.go index 4e8992da3d1d..313613ab714d 100644 --- a/courier/template/email/recovery_code_valid.go +++ b/courier/template/email/recovery_code_valid.go @@ -23,6 +23,7 @@ type ( Identity map[string]interface{} `json:"identity"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/email/recovery_valid.go b/courier/template/email/recovery_valid.go index f82a40b4f919..ae681b7c875b 100644 --- a/courier/template/email/recovery_valid.go +++ b/courier/template/email/recovery_valid.go @@ -23,6 +23,7 @@ type ( Identity map[string]interface{} `json:"identity"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/email/registration_code_valid.go b/courier/template/email/registration_code_valid.go index ec52362a8990..7994c1484afc 100644 --- a/courier/template/email/registration_code_valid.go +++ b/courier/template/email/registration_code_valid.go @@ -23,6 +23,7 @@ type ( RegistrationCode string `json:"registration_code"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/email/verification_code_valid.go b/courier/template/email/verification_code_valid.go index bd2045a03d25..d9b527901519 100644 --- a/courier/template/email/verification_code_valid.go +++ b/courier/template/email/verification_code_valid.go @@ -24,6 +24,7 @@ type ( Identity map[string]interface{} `json:"identity"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/email/verification_valid.go b/courier/template/email/verification_valid.go index eb9578261d83..c8d24c4372ac 100644 --- a/courier/template/email/verification_valid.go +++ b/courier/template/email/verification_valid.go @@ -23,6 +23,7 @@ type ( Identity map[string]interface{} `json:"identity"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/sms/login_code_valid.go b/courier/template/sms/login_code_valid.go index f365a6ba2800..f19e6e567771 100644 --- a/courier/template/sms/login_code_valid.go +++ b/courier/template/sms/login_code_valid.go @@ -22,6 +22,7 @@ type ( Identity map[string]interface{} `json:"identity"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/sms/login_code_valid_test.go b/courier/template/sms/login_code_valid_test.go index a2915c750c89..48e24475c8d7 100644 --- a/courier/template/sms/login_code_valid_test.go +++ b/courier/template/sms/login_code_valid_test.go @@ -25,7 +25,7 @@ func TestNewLoginCodeValid(t *testing.T) { tpl := sms.NewLoginCodeValid(reg, &sms.LoginCodeValidModel{To: expectedPhone, LoginCode: otp}) - expectedBody := fmt.Sprintf("Your login code is: %s\n", otp) + expectedBody := fmt.Sprintf("Your login code is: %s\n\nIt expires in 0 minutes.\n", otp) actualBody, err := tpl.SMSBody(context.Background()) require.NoError(t, err) diff --git a/courier/template/sms/registration_code_valid.go b/courier/template/sms/registration_code_valid.go new file mode 100644 index 000000000000..7413f75a94de --- /dev/null +++ b/courier/template/sms/registration_code_valid.go @@ -0,0 +1,55 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package sms + +import ( + "context" + "encoding/json" + "os" + + "github.com/ory/kratos/courier/template" +) + +type ( + RegistrationCodeValid struct { + deps template.Dependencies + model *RegistrationCodeValidModel + } + RegistrationCodeValidModel struct { + To string `json:"to"` + RegistrationCode string `json:"registration_code"` + Identity map[string]interface{} `json:"identity"` + RequestURL string `json:"request_url"` + TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` + } +) + +func NewRegistrationCodeValid(d template.Dependencies, m *RegistrationCodeValidModel) *RegistrationCodeValid { + return &RegistrationCodeValid{deps: d, model: m} +} + +func (t *RegistrationCodeValid) PhoneNumber() (string, error) { + return t.model.To, nil +} + +func (t *RegistrationCodeValid) SMSBody(ctx context.Context) (string, error) { + return template.LoadText( + ctx, + t.deps, + os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), + "registration_code/valid/sms.body.gotmpl", + "registration_code/valid/sms.body*", + t.model, + t.deps.CourierConfig().CourierSMSTemplatesRegistrationCodeValid(ctx).Body.PlainText, + ) +} + +func (t *RegistrationCodeValid) MarshalJSON() ([]byte, error) { + return json.Marshal(t.model) +} + +func (t *RegistrationCodeValid) TemplateType() template.TemplateType { + return template.TypeRegistrationCodeValid +} diff --git a/courier/template/sms/registration_code_valid_test.go b/courier/template/sms/registration_code_valid_test.go new file mode 100644 index 000000000000..1299c113f331 --- /dev/null +++ b/courier/template/sms/registration_code_valid_test.go @@ -0,0 +1,37 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package sms_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ory/kratos/courier/template/sms" + "github.com/ory/kratos/internal" +) + +func TestNewRegistrationCodeValid(t *testing.T) { + _, reg := internal.NewFastRegistryWithMocks(t) + + const ( + expectedPhone = "+12345678901" + otp = "012345" + ) + + tpl := sms.NewRegistrationCodeValid(reg, &sms.RegistrationCodeValidModel{To: expectedPhone, RegistrationCode: otp}) + + expectedBody := fmt.Sprintf("Your registration code is: %s\n\nIt expires in 0 minutes.\n", otp) + + actualBody, err := tpl.SMSBody(context.Background()) + require.NoError(t, err) + assert.Equal(t, expectedBody, actualBody) + + actualPhone, err := tpl.PhoneNumber() + require.NoError(t, err) + assert.Equal(t, expectedPhone, actualPhone) +} diff --git a/courier/template/sms/verification_code.go b/courier/template/sms/verification_code.go index 4204df0ac4c8..13c24be534cd 100644 --- a/courier/template/sms/verification_code.go +++ b/courier/template/sms/verification_code.go @@ -23,6 +23,7 @@ type ( Identity map[string]interface{} `json:"identity"` RequestURL string `json:"request_url"` TransientPayload map[string]interface{} `json:"transient_payload"` + ExpiresInMinutes int `json:"expires_in_minutes"` } ) diff --git a/courier/template/sms/verification_code_test.go b/courier/template/sms/verification_code_test.go index fc2bb892e6b2..4a31ae7f6ce3 100644 --- a/courier/template/sms/verification_code_test.go +++ b/courier/template/sms/verification_code_test.go @@ -25,7 +25,7 @@ func TestNewOTPMessage(t *testing.T) { tpl := sms.NewVerificationCodeValid(reg, &sms.VerificationCodeValidModel{To: expectedPhone, VerificationCode: otp}) - expectedBody := fmt.Sprintf("Your verification code is: %s\n", otp) + expectedBody := fmt.Sprintf("Your verification code is: %s\n\nIf this was not you, do nothing. It expires in 0 minutes.\n", otp) actualBody, err := tpl.SMSBody(context.Background()) require.NoError(t, err) diff --git a/driver/config/config.go b/driver/config/config.go index d1e28d166b98..dd7a6a42f00d 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -69,6 +69,7 @@ const ( ViperKeyCourierTemplatesVerificationCodeValidEmail = "courier.templates.verification_code.valid.email" ViperKeyCourierTemplatesVerificationCodeValidSMS = "courier.templates.verification_code.valid.sms" ViperKeyCourierTemplatesLoginCodeValidSMS = "courier.templates.login_code.valid.sms" + ViperKeyCourierTemplatesRegistrationCodeValidSMS = "courier.templates.registration_code.valid.sms" ViperKeyCourierDeliveryStrategy = "courier.delivery_strategy" ViperKeyCourierHTTPRequestConfig = "courier.http.request_config" ViperKeyCourierTemplatesLoginCodeValidEmail = "courier.templates.login_code.valid.email" @@ -322,6 +323,7 @@ type ( CourierTemplatesRegistrationCodeValid(ctx context.Context) *CourierEmailTemplate CourierSMSTemplatesVerificationCodeValid(ctx context.Context) *CourierSMSTemplate CourierSMSTemplatesLoginCodeValid(ctx context.Context) *CourierSMSTemplate + CourierSMSTemplatesRegistrationCodeValid(ctx context.Context) *CourierSMSTemplate CourierMessageRetries(ctx context.Context) int CourierWorkerPullCount(ctx context.Context) int CourierWorkerPullWait(ctx context.Context) time.Duration @@ -1164,6 +1166,10 @@ func (p *Config) CourierSMSTemplatesLoginCodeValid(ctx context.Context) *Courier return p.CourierSMSTemplatesHelper(ctx, ViperKeyCourierTemplatesLoginCodeValidSMS) } +func (p *Config) CourierSMSTemplatesRegistrationCodeValid(ctx context.Context) *CourierSMSTemplate { + return p.CourierSMSTemplatesHelper(ctx, ViperKeyCourierTemplatesRegistrationCodeValidSMS) +} + func (p *Config) CourierTemplatesLoginCodeValid(ctx context.Context) *CourierEmailTemplate { return p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesLoginCodeValidEmail) } diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 119ed3766fce..abe5a4e7ac5b 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -2043,6 +2043,9 @@ "properties": { "email": { "$ref": "#/definitions/emailCourierTemplate" + }, + "sms": { + "$ref": "#/definitions/smsCourierTemplate" } }, "required": ["email"] diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/testhelpers/courier.go b/internal/testhelpers/courier.go index fcb77f47005d..f4779adcd6d1 100644 --- a/internal/testhelpers/courier.go +++ b/internal/testhelpers/courier.go @@ -20,6 +20,7 @@ func CourierExpectMessage(ctx context.Context, t *testing.T, reg interface { courier.PersistenceProvider }, recipient, subject string, ) *courier.Message { + t.Helper() messages, total, _, err := reg.CourierPersister().ListMessages(ctx, courier.ListCourierMessagesParameters{ Recipient: recipient, }, []keysetpagination.Option{}) @@ -31,7 +32,7 @@ func CourierExpectMessage(ctx context.Context, t *testing.T, reg interface { }) for _, m := range messages { - if strings.EqualFold(m.Recipient, recipient) && (strings.EqualFold(m.Subject, subject) || strings.Contains(m.Body, subject)) { + if strings.EqualFold(m.Recipient, recipient) && (strings.Contains(m.Subject, subject) || strings.Contains(m.Body, subject)) { return &m } } diff --git a/persistence/sql/migratest/testdata/20200402142539_testdata.sql b/persistence/sql/migratest/testdata/20200402142539_testdata.sql index 36781f7a493a..6d67a5358c80 100644 --- a/persistence/sql/migratest/testdata/20200402142539_testdata.sql +++ b/persistence/sql/migratest/testdata/20200402142539_testdata.sql @@ -3,13 +3,13 @@ INSERT INTO identities (id, traits_schema_id, traits, created_at, updated_at) VA INSERT INTO continuity_containers (id, identity_id, name, payload, expires_at, created_at, updated_at) VALUES ('50ba09d3-481b-4060-844a-9541a9cec39c', '5ff66179-c240-4703-b0d8-494592cefff5', 'ory_kratos_settings_profile', '{"traits":{"email":"baz@ory.sh"},"request_id":""}', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); -INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('98f45a85-0782-49f1-a7b5-8f83d160b4a5', 1, 2, 'Hi, please verify your account by clicking the following link: +INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('98f45a85-0782-49f1-a7b5-8f83d160b4a5', 1, 2, 'Hi, Verify your account by opening the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/swmcFweNFSfvTSTKecmZjO6I8x0hxzZS', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); -INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('7a9d6df4-3b60-4cae-996e-d15d78b0fc36', 1, 2, 'Hi, please verify your account by clicking the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb', 'Please verify your email address', 'foobar@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); -INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('77fdc5e0-2260-49da-8aae-c36ba255d05b', 1, 2, 'Hi, please verify your account by clicking the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/SQcSX0Jx6IVEDKqAuaLZLNEw00J4vlig', 'Please verify your email address', 'foobar@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); -INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('a3ea4c30-0c6e-47b1-99ba-8fa69282e166', 1, 2, 'Hi, please verify your account by clicking the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); +INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('7a9d6df4-3b60-4cae-996e-d15d78b0fc36', 1, 2, 'Hi, Verify your account by opening the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb', 'Please verify your email address', 'foobar@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); +INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('77fdc5e0-2260-49da-8aae-c36ba255d05b', 1, 2, 'Hi, Verify your account by opening the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/SQcSX0Jx6IVEDKqAuaLZLNEw00J4vlig', 'Please verify your email address', 'foobar@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); +INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('a3ea4c30-0c6e-47b1-99ba-8fa69282e166', 1, 2, 'Hi, Verify your account by opening the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); INSERT INTO identity_credential_types (id, name) VALUES ('22bff9ae-f5aa-45d7-803b-97ec0b4e7b32', 'password'); INSERT INTO identity_credential_types (id, name) VALUES ('8071b37b-0d54-4c6f-8234-72cffb4ce784', 'totp'); diff --git a/persistence/sql/migratest/testdata/20210307130558_testdata.sql b/persistence/sql/migratest/testdata/20210307130558_testdata.sql index e92788541f35..b256c62cb945 100644 --- a/persistence/sql/migratest/testdata/20210307130558_testdata.sql +++ b/persistence/sql/migratest/testdata/20210307130558_testdata.sql @@ -1 +1 @@ -INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('34948489-31dc-454a-ab3b-b2dcc75a787f', 1, 2, 'Hi, please verify your account by clicking the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); +INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('34948489-31dc-454a-ab3b-b2dcc75a787f', 1, 2, 'Hi, Verify your account by opening the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19'); diff --git a/persistence/sql/migratest/testdata/20221205095201_testdata.sql b/persistence/sql/migratest/testdata/20221205095201_testdata.sql index de20fc952a73..fee47fdd5e8d 100644 --- a/persistence/sql/migratest/testdata/20221205095201_testdata.sql +++ b/persistence/sql/migratest/testdata/20221205095201_testdata.sql @@ -17,7 +17,7 @@ VALUES 'd9d4401c-08a1-434c-8ab5-4a7edefde351', 1, 2, - 'Hi, please verify your account by clicking the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb', + 'Hi, Verify your account by opening the following link: http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb', 'Please verify your email address', 'foobar@ory.sh', '2013-10-07 08:23:19', @@ -38,4 +38,4 @@ VALUES '884f556e-eb3a-4b9f-bee3-11345642c6c0', '2013-10-07 08:23:19', '2013-10-07 08:23:19' - ) \ No newline at end of file + ) diff --git a/selfservice/strategy/code/code_sender.go b/selfservice/strategy/code/code_sender.go index 02fda31f3d0a..20e392836ce7 100644 --- a/selfservice/strategy/code/code_sender.go +++ b/selfservice/strategy/code/code_sender.go @@ -101,21 +101,35 @@ func (s *Sender) SendCode(ctx context.Context, f flow.Flow, id *identity.Identit return err } - emailModel := email.RegistrationCodeValidModel{ - To: address.To, - RegistrationCode: rawCode, - Traits: model, - RequestURL: f.GetRequestURL(), - TransientPayload: transientPayload, - } - s.deps.Audit(). WithField("registration_flow_id", code.FlowID). WithField("registration_code_id", code.ID). WithSensitiveField("registration_code", rawCode). Info("Sending out registration email with code.") - if err := s.send(ctx, string(address.Via), email.NewRegistrationCodeValid(s.deps, &emailModel)); err != nil { + var t courier.Template + switch address.Via { + case identity.ChannelTypeEmail: + t = email.NewRegistrationCodeValid(s.deps, &email.RegistrationCodeValidModel{ + To: address.To, + RegistrationCode: rawCode, + Traits: model, + RequestURL: f.GetRequestURL(), + TransientPayload: transientPayload, + ExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()), + }) + case identity.ChannelTypeSMS: + t = sms.NewRegistrationCodeValid(s.deps, &sms.RegistrationCodeValidModel{ + To: address.To, + RegistrationCode: rawCode, + Identity: model, + RequestURL: f.GetRequestURL(), + TransientPayload: transientPayload, + ExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()), + }) + } + + if err := s.send(ctx, string(address.Via), t); err != nil { return errors.WithStack(err) } @@ -153,6 +167,7 @@ func (s *Sender) SendCode(ctx context.Context, f flow.Flow, id *identity.Identit Identity: model, RequestURL: f.GetRequestURL(), TransientPayload: transientPayload, + ExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()), }) case identity.ChannelTypeSMS: t = sms.NewLoginCodeValid(s.deps, &sms.LoginCodeValidModel{ @@ -161,6 +176,7 @@ func (s *Sender) SendCode(ctx context.Context, f flow.Flow, id *identity.Identit Identity: model, RequestURL: f.GetRequestURL(), TransientPayload: transientPayload, + ExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()), }) } @@ -266,6 +282,7 @@ func (s *Sender) SendRecoveryCodeTo(ctx context.Context, i *identity.Identity, c Identity: model, RequestURL: f.GetRequestURL(), TransientPayload: transientPayload, + ExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()), } return s.send(ctx, string(code.RecoveryAddress.Via), email.NewRecoveryCodeValid(s.deps, &emailModel)) @@ -371,6 +388,7 @@ func (s *Sender) SendVerificationCodeTo(ctx context.Context, f *verification.Flo VerificationCode: codeString, RequestURL: f.GetRequestURL(), TransientPayload: transientPayload, + ExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()), }) case identity.ChannelTypeSMS: t = sms.NewVerificationCodeValid(s.deps, &sms.VerificationCodeValidModel{ @@ -379,6 +397,7 @@ func (s *Sender) SendVerificationCodeTo(ctx context.Context, f *verification.Flo Identity: model, RequestURL: f.GetRequestURL(), TransientPayload: transientPayload, + ExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()), }) default: return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Expected email or sms but got %s", code.VerifiableAddress.Via)) diff --git a/selfservice/strategy/code/code_sender_test.go b/selfservice/strategy/code/code_sender_test.go index e5ba75826eb5..4e306a78cf02 100644 --- a/selfservice/strategy/code/code_sender_test.go +++ b/selfservice/strategy/code/code_sender_test.go @@ -66,7 +66,7 @@ func TestSender(t *testing.T) { require.Len(t, messages, 2) assert.EqualValues(t, "tracked@ory.sh", messages[0].Recipient) - assert.Contains(t, messages[0].Subject, "Recover access to your account") + assert.Contains(t, messages[0].Subject, "Use code") assert.Regexp(t, testhelpers.CodeRegex, messages[0].Body) @@ -122,7 +122,7 @@ func TestSender(t *testing.T) { require.Len(t, messages, 2) assert.EqualValues(t, "tracked@ory.sh", messages[0].Recipient) - assert.Contains(t, messages[0].Subject, "Please verify your email address") + assert.Contains(t, messages[0].Subject, "Use code") assert.Regexp(t, testhelpers.CodeRegex, messages[0].Body) diff --git a/selfservice/strategy/code/strategy.go b/selfservice/strategy/code/strategy.go index 1e8fb5cb6af4..aaac228508fd 100644 --- a/selfservice/strategy/code/strategy.go +++ b/selfservice/strategy/code/strategy.go @@ -352,7 +352,7 @@ func (s *Strategy) populateEmailSentFlow(ctx context.Context, f flow.Flow) error case flow.LoginFlow: route = login.RouteSubmitFlow codeMetaLabel = text.NewInfoNodeLabelLoginCode() - message = text.NewLoginEmailWithCodeSent() + message = text.NewLoginCodeSent() // preserve the login identifier that was submitted // so we can retry the code flow with the same data diff --git a/selfservice/strategy/code/strategy_login_test.go b/selfservice/strategy/code/strategy_login_test.go index d93972162852..95952f45d7de 100644 --- a/selfservice/strategy/code/strategy_login_test.go +++ b/selfservice/strategy/code/strategy_login_test.go @@ -236,8 +236,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", stringsx.ToUpperInitial(s.identityEmail)) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -257,8 +257,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -284,8 +284,8 @@ func TestLoginCodeStrategy(t *testing.T) { t.Logf("s.body: %s", s.body) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -450,8 +450,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -478,8 +478,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -505,8 +505,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -564,8 +564,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -616,8 +616,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -652,8 +652,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -663,8 +663,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message = testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message = testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode2 := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode2) @@ -713,8 +713,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, loginEmail, "Login to your account") - require.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, loginEmail, "Use code") + require.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) require.NotEmpty(t, loginCode) @@ -795,8 +795,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("identifier", s.identityEmail) }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -837,8 +837,8 @@ func TestLoginCodeStrategy(t *testing.T) { v.Set("method", "code") }, false, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") loginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, loginCode) @@ -988,8 +988,8 @@ func TestLoginCodeStrategy(t *testing.T) { var message *courier.Message if !strings.HasPrefix(identifier, "+") { // email - message = testhelpers.CourierExpectMessage(ctx, t, reg, x.GracefulNormalization(identifier), "Login to your account") - assert.Contains(t, message.Body, "please login to your account by entering the following code") + message = testhelpers.CourierExpectMessage(ctx, t, reg, x.GracefulNormalization(identifier), "Use code") + assert.Contains(t, message.Body, "Login to your account with the following code") } else { // SMS message = testhelpers.CourierExpectMessage(ctx, t, reg, x.GracefulNormalization(identifier), "Your login code is:") diff --git a/selfservice/strategy/code/strategy_recovery_test.go b/selfservice/strategy/code/strategy_recovery_test.go index 483e8dfc6f89..9b55016daebb 100644 --- a/selfservice/strategy/code/strategy_recovery_test.go +++ b/selfservice/strategy/code/strategy_recovery_test.go @@ -242,8 +242,8 @@ func TestRecovery(t *testing.T) { assert.Len(t, gjson.Get(recoverySubmissionResponse, "ui.messages").Array(), 1, "%s", recoverySubmissionResponse) assertx.EqualAsJSON(t, text.NewRecoveryEmailWithCodeSent(), json.RawMessage(gjson.Get(recoverySubmissionResponse, "ui.messages.0").Raw)) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") - assert.Contains(t, message.Body, "please recover access to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") + assert.Contains(t, message.Body, "Recover access to your account by entering") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, recoveryCode) @@ -316,7 +316,7 @@ func TestRecovery(t *testing.T) { formPayload.Set("transient_payload", templatePayload) body, _ := testhelpers.RecoveryMakeRequest(t, false, f, client, formPayload.Encode()) - message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Use code") assert.Equal(t, templatePayload, gjson.GetBytes(message.TemplateData, "transient_payload").String(), "should pass transient payload to email template") @@ -607,7 +607,7 @@ func TestRecovery(t *testing.T) { addr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.VerifiableAddressTypeEmail, email) assert.NoError(t, err) - emailText := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Recover access to your account") + emailText := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, emailText, 1) // Deactivate the identity @@ -646,7 +646,7 @@ func TestRecovery(t *testing.T) { actual := expectSuccessfulRecovery(t, cl, RecoveryClientTypeBrowser, func(v url.Values) { v.Set("email", email) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) cl.CheckRedirect = func(req *http.Request, via []*http.Request) error { @@ -707,7 +707,7 @@ func TestRecovery(t *testing.T) { v.Set("email", recoveryEmail) }, http.StatusOK) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) form := withCSRFToken(t, testCase.ClientType, actual, url.Values{ @@ -818,8 +818,8 @@ func TestRecovery(t *testing.T) { initialFlowId := gjson.Get(body, "id") - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") - assert.Contains(t, message.Body, "please recover access to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") + assert.Contains(t, message.Body, "Recover access to your account by entering") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) @@ -873,7 +873,7 @@ func TestRecovery(t *testing.T) { assert.True(t, gjson.Get(body, "ui.nodes.#(attributes.name==code)").Exists()) assert.Equal(t, recoveryEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, recoveryCode, http.StatusOK) @@ -892,14 +892,14 @@ func TestRecovery(t *testing.T) { require.NotEmpty(t, action) assert.Equal(t, recoveryEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode1 := testhelpers.CourierExpectCodeInMessage(t, message1, 1) body = resendRecoveryCode(t, c, body, RecoveryClientTypeBrowser, http.StatusOK) assert.True(t, gjson.Get(body, "ui.nodes.#(attributes.name==code)").Exists()) assert.Equal(t, recoveryEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message2 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message2 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode2 := testhelpers.CourierExpectCodeInMessage(t, message2, 1) body = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, recoveryCode1, http.StatusOK) @@ -943,7 +943,7 @@ func TestRecovery(t *testing.T) { v.Set("email", recoveryEmail) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) action := gjson.Get(body, "ui.action").String() @@ -975,7 +975,7 @@ func TestRecovery(t *testing.T) { v.Set("email", recoveryEmail) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) action := gjson.Get(body, "ui.action").String() @@ -1117,8 +1117,8 @@ func TestRecovery_WithContinueWith(t *testing.T) { assert.Len(t, gjson.Get(recoverySubmissionResponse, "ui.messages").Array(), 1, "%s", recoverySubmissionResponse) assertx.EqualAsJSON(t, text.NewRecoveryEmailWithCodeSent(), json.RawMessage(gjson.Get(recoverySubmissionResponse, "ui.messages.0").Raw)) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") - assert.Contains(t, message.Body, "please recover access to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") + assert.Contains(t, message.Body, "Recover access to your account by entering") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, recoveryCode) @@ -1435,7 +1435,7 @@ func TestRecovery_WithContinueWith(t *testing.T) { addr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.VerifiableAddressTypeEmail, email) assert.NoError(t, err) - emailText := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Recover access to your account") + emailText := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, emailText, 1) // Deactivate the identity @@ -1478,7 +1478,7 @@ func TestRecovery_WithContinueWith(t *testing.T) { actual := submitRecoveryForm(t, cl, testCase.ClientType, func(v url.Values) { v.Set("email", email) }, http.StatusOK) - message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, email, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) submitCodeAndExpectRedirectToSettings(t, cl, testCase.ClientType, recoveryCode, actual) @@ -1555,7 +1555,7 @@ func TestRecovery_WithContinueWith(t *testing.T) { v.Set("email", recoveryEmail) }, http.StatusOK) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) form := withCSRFToken(t, testCase.ClientType, actual, url.Values{ @@ -1631,7 +1631,7 @@ func TestRecovery_WithContinueWith(t *testing.T) { v.Set("email", recoveryEmail) }, http.StatusOK) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) action := gjson.Get(actual, "ui.action").String() @@ -1744,8 +1744,8 @@ func TestRecovery_WithContinueWith(t *testing.T) { initialFlowId := gjson.Get(body, "id") - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") - assert.Contains(t, message.Body, "please recover access to your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") + assert.Contains(t, message.Body, "Recover access to your account by entering") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) @@ -1812,7 +1812,7 @@ func TestRecovery_WithContinueWith(t *testing.T) { assert.True(t, gjson.Get(body, "ui.nodes.#(attributes.name==code)").Exists()) assert.Equal(t, recoveryEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) submitCodeAndExpectRedirectToSettings(t, c, testCase.ClientType, recoveryCode, body) @@ -1835,14 +1835,14 @@ func TestRecovery_WithContinueWith(t *testing.T) { require.NotEmpty(t, action) assert.Equal(t, recoveryEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode1 := testhelpers.CourierExpectCodeInMessage(t, message1, 1) body = resendRecoveryCode(t, c, body, testCase.ClientType, http.StatusOK) assert.True(t, gjson.Get(body, "ui.nodes.#(attributes.name==code)").Exists()) assert.Equal(t, recoveryEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message2 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message2 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode2 := testhelpers.CourierExpectCodeInMessage(t, message2, 1) body = submitRecoveryCode(t, c, body, testCase.ClientType, recoveryCode1, http.StatusOK) @@ -1892,7 +1892,7 @@ func TestRecovery_WithContinueWith(t *testing.T) { v.Set("email", recoveryEmail) }, http.StatusOK) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) action := gjson.Get(body, "ui.action").String() @@ -1920,7 +1920,7 @@ func TestRecovery_WithContinueWith(t *testing.T) { v.Set("email", recoveryEmail) }, http.StatusOK) - message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") + message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Use code") recoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) action := gjson.Get(body, "ui.action").String() diff --git a/selfservice/strategy/code/strategy_registration_test.go b/selfservice/strategy/code/strategy_registration_test.go index c5988047d0a3..b5f0d6e02c9f 100644 --- a/selfservice/strategy/code/strategy_registration_test.go +++ b/selfservice/strategy/code/strategy_registration_test.go @@ -274,8 +274,8 @@ func TestRegistrationCodeStrategy(t *testing.T) { // 2. Submit Identifier (email) state = registerNewUser(ctx, t, state, tc.apiType, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, state.email, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, state.email, "Use code") + assert.Contains(t, message.Body, "Complete your account registration with the following code") registrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode) @@ -307,8 +307,8 @@ func TestRegistrationCodeStrategy(t *testing.T) { // 2. Submit Identifier (email) state = registerNewUser(ctx, t, state, tc.apiType, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, sourceMail, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, sourceMail, "Use code") + assert.Contains(t, message.Body, "Complete your account registration with the following code") registrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode) @@ -351,8 +351,8 @@ func TestRegistrationCodeStrategy(t *testing.T) { require.Equal(t, "code", val, body) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Use code") + assert.Contains(t, message.Body, "Complete your account registration with the following code") registrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode) @@ -372,12 +372,12 @@ func TestRegistrationCodeStrategy(t *testing.T) { } else { require.NotEmptyf(t, csrfToken, "expected to find the csrf_token but got %s", body) } - require.Containsf(t, gjson.Get(body, "ui.messages").String(), "An email containing a code has been sent to the email address you provided.", "%s", body) + require.Containsf(t, gjson.Get(body, "ui.messages").String(), "A code has been sent to the address(es) you provided", "%s", body) }) // get the new code from email - message = testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message = testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Use code") + assert.Contains(t, message.Body, "Complete your account registration with the following code") registrationCode2 := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode2) @@ -410,8 +410,8 @@ func TestRegistrationCodeStrategy(t *testing.T) { // 2. Submit Identifier (email) s = registerNewUser(ctx, t, s, tc.apiType, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Use code") + assert.Contains(t, message.Body, "Complete your account registration with the following code") registrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode) @@ -439,8 +439,8 @@ func TestRegistrationCodeStrategy(t *testing.T) { // 2. Submit Identifier (email) s = registerNewUser(ctx, t, s, tc.apiType, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Use code") + assert.Contains(t, message.Body, "Complete your account registration") registrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode) @@ -573,8 +573,8 @@ func TestRegistrationCodeStrategy(t *testing.T) { // 2. Submit Identifier (email) state = registerNewUser(ctx, t, state, tc.apiType, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, state.email, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, state.email, "Use code") + assert.Contains(t, message.Body, "Complete your account registration with the following code") registrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode) @@ -597,8 +597,8 @@ func TestRegistrationCodeStrategy(t *testing.T) { // 2. Submit Identifier (email) s = registerNewUser(ctx, t, s, tc.apiType, nil) - message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Complete your account registration") - assert.Contains(t, message.Body, "please complete your account registration by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, "Use code") + assert.Contains(t, message.Body, "Complete your account registration with the following code") registrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) assert.NotEmpty(t, registrationCode) diff --git a/selfservice/strategy/code/strategy_verification_test.go b/selfservice/strategy/code/strategy_verification_test.go index 322dc56eabd2..5bd8417d180d 100644 --- a/selfservice/strategy/code/strategy_verification_test.go +++ b/selfservice/strategy/code/strategy_verification_test.go @@ -285,8 +285,8 @@ func TestVerification(t *testing.T) { v.Set("email", verificationEmail) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") - assert.Contains(t, message.Body, "please verify your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") + assert.Contains(t, message.Body, "Verify your account with the following code") code := testhelpers.CourierExpectCodeInMessage(t, message, 1) @@ -310,8 +310,8 @@ func TestVerification(t *testing.T) { assert.EqualValues(t, verificationEmail, gjson.Get(actual, "ui.nodes.#(attributes.name==email).attributes.value").String(), "%s", actual) assertx.EqualAsJSON(t, text.NewVerificationEmailWithCodeSent(), json.RawMessage(gjson.Get(actual, "ui.messages.0").Raw)) - message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") - assert.Contains(t, message.Body, "please verify your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") + assert.Contains(t, message.Body, "Verify your account with the following code") verificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1) @@ -374,7 +374,7 @@ func TestVerification(t *testing.T) { } expectSuccess(t, nil, false, false, values) - message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") + message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") verificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1) code := testhelpers.CourierExpectCodeInMessage(t, message, 1) @@ -442,8 +442,8 @@ func TestVerification(t *testing.T) { v.Set("email", verificationEmail) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") - assert.Contains(t, message.Body, "please verify your account by entering the following code") + message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") + assert.Contains(t, message.Body, "Verify your account with the following code") verificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1) @@ -507,7 +507,7 @@ func TestVerification(t *testing.T) { v.Set("email", verificationEmail) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") + message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") _ = testhelpers.CourierExpectCodeInMessage(t, message, 1) c := testhelpers.NewClientWithCookies(t) @@ -516,7 +516,7 @@ func TestVerification(t *testing.T) { assert.True(t, gjson.Get(body, "ui.nodes.#(attributes.name==code)").Exists()) assert.Equal(t, verificationEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") + message = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") verificationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) submitVerificationCode(t, body, c, verificationCode) @@ -527,7 +527,7 @@ func TestVerification(t *testing.T) { v.Set("email", verificationEmail) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") + message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") firstCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) c := testhelpers.NewClientWithCookies(t) @@ -536,7 +536,7 @@ func TestVerification(t *testing.T) { assert.True(t, gjson.Get(body, "ui.nodes.#(attributes.name==code)").Exists()) assert.Equal(t, verificationEmail, gjson.Get(body, "ui.nodes.#(attributes.name==email).attributes.value").String()) - message = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") + message = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") secondCode := testhelpers.CourierExpectCodeInMessage(t, message, 1) body, res := submitVerificationCode(t, body, c, firstCode) @@ -587,7 +587,7 @@ func TestVerification(t *testing.T) { body := expectSuccess(t, nil, true, false, func(v url.Values) { v.Set("email", verificationEmail) }) - message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") + message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") code := testhelpers.CourierExpectCodeInMessage(t, message, 1) body, res := submitVerificationCode(t, body, c, code) @@ -597,7 +597,7 @@ func TestVerification(t *testing.T) { body = expectSuccess(t, nil, true, false, func(v url.Values) { v.Set("email", verificationEmail) }) - message = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") + message = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Use code") code = testhelpers.CourierExpectCodeInMessage(t, message, 1) body, res = submitVerificationCode(t, body, c, code) diff --git a/selfservice/strategy/link/sender.go b/selfservice/strategy/link/sender.go index 41231a721b8b..c289b657e0a1 100644 --- a/selfservice/strategy/link/sender.go +++ b/selfservice/strategy/link/sender.go @@ -202,6 +202,7 @@ func (s *Sender) SendRecoveryTokenTo(ctx context.Context, f *recovery.Flow, i *i Identity: model, RequestURL: f.GetRequestURL(), TransientPayload: transientPayload, + ExpiresInMinutes: int(s.r.Config().SelfServiceLinkMethodLifespan(ctx).Minutes()), })) } @@ -231,13 +232,14 @@ func (s *Sender) SendVerificationTokenTo(ctx context.Context, f *verification.Fl "token": {token.Token}, }).String() - if err := s.send(ctx, string(address.Via), email.NewVerificationValid(s.r, + if err := s.send(ctx, address.Via, email.NewVerificationValid(s.r, &email.VerificationValidModel{ To: address.Value, VerificationURL: verificationUrl, Identity: model, RequestURL: f.GetRequestURL(), TransientPayload: transientPayload, + ExpiresInMinutes: int(s.r.Config().SelfServiceLinkMethodLifespan(ctx).Minutes()), })); err != nil { return err } diff --git a/selfservice/strategy/link/strategy_recovery_test.go b/selfservice/strategy/link/strategy_recovery_test.go index 5cc6c510d73e..531cb4e77502 100644 --- a/selfservice/strategy/link/strategy_recovery_test.go +++ b/selfservice/strategy/link/strategy_recovery_test.go @@ -506,7 +506,7 @@ func TestRecovery(t *testing.T) { assertx.EqualAsJSON(t, text.NewRecoveryEmailSent(), json.RawMessage(gjson.Get(recoverySubmissionResponse, "ui.messages.0").Raw)) message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") - assert.Contains(t, message.Body, "please recover access to your account by clicking the following link") + assert.Contains(t, message.Body, "Recover access to your account by clicking the following link") recoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1) @@ -788,7 +788,7 @@ func TestRecovery(t *testing.T) { }) message := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, "Recover access to your account") - assert.Contains(t, message.Body, "please recover access to your account by clicking the following link") + assert.Contains(t, message.Body, "Recover access to your account by clicking the following link") recoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1) diff --git a/selfservice/strategy/link/strategy_verification_test.go b/selfservice/strategy/link/strategy_verification_test.go index b2fd7c0f405a..ae0e20021338 100644 --- a/selfservice/strategy/link/strategy_verification_test.go +++ b/selfservice/strategy/link/strategy_verification_test.go @@ -248,7 +248,7 @@ func TestVerification(t *testing.T) { }) message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") - assert.Contains(t, message.Body, "Hi, please verify your account by clicking the following link") + assert.Contains(t, message.Body, "Verify your account by opening the following link") verificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1) @@ -283,7 +283,7 @@ func TestVerification(t *testing.T) { assertx.EqualAsJSON(t, text.NewVerificationEmailSent(), json.RawMessage(gjson.Get(actual, "ui.messages.0").Raw)) message := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, "Please verify your email address") - assert.Contains(t, message.Body, "please verify your account by clicking the following link") + assert.Contains(t, message.Body, "Verify your account by opening the following link") verificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1) diff --git a/selfservice/strategy/profile/strategy_test.go b/selfservice/strategy/profile/strategy_test.go index d34c3f9e94f6..be9b448a0215 100644 --- a/selfservice/strategy/profile/strategy_test.go +++ b/selfservice/strategy/profile/strategy_test.go @@ -544,7 +544,7 @@ func TestStrategyTraits(t *testing.T) { m, err := reg.CourierPersister().LatestQueuedMessage(context.Background()) require.NoError(t, err) - assert.Contains(t, m.Subject, "verify your email address") + assert.Contains(t, m.Subject, "Use code") } payload := func(newEmail string) func(v url.Values) { diff --git a/test/e2e/cypress/helpers/index.ts b/test/e2e/cypress/helpers/index.ts index 52bcb339ad50..ef61a5546bba 100644 --- a/test/e2e/cypress/helpers/index.ts +++ b/test/e2e/cypress/helpers/index.ts @@ -95,7 +95,7 @@ export const appPrefix = (app) => `[data-testid="app-${app}"] ` export const codeRegex = /(\d{6})/ -export function extractRecoveryCode(body: string): string | null { +export function extractOTPCode(body: string): string | null { const result = codeRegex.exec(body) if (result != null && result.length > 0) { return result[0] diff --git a/test/e2e/cypress/integration/profiles/code/login/error.spec.ts b/test/e2e/cypress/integration/profiles/code/login/error.spec.ts index 244d40ab8132..cf929a1df6fb 100644 --- a/test/e2e/cypress/integration/profiles/code/login/error.spec.ts +++ b/test/e2e/cypress/integration/profiles/code/login/error.spec.ts @@ -78,10 +78,7 @@ context("Login error messages with code method", () => { cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1010014"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1010014"]').should("exist") cy.get(Selectors[app]["code"]).type("123456") cy.submitCodeForm(app) diff --git a/test/e2e/cypress/integration/profiles/code/registration/error.spec.ts b/test/e2e/cypress/integration/profiles/code/registration/error.spec.ts index 4570c1c5cc60..a1e10a196c02 100644 --- a/test/e2e/cypress/integration/profiles/code/registration/error.spec.ts +++ b/test/e2e/cypress/integration/profiles/code/registration/error.spec.ts @@ -69,10 +69,7 @@ context("Registration error messages with code method", () => { cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.get(Selectors[app]["code"]).type("123456") cy.submitCodeForm(app) @@ -90,10 +87,7 @@ context("Registration error messages with code method", () => { cy.get(Selectors[app]["tos"]).click() cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") if (app !== "express") { // the mobile app doesn't render hidden fields in the DOM @@ -131,10 +125,7 @@ context("Registration error messages with code method", () => { cy.get(Selectors[app]["tos"]).click() cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.removeAttribute([Selectors[app]["code"]], "required") @@ -200,10 +191,7 @@ context("Registration error messages with code method", () => { cy.get(Selectors[app]["tos"]).click() cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.getRegistrationCodeFromEmail(email).then((code) => { cy.get(Selectors[app]["code"]).type(code) diff --git a/test/e2e/cypress/integration/profiles/code/registration/success.spec.ts b/test/e2e/cypress/integration/profiles/code/registration/success.spec.ts index c715d80cd86e..91c48b760f1c 100644 --- a/test/e2e/cypress/integration/profiles/code/registration/success.spec.ts +++ b/test/e2e/cypress/integration/profiles/code/registration/success.spec.ts @@ -98,10 +98,7 @@ context("Registration success with code method", () => { cy.get(Selectors[app]["tos"]).click() cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.getRegistrationCodeFromEmail(email).then((code) => cy.wrap(code).as("code1"), @@ -166,10 +163,7 @@ context("Registration success with code method", () => { cy.get(Selectors[app]["tos"]).click() cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.getRegistrationCodeFromEmail(email).should((code) => { cy.get(Selectors[app]["code"]).type(code) @@ -216,10 +210,7 @@ context("Registration success with code method", () => { cy.get(Selectors[app]["tos"]).click() cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.getRegistrationCodeFromEmail(email).should((code) => { cy.get(Selectors[app]["code"]).type(code) @@ -329,10 +320,7 @@ context("Registration success with code method", () => { cy.get(Selectors[app]["email2"]).type(email2) cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") // intentionally use email 1 to sign up for the account cy.getRegistrationCodeFromEmail(email, { expectedCount: 1 }).should( diff --git a/test/e2e/cypress/integration/profiles/oidc-provider/login.spec.ts b/test/e2e/cypress/integration/profiles/oidc-provider/login.spec.ts index 861cfd0d2a79..d7f73f44872c 100644 --- a/test/e2e/cypress/integration/profiles/oidc-provider/login.spec.ts +++ b/test/e2e/cypress/integration/profiles/oidc-provider/login.spec.ts @@ -218,14 +218,20 @@ context("OpenID Provider - change between flows", () => { }) it("switch to recovery flow with password reset", () => { + cy.updateConfigFile((config) => { + config.selfservice.flows.recovery.lifespan = "1m" + config.selfservice.methods.link.config.lifespan = "1m" + config.selfservice.flows.verification.enabled = false + if (!config.selfservice.flows.recovery) { + config.selfservice.flows.recovery = {} + } + config.selfservice.flows.recovery.enabled = true + config.selfservice.flows.settings.privileged_session_max_age = "5m" + return config + }) cy.deleteMail() - cy.longRecoveryLifespan() - cy.longLinkLifespan() - cy.disableVerification() - cy.enableRecovery() cy.useRecoveryStrategy("code") cy.notifyUnknownRecipients("recovery", false) - cy.longPrivilegedSessionTime() const identity = gen.identityWithWebsite() cy.registerApi(identity) diff --git a/test/e2e/cypress/integration/profiles/recovery/code/errors.spec.ts b/test/e2e/cypress/integration/profiles/recovery/code/errors.spec.ts index 23d877049e6c..8da454cfdfe7 100644 --- a/test/e2e/cypress/integration/profiles/recovery/code/errors.spec.ts +++ b/test/e2e/cypress/integration/profiles/recovery/code/errors.spec.ts @@ -1,7 +1,7 @@ // Copyright © 2023 Ory Corp // SPDX-License-Identifier: Apache-2.0 -import { appPrefix, email, extractRecoveryCode, gen } from "../../../../helpers" +import { appPrefix, email, extractOTPCode, gen } from "../../../../helpers" import { routes as express } from "../../../../helpers/express" import { routes as react } from "../../../../helpers/react" @@ -123,7 +123,7 @@ context("Account Recovery Errors", () => { expect(message.toAddresses).to.have.length(1) expect(message.toAddresses[0].trim()).to.equal(email) - const code = extractRecoveryCode(message.body) + const code = extractOTPCode(message.body) expect(code).to.be.null }) }) diff --git a/test/e2e/cypress/integration/profiles/two-steps/registration/code.spec.ts b/test/e2e/cypress/integration/profiles/two-steps/registration/code.spec.ts index bffafb36ee03..41cc98c03eb8 100644 --- a/test/e2e/cypress/integration/profiles/two-steps/registration/code.spec.ts +++ b/test/e2e/cypress/integration/profiles/two-steps/registration/code.spec.ts @@ -104,10 +104,7 @@ context("Registration success with code method", () => { cy.submitProfileForm(app) cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.getRegistrationCodeFromEmail(email).then((code) => cy.wrap(code).as("code1"), @@ -187,10 +184,7 @@ context("Registration success with code method", () => { cy.submitProfileForm(app) cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.getRegistrationCodeFromEmail(email).should((code) => { cy.get(Selectors[app]["code"]).type(code) @@ -234,10 +228,7 @@ context("Registration success with code method", () => { cy.submitProfileForm(app) cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") cy.getRegistrationCodeFromEmail(email).should((code) => { cy.get(Selectors[app]["code"]).type(code) @@ -305,10 +296,7 @@ context("Registration success with code method", () => { cy.submitProfileForm(app) cy.submitCodeForm(app) - cy.get('[data-testid="ui/message/1040005"]').should( - "contain", - "An email containing a code has been sent to the email address you provided", - ) + cy.get('[data-testid="ui/message/1040005"]').should("be.visible") // intentionally use email 1 to sign up for the account cy.getRegistrationCodeFromEmail(email, { expectedCount: 1 }).should( diff --git a/test/e2e/cypress/integration/profiles/verification/registration/errors.spec.ts b/test/e2e/cypress/integration/profiles/verification/registration/errors.spec.ts index 06a68f4af237..f8436c7eae1a 100644 --- a/test/e2e/cypress/integration/profiles/verification/registration/errors.spec.ts +++ b/test/e2e/cypress/integration/profiles/verification/registration/errors.spec.ts @@ -66,7 +66,7 @@ context("Account Verification Registration Errors", () => { it("is unable to verify the email address if the code is incorrect", () => { cy.getMail({ - subject: "Please verify your email address", + body: "Verify your account", email: identity.email, }).then((mail) => { const link = parseHtml(mail.body).querySelector("a") diff --git a/test/e2e/cypress/integration/profiles/verification/settings/error.spec.ts b/test/e2e/cypress/integration/profiles/verification/settings/error.spec.ts index 287f63a931e0..91ae2c5e8259 100644 --- a/test/e2e/cypress/integration/profiles/verification/settings/error.spec.ts +++ b/test/e2e/cypress/integration/profiles/verification/settings/error.spec.ts @@ -72,7 +72,7 @@ context("Account Verification Settings Error", () => { cy.get('button[value="profile"]').click() cy.getMail({ - subject: "Please verify your email address", + body: "Verify your account", email, }).then((mail) => { const link = parseHtml(mail.body).querySelector("a") diff --git a/test/e2e/cypress/integration/profiles/verification/verify/errors.spec.ts b/test/e2e/cypress/integration/profiles/verification/verify/errors.spec.ts index 97fd908a2e22..afb8d9730413 100644 --- a/test/e2e/cypress/integration/profiles/verification/verify/errors.spec.ts +++ b/test/e2e/cypress/integration/profiles/verification/verify/errors.spec.ts @@ -70,12 +70,9 @@ context("Account Verification Error", () => { cy.getMail({ removeMail: true, - subject: "Please verify your email address", + body: "Verify your account", email: identity.email, }).then((message) => { - expect(message.subject).to.equal( - "Please verify your email address", - ) expect(message.toAddresses[0].trim()).to.equal(identity.email) const link = parseHtml(message.body).querySelector("a") @@ -136,7 +133,7 @@ context("Account Verification Error", () => { cy.getMail({ email: identity.email, - subject: "Please verify your email address", + body: "Verify your account", }).then((mail) => { const link = parseHtml(mail.body).querySelector("a") diff --git a/test/e2e/cypress/support/commands.ts b/test/e2e/cypress/support/commands.ts index 89b5c7cb15c5..2d933360c26e 100644 --- a/test/e2e/cypress/support/commands.ts +++ b/test/e2e/cypress/support/commands.ts @@ -4,7 +4,7 @@ import { APP_URL, assertVerifiableAddress, - extractRecoveryCode, + extractOTPCode, gen, KRATOS_ADMIN, KRATOS_PUBLIC, @@ -1112,9 +1112,8 @@ Cypress.Commands.add( ({ expect: { email, redirectTo }, strategy = "code" }) => { cy.getMail({ email, - subject: "Please verify your email address", + body: "Verify your account", }).then((message) => { - expect(message.subject).to.equal("Please verify your email address") expect(message.fromAddress.trim()).to.equal("no-reply@ory.kratos.sh") expect(message.toAddresses).to.have.length(1) expect(message.toAddresses[0].trim()).to.equal(email) @@ -1176,9 +1175,9 @@ Cypress.Commands.add( cy.getMail({ removeMail: true, email, - subject: "Recover access to your account", + body: "Recover access to your account", }).should((message) => { - const code = extractRecoveryCode(message.body) + const code = extractOTPCode(message.body) expect(code).to.not.be.undefined expect(code.length).to.equal(6) cy.wrap(code).as("recoveryCode") @@ -1221,7 +1220,7 @@ Cypress.Commands.add( cy.getMail({ removeMail: true, email, - subject: "Please verify your email address", + body: "Verify your account", }).should((message) => { expect(message.fromAddress.trim()).to.equal("no-reply@ory.kratos.sh") expect(message.toAddresses).to.have.length(1) @@ -1286,6 +1285,7 @@ Cypress.Commands.add( expectedCount = 1, email = undefined, subject = undefined, + body = undefined, }) => { let tries = 0 const req = () => @@ -1313,6 +1313,9 @@ Cypress.Commands.add( if (subject) { filters.push((m: MailMessage) => m.subject.includes(subject)) } + if (body) { + filters.push((m: MailMessage) => m.body.includes(body)) + } const filtered = response.body.mailItems.filter((m) => { return filters.every((f) => f(m)) }) @@ -1525,13 +1528,13 @@ Cypress.Commands.add("getVerificationCodeFromEmail", (email) => { .getMail({ removeMail: true, email, - subject: "Please verify your email address", + body: "Verify your account", }) .should((message) => { expect(message.toAddresses[0].trim()).to.equal(email) }) .then((message) => { - const code = extractRecoveryCode(message.body) + const code = extractOTPCode(message.body) expect(code).to.not.be.undefined expect(code.length).to.equal(6) return code @@ -1543,14 +1546,14 @@ Cypress.Commands.add("getRegistrationCodeFromEmail", (email, opts) => { .getMail({ removeMail: true, email, - subject: "Complete your account registration", + body: "Complete your account registration with the following code", ...opts, }) .should((message) => { expect(message.toAddresses[0].trim()).to.equal(email) }) .then((message) => { - const code = extractRecoveryCode(message.body) + const code = extractOTPCode(message.body) expect(code).to.not.be.undefined expect(code.length).to.equal(6) return code @@ -1562,14 +1565,14 @@ Cypress.Commands.add("getLoginCodeFromEmail", (email, opts) => { .getMail({ removeMail: true, email, - subject: "Login to your account", + body: "Login to your account with the following code", ...opts, }) .should((message) => { expect(message.toAddresses[0].trim()).to.equal(email) }) .then((message) => { - const code = extractRecoveryCode(message.body) + const code = extractOTPCode(message.body) expect(code).to.not.be.undefined expect(code.length).to.equal(6) return code diff --git a/test/e2e/cypress/support/index.d.ts b/test/e2e/cypress/support/index.d.ts index a6a7120937de..667360b32c66 100644 --- a/test/e2e/cypress/support/index.d.ts +++ b/test/e2e/cypress/support/index.d.ts @@ -114,6 +114,7 @@ declare global { expectedCount?: number email?: string subject?: string + body?: string }): Chainable performEmailVerification(opts?: { diff --git a/test/e2e/mock/httptarget/go.mod b/test/e2e/mock/httptarget/go.mod new file mode 100644 index 000000000000..2d66a9ff4f48 --- /dev/null +++ b/test/e2e/mock/httptarget/go.mod @@ -0,0 +1,10 @@ +module github.com/ory/mock + +go 1.22.1 + +require ( + github.com/julienschmidt/httprouter v1.3.0 + github.com/ory/graceful v0.1.3 +) + +require github.com/pkg/errors v0.9.1 // indirect diff --git a/test/e2e/mock/httptarget/go.sum b/test/e2e/mock/httptarget/go.sum new file mode 100644 index 000000000000..e44bf27060c1 --- /dev/null +++ b/test/e2e/mock/httptarget/go.sum @@ -0,0 +1,16 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/ory/graceful v0.1.3 h1:FaeXcHZh168WzS+bqruqWEw/HgXWLdNv2nJ+fbhxbhc= +github.com/ory/graceful v0.1.3/go.mod h1:4zFz687IAF7oNHHiB586U4iL+/4aV09o/PYLE34t2bA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/test/e2e/mock/httptarget/main.go b/test/e2e/mock/httptarget/main.go new file mode 100644 index 000000000000..c95d0834fb76 --- /dev/null +++ b/test/e2e/mock/httptarget/main.go @@ -0,0 +1,79 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "cmp" + "fmt" + "io" + "log" + "net/http" + "os" + "sync" + + "github.com/julienschmidt/httprouter" + + "github.com/ory/graceful" +) + +var ( + documentsLock sync.RWMutex + documents = make(map[string][]byte) +) + +func main() { + port := cmp.Or(os.Getenv("PORT"), "4471") + server := graceful.WithDefaults(&http.Server{Addr: fmt.Sprintf(":%s", port)}) + register(server) + if err := graceful.Graceful(server.ListenAndServe, server.Shutdown); err != nil { + log.Fatalln(err) + } +} + +func register(server *http.Server) { + router := httprouter.New() + + router.GET("/health", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + _, _ = w.Write([]byte("OK")) + }) + + router.GET("/documents/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + id := ps.ByName("id") + + documentsLock.RLock() + doc, ok := documents[id] + documentsLock.RUnlock() + + if ok { + _, _ = w.Write(doc) + } else { + w.WriteHeader(http.StatusNotFound) + } + }) + + router.PUT("/documents/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + documentsLock.Lock() + defer documentsLock.Unlock() + id := ps.ByName("id") + + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + documents[id] = body + }) + + router.DELETE("/documents/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + documentsLock.Lock() + defer documentsLock.Unlock() + id := ps.ByName("id") + + delete(documents, id) + w.WriteHeader(http.StatusNoContent) + }) + + server.Handler = router +} diff --git a/test/e2e/package-lock.json b/test/e2e/package-lock.json index f6452591bda9..7dd8c6416648 100644 --- a/test/e2e/package-lock.json +++ b/test/e2e/package-lock.json @@ -8,7 +8,8 @@ "name": "@ory/kratos-e2e-suite", "version": "0.0.1", "dependencies": { - "@faker-js/faker": "8.4.1", + "@faker-js/faker": "9.0.3", + "@types/promise-retry": "^1.1.6", "async-retry": "1.3.3", "mailhog": "4.16.0", "promise-retry": "^2.0.1" @@ -26,6 +27,7 @@ "got": "11.8.2", "json-schema-to-typescript": "12.0.0", "otplib": "12.0.1", + "phone-number-generator-js": "^1.2.12", "process": "0.11.10", "typescript": "4.7.4", "wait-on": "7.0.1", @@ -99,9 +101,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", - "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.3.tgz", + "integrity": "sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==", "funding": [ { "type": "opencollective", @@ -110,8 +112,8 @@ ], "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0", - "npm": ">=6.14.13" + "node": ">=18.0.0", + "npm": ">=9.0.0" } }, "node_modules/@hapi/hoek": { @@ -329,6 +331,15 @@ "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", "dev": true }, + "node_modules/@types/promise-retry": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/promise-retry/-/promise-retry-1.1.6.tgz", + "integrity": "sha512-EC1+OMXV0PZb0pf+cmyxc43MEP2CDumZe4AfuxWboxxEixztIebknpJPZAX5XlodGF1OY+C1E/RAeNGzxf+bJA==", + "license": "MIT", + "dependencies": { + "@types/retry": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -341,8 +352,7 @@ "node_modules/@types/retry": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", - "dev": true + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", @@ -356,6 +366,13 @@ "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.12.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", + "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/yamljs": { "version": "0.2.31", "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.31.tgz", @@ -540,9 +557,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -780,6 +797,25 @@ "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", "dev": true }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, + "node_modules/class-validator/node_modules/libphonenumber-js": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.9.tgz", + "integrity": "sha512-Zs5wf5HaWzW2/inlupe2tstl0I/Tbqo7lH20ZLr6Is58u7Dz2n+gRFGNlj9/gWxFvNfp9+YyDsiegjNhdixB9A==", + "dev": true, + "license": "MIT" + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -1158,14 +1194,16 @@ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" }, "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, "hasInstallScript": true, + "license": "ISC", "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" }, "engines": { @@ -1214,6 +1252,29 @@ "node": ">=0.8.0" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true, + "license": "ISC" + }, "node_modules/event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -1966,6 +2027,13 @@ "node": "> 0.8" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.30", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.30.tgz", + "integrity": "sha512-PLGc+xfrQrkya/YK2/5X+bPpxRmyJBHM+xxz9krUdSgk4Vs2ZwxX5/Ow0lv3r9PDlDtNWb4u+it8MY5rZ0IyGw==", + "dev": true, + "license": "MIT" + }, "node_modules/listr2": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", @@ -2365,6 +2433,18 @@ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, + "node_modules/phone-number-generator-js": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/phone-number-generator-js/-/phone-number-generator-js-1.2.12.tgz", + "integrity": "sha512-AtJpQjHFlXqD2ZMZLUlzrNKNTwwyn9gFASeTgfcGqdWUUHsddThKkCbsJ7VyDyj7C2Xo0oce/XOARH8eElas6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "class-validator": "0.14.1", + "libphonenumber-js": "1.10.30", + "lodash": "4.17.21" + } + }, "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -2446,6 +2526,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -2964,6 +3045,16 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -3060,10 +3151,11 @@ "dev": true }, "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -3172,9 +3264,9 @@ } }, "@faker-js/faker": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", - "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==" + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.3.tgz", + "integrity": "sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==" }, "@hapi/hoek": { "version": "9.3.0", @@ -3374,6 +3466,14 @@ "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", "dev": true }, + "@types/promise-retry": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/promise-retry/-/promise-retry-1.1.6.tgz", + "integrity": "sha512-EC1+OMXV0PZb0pf+cmyxc43MEP2CDumZe4AfuxWboxxEixztIebknpJPZAX5XlodGF1OY+C1E/RAeNGzxf+bJA==", + "requires": { + "@types/retry": "*" + } + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -3386,8 +3486,7 @@ "@types/retry": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", - "dev": true + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" }, "@types/sinonjs__fake-timers": { "version": "8.1.1", @@ -3401,6 +3500,12 @@ "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, + "@types/validator": { + "version": "13.12.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", + "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==", + "dev": true + }, "@types/yamljs": { "version": "0.2.31", "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.31.tgz", @@ -3538,9 +3643,9 @@ "dev": true }, "axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "requires": { "follow-redirects": "^1.15.6", @@ -3718,6 +3823,25 @@ "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", "dev": true }, + "class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dev": true, + "requires": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + }, + "dependencies": { + "libphonenumber-js": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.9.tgz", + "integrity": "sha512-Zs5wf5HaWzW2/inlupe2tstl0I/Tbqo7lH20ZLr6Is58u7Dz2n+gRFGNlj9/gWxFvNfp9+YyDsiegjNhdixB9A==", + "dev": true + } + } + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -4020,13 +4144,14 @@ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" }, "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, "requires": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" } }, @@ -4069,6 +4194,26 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true + } + } + }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -4624,6 +4769,12 @@ "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", "dev": true }, + "libphonenumber-js": { + "version": "1.10.30", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.30.tgz", + "integrity": "sha512-PLGc+xfrQrkya/YK2/5X+bPpxRmyJBHM+xxz9krUdSgk4Vs2ZwxX5/Ow0lv3r9PDlDtNWb4u+it8MY5rZ0IyGw==", + "dev": true + }, "listr2": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", @@ -4924,6 +5075,17 @@ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, + "phone-number-generator-js": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/phone-number-generator-js/-/phone-number-generator-js-1.2.12.tgz", + "integrity": "sha512-AtJpQjHFlXqD2ZMZLUlzrNKNTwwyn9gFASeTgfcGqdWUUHsddThKkCbsJ7VyDyj7C2Xo0oce/XOARH8eElas6A==", + "dev": true, + "requires": { + "class-validator": "0.14.1", + "libphonenumber-js": "1.10.30", + "lodash": "4.17.21" + } + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -5362,6 +5524,12 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, + "validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -5436,9 +5604,9 @@ "dev": true }, "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "requires": {} }, diff --git a/test/e2e/package.json b/test/e2e/package.json index 05b04db6880a..a33ac330bf00 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -11,7 +11,8 @@ "wait-on": "wait-on" }, "dependencies": { - "@faker-js/faker": "8.4.1", + "@faker-js/faker": "9.0.3", + "@types/promise-retry": "^1.1.6", "async-retry": "1.3.3", "mailhog": "4.16.0", "promise-retry": "^2.0.1" @@ -29,6 +30,7 @@ "got": "11.8.2", "json-schema-to-typescript": "12.0.0", "otplib": "12.0.1", + "phone-number-generator-js": "^1.2.12", "process": "0.11.10", "typescript": "4.7.4", "wait-on": "7.0.1", diff --git a/test/e2e/playwright.config.ts b/test/e2e/playwright.config.ts index a3f81c73060d..2ace64395520 100644 --- a/test/e2e/playwright.config.ts +++ b/test/e2e/playwright.config.ts @@ -65,6 +65,12 @@ export default defineConfig({ reuseExistingServer: false, url: "http://localhost:8025/", }, + { + command: "go run test/e2e/mock/httptarget/main.go", + cwd: "../..", + reuseExistingServer: false, + url: "http://localhost:4471/health", + }, ], }) diff --git a/test/e2e/playwright/actions/identity.ts b/test/e2e/playwright/actions/identity.ts new file mode 100644 index 000000000000..f05bc2f68a3e --- /dev/null +++ b/test/e2e/playwright/actions/identity.ts @@ -0,0 +1,61 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { faker } from "@faker-js/faker" +import { APIRequestContext } from "@playwright/test" +import { CreateIdentityBody } from "@ory/kratos-client" +import { generatePhoneNumber, CountryNames } from "phone-number-generator-js" +import { expect } from "../fixtures" + +export async function createIdentity( + request: APIRequestContext, + data: Partial, +) { + const resp = await request.post("http://localhost:4434/admin/identities", { + data, + }) + expect(resp.status()).toBe(201) + return await resp.json() +} + +export async function createIdentityWithPhoneNumber( + request: APIRequestContext, +) { + const phone = generatePhoneNumber({ + countryName: CountryNames.Germany, + withoutCountryCode: false, + }) + return { + identity: await createIdentity(request, { + schema_id: "sms", + traits: { + phone, + }, + }), + phone, + } +} + +export async function createIdentityWithPassword(request: APIRequestContext) { + const email = faker.internet.email({ provider: "ory.sh" }) + const password = faker.internet.password() + return { + identity: await createIdentity(request, { + schema_id: "email", + traits: { + email, + website: faker.internet.url(), + }, + + credentials: { + password: { + config: { + password, + }, + }, + }, + }), + email, + password, + } +} diff --git a/test/e2e/playwright/actions/webhook.ts b/test/e2e/playwright/actions/webhook.ts new file mode 100644 index 000000000000..82b2511d5d90 --- /dev/null +++ b/test/e2e/playwright/actions/webhook.ts @@ -0,0 +1,57 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { request } from "@playwright/test" +import retry from "promise-retry" +import { retryOptions } from "../lib/config" + +export const WEBHOOK_TARGET = "http://127.0.0.1:4471" + +const baseUrl = WEBHOOK_TARGET + +/** + * Fetches a documented (hopefully) created by web hook + * + * @param key + */ +export async function fetchDocument(key: string) { + const r = await request.newContext() + + return retry(async (retry) => { + const res = await r.get(documentUrl(key)) + if (res.status() !== 200) { + const body = await res.text() + const message = `Expected response code 200 but received ${res.status()}: ${body}` + return retry(message) + } + return await res.json() + }, retryOptions) +} + +/** + * Fetches a documented (hopefully) created by web hook + * + * @param key + */ +export async function deleteDocument(key: string) { + const r = await request.newContext() + + return retry(async (retry) => { + const res = await r.delete(documentUrl(key)) + if (res.status() !== 204) { + const body = await res.text() + const message = `Expected response code 204 but received ${res.status()}: ${body}` + return retry(message) + } + return + }, retryOptions) +} + +/** + * Returns the URL for a specific document + * + * @param key + */ +export function documentUrl(key: string) { + return `${baseUrl}/documents/${key}` +} diff --git a/test/e2e/playwright/fixtures/index.ts b/test/e2e/playwright/fixtures/index.ts index b915dd4d937f..7f227ac7b5ee 100644 --- a/test/e2e/playwright/fixtures/index.ts +++ b/test/e2e/playwright/fixtures/index.ts @@ -1,14 +1,13 @@ // Copyright © 2023 Ory Corp // SPDX-License-Identifier: Apache-2.0 -import { faker } from "@faker-js/faker" import { Identity } from "@ory/kratos-client" import { + APIRequestContext, CDPSession, - test as base, expect as baseExpect, - APIRequestContext, Page, + test as base, } from "@playwright/test" import { writeFile } from "fs/promises" import { merge } from "lodash" @@ -19,6 +18,7 @@ import { SessionWithResponse } from "../types" import { retryOptions } from "../lib/request" import promiseRetry from "promise-retry" import { Protocol } from "playwright-core/types/protocol" +import { createIdentityWithPassword } from "../actions/identity" // from https://stackoverflow.com/questions/61132262/typescript-deep-partial type DeepPartial = T extends object @@ -104,31 +104,15 @@ export const test = base.extend({ await pageCDPSession.send("WebAuthn.disable") }, identity: async ({ request }, use, i) => { - const email = faker.internet.email({ provider: "ory.sh" }) - const password = faker.internet.password() - const resp = await request.post("http://localhost:4434/admin/identities", { - data: { - schema_id: "email", - traits: { - email, - website: faker.internet.url(), - }, - - credentials: { - password: { - config: { - password, - }, - }, - }, - }, - }) - const oryIdentity = await resp.json() + const { + identity: oryIdentity, + password, + email, + } = await createIdentityWithPassword(request) i.attach("identity", { body: JSON.stringify(oryIdentity, null, 2), contentType: "application/json", }) - expect(resp.status()).toBe(201) await use({ oryIdentity, email, diff --git a/test/e2e/playwright/fixtures/schemas/sms.ts b/test/e2e/playwright/fixtures/schemas/sms.ts new file mode 100644 index 000000000000..0e5186633dfb --- /dev/null +++ b/test/e2e/playwright/fixtures/schemas/sms.ts @@ -0,0 +1,35 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +export default { + $id: "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json", + $schema: "http://json-schema.org/draft-07/schema#", + title: "Person", + type: "object", + properties: { + traits: { + type: "object", + properties: { + phone: { + type: "string", + format: "tel", + title: "Your Phone Number", + minLength: 3, + "ory.sh/kratos": { + credentials: { + code: { + identifier: true, + via: "sms", + }, + }, + verification: { + via: "sms", + }, + }, + }, + }, + required: ["phone"], + additionalProperties: false, + }, + }, +} diff --git a/test/e2e/playwright/lib/config.ts b/test/e2e/playwright/lib/config.ts new file mode 100644 index 000000000000..4584d51f746e --- /dev/null +++ b/test/e2e/playwright/lib/config.ts @@ -0,0 +1,14 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import type { OperationOptions } from "retry" + +export type RetryOptions = OperationOptions + +export const retryOptions: RetryOptions = { + retries: 20, + factor: 1, + maxTimeout: 500, + minTimeout: 250, + randomize: false, +} diff --git a/test/e2e/playwright/models/elements/registration.ts b/test/e2e/playwright/models/elements/registration.ts new file mode 100644 index 000000000000..029903f14e49 --- /dev/null +++ b/test/e2e/playwright/models/elements/registration.ts @@ -0,0 +1,45 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { expect, Page } from "@playwright/test" +import { createInputLocator, InputLocator } from "../../selectors/input" +import { OryKratosConfiguration } from "../../../shared/config" + +export class RegistrationPage { + public identifier: InputLocator + + constructor(readonly page: Page, readonly config: OryKratosConfiguration) { + this.identifier = createInputLocator(page, "identifier") + } + + async open() { + await Promise.all([ + this.page.goto(this.config.selfservice.flows.registration.ui_url), + this.isReady(), + this.page.waitForURL((url) => + url + .toString() + .includes(this.config.selfservice.flows.registration.ui_url), + ), + ]) + await this.isReady() + } + + inputField(name: string) { + return this.page.locator(`input[name="${name}"]`) + } + + submitField(name: string) { + return this.page.locator(`[type="submit"][name="method"][value="${name}"]`) + } + + async isReady() { + await expect(this.inputField("csrf_token").nth(0)).toBeHidden() + } + + async triggerRegistrationWithCode(identifier: string) { + await this.inputField("traits.phone").fill(identifier) + await this.submitField("profile").click() + await this.submitField("code").click() + } +} diff --git a/test/e2e/playwright/tests/desktop/code/sms.spec.ts b/test/e2e/playwright/tests/desktop/code/sms.spec.ts new file mode 100644 index 000000000000..8d374c2ce1b7 --- /dev/null +++ b/test/e2e/playwright/tests/desktop/code/sms.spec.ts @@ -0,0 +1,116 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { test } from "../../../fixtures" +import { toConfig } from "../../../lib/helper" +import smsSchema from "../../../fixtures/schemas/sms" +import { LoginPage } from "../../../models/elements/login" +import { hasSession } from "../../../actions/session" +import { createIdentityWithPhoneNumber } from "../../../actions/identity" +import { + deleteDocument, + documentUrl, + fetchDocument, +} from "../../../actions/webhook" +import { RegistrationPage } from "../../../models/elements/registration" +import { CountryNames, generatePhoneNumber } from "phone-number-generator-js" + +const documentId = "doc-" + Math.random().toString(36).substring(7) + +test.describe("account enumeration protection off", () => { + test.use({ + configOverride: { + security: { + account_enumeration: { + enabled: false, + }, + }, + selfservice: { + flows: { + login: { + style: "unified", + }, + registration: { + after: { + code: { + hooks: [ + { + hook: "session", + }, + ], + }, + }, + }, + }, + methods: { + code: { + passwordless_enabled: true, + }, + password: { + enabled: false, + }, + }, + }, + courier: { + channels: [ + { + id: "sms", + type: "http", + request_config: { + body: "base64://ZnVuY3Rpb24oY3R4KSB7DQpjdHg6IGN0eCwNCn0=", + method: "PUT", + url: documentUrl(documentId), + }, + }, + ], + }, + identity: { + default_schema_id: "sms", + schemas: [ + { + id: "sms", + url: + "base64://" + + Buffer.from(JSON.stringify(smsSchema), "ascii").toString( + "base64", + ), + }, + ], + }, + }, + }) + + test.afterEach(async () => { + await deleteDocument(documentId) + }) + + test("login succeeds", async ({ page, config, kratosPublicURL }) => { + const identity = await createIdentityWithPhoneNumber(page.request) + + const login = new LoginPage(page, config) + await login.open() + await login.triggerLoginWithCode(identity.phone) + + const result = await fetchDocument(documentId) + await login.codeInput.input.fill(result.ctx.template_data.login_code) + await login.codeSubmit.getByText("Continue").click() + await hasSession(page.request, kratosPublicURL) + }) + + test("registration succeeds", async ({ page, config, kratosPublicURL }) => { + const phone = generatePhoneNumber({ + countryName: CountryNames.Germany, + withoutCountryCode: false, + }) + + const registration = new RegistrationPage(page, config) + await registration.open() + await registration.triggerRegistrationWithCode(phone) + + const result = await fetchDocument(documentId) + const code = result.ctx.template_data.registration_code + await registration.inputField("code").fill(code) + await registration.submitField("code").getByText("Continue").click() + await hasSession(page.request, kratosPublicURL) + }) +}) diff --git a/text/id.go b/text/id.go index 3d9028af8ed0..c6d26323f5e2 100644 --- a/text/id.go +++ b/text/id.go @@ -23,7 +23,7 @@ const ( InfoSelfServiceLoginContinueWebAuthn // 1010011 InfoSelfServiceLoginWebAuthnPasswordless // 1010012 InfoSelfServiceLoginContinue // 1010013 - InfoSelfServiceLoginEmailWithCodeSent // 1010014 + InfoSelfServiceLoginCodeSent // 1010014 InfoSelfServiceLoginCode // 1010015 InfoSelfServiceLoginLink // 1010016 InfoSelfServiceLoginAndLink // 1010017 diff --git a/text/message_login.go b/text/message_login.go index f5ebc54d9899..f60761f49e69 100644 --- a/text/message_login.go +++ b/text/message_login.go @@ -224,11 +224,11 @@ func NewInfoSelfServiceLoginContinue() *Message { } } -func NewLoginEmailWithCodeSent() *Message { +func NewLoginCodeSent() *Message { return &Message{ - ID: InfoSelfServiceLoginEmailWithCodeSent, + ID: InfoSelfServiceLoginCodeSent, Type: Info, - Text: "An email containing a code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and retry the login.", + Text: "A code has been sent to the address you provided. If you have not received an message, check the spelling of the address and retry the login.", } } diff --git a/text/message_registration.go b/text/message_registration.go index 443d234cfda7..5249f21e3b8b 100644 --- a/text/message_registration.go +++ b/text/message_registration.go @@ -84,7 +84,7 @@ func NewRegistrationEmailWithCodeSent() *Message { return &Message{ ID: InfoSelfServiceRegistrationEmailWithCodeSent, Type: Info, - Text: "An email containing a code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and retry the registration.", + Text: "A code has been sent to the address(es) you provided. If you have not received a message, check the spelling of the address and retry the registration.", } } From 6b81d4aacf77ed13e1def71a2c32aa19ea8e0582 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:01:47 +0000 Subject: [PATCH 021/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From bafd32a087f955880291d0649d77c442a7218030 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:55:45 +0000 Subject: [PATCH 022/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 83 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cf2d4290239..e5207690f392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,16 @@ **Table of Contents** -- [ (2024-10-03)](#2024-10-03) +- [ (2024-10-04)](#2024-10-04) - [Bug Fixes](#bug-fixes) - [Documentation](#documentation) + - [Features](#features) - [1.3.0 (2024-09-26)](#130-2024-09-26) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes-1) - [Code Generation](#code-generation) - [Documentation](#documentation-1) - - [Features](#features) + - [Features](#features-1) - [Tests](#tests) - [Unclassified](#unclassified) - [1.2.0 (2024-06-05)](#120-2024-06-05) @@ -21,7 +22,7 @@ - [Bug Fixes](#bug-fixes-2) - [Code Generation](#code-generation-1) - [Documentation](#documentation-2) - - [Features](#features-1) + - [Features](#features-2) - [Tests](#tests-1) - [Unclassified](#unclassified-1) - [1.1.0 (2024-02-20)](#110-2024-02-20) @@ -29,7 +30,7 @@ - [Bug Fixes](#bug-fixes-3) - [Code Generation](#code-generation-2) - [Documentation](#documentation-3) - - [Features](#features-2) + - [Features](#features-3) - [Reverts](#reverts) - [Tests](#tests-2) - [Unclassified](#unclassified-2) @@ -37,7 +38,7 @@ - [Bug Fixes](#bug-fixes-4) - [Code Generation](#code-generation-3) - [Documentation](#documentation-4) - - [Features](#features-3) + - [Features](#features-4) - [Tests](#tests-3) - [Unclassified](#unclassified-3) - [0.13.0 (2023-04-18)](#0130-2023-04-18) @@ -46,7 +47,7 @@ - [Code Generation](#code-generation-4) - [Code Refactoring](#code-refactoring) - [Documentation](#documentation-5) - - [Features](#features-4) + - [Features](#features-5) - [Tests](#tests-4) - [Unclassified](#unclassified-4) - [0.11.1 (2023-01-14)](#0111-2023-01-14) @@ -54,18 +55,18 @@ - [Bug Fixes](#bug-fixes-6) - [Code Generation](#code-generation-5) - [Documentation](#documentation-6) - - [Features](#features-5) + - [Features](#features-6) - [Tests](#tests-5) - [0.11.0 (2022-12-02)](#0110-2022-12-02) - [Code Generation](#code-generation-6) - - [Features](#features-6) + - [Features](#features-7) - [0.11.0-alpha.0.pre.2 (2022-11-28)](#0110-alpha0pre2-2022-11-28) - [Breaking Changes](#breaking-changes-5) - [Bug Fixes](#bug-fixes-7) - [Code Generation](#code-generation-7) - [Code Refactoring](#code-refactoring-1) - [Documentation](#documentation-7) - - [Features](#features-7) + - [Features](#features-8) - [Reverts](#reverts-1) - [Tests](#tests-6) - [Unclassified](#unclassified-5) @@ -78,7 +79,7 @@ - [Code Generation](#code-generation-9) - [Code Refactoring](#code-refactoring-2) - [Documentation](#documentation-8) - - [Features](#features-8) + - [Features](#features-9) - [Tests](#tests-7) - [Unclassified](#unclassified-6) - [0.9.0-alpha.3 (2022-03-25)](#090-alpha3-2022-03-25) @@ -95,7 +96,7 @@ - [Code Generation](#code-generation-12) - [Code Refactoring](#code-refactoring-3) - [Documentation](#documentation-10) - - [Features](#features-9) + - [Features](#features-10) - [Tests](#tests-8) - [Unclassified](#unclassified-7) - [0.8.3-alpha.1.pre.0 (2022-01-21)](#083-alpha1pre0-2022-01-21) @@ -104,7 +105,7 @@ - [Code Generation](#code-generation-13) - [Code Refactoring](#code-refactoring-4) - [Documentation](#documentation-11) - - [Features](#features-10) + - [Features](#features-11) - [Tests](#tests-9) - [0.8.2-alpha.1 (2021-12-17)](#082-alpha1-2021-12-17) - [Bug Fixes](#bug-fixes-14) @@ -114,14 +115,14 @@ - [Bug Fixes](#bug-fixes-15) - [Code Generation](#code-generation-15) - [Documentation](#documentation-13) - - [Features](#features-11) + - [Features](#features-12) - [Tests](#tests-10) - [0.8.0-alpha.4.pre.0 (2021-11-09)](#080-alpha4pre0-2021-11-09) - [Breaking Changes](#breaking-changes-10) - [Bug Fixes](#bug-fixes-16) - [Code Generation](#code-generation-16) - [Documentation](#documentation-14) - - [Features](#features-12) + - [Features](#features-13) - [Tests](#tests-11) - [0.8.0-alpha.3 (2021-10-28)](#080-alpha3-2021-10-28) - [Bug Fixes](#bug-fixes-17) @@ -134,7 +135,7 @@ - [Code Generation](#code-generation-19) - [Code Refactoring](#code-refactoring-5) - [Documentation](#documentation-15) - - [Features](#features-13) + - [Features](#features-14) - [Reverts](#reverts-2) - [Tests](#tests-12) - [Unclassified](#unclassified-8) @@ -146,13 +147,13 @@ - [Bug Fixes](#bug-fixes-19) - [Code Generation](#code-generation-22) - [Documentation](#documentation-16) - - [Features](#features-14) + - [Features](#features-15) - [Tests](#tests-13) - [0.7.3-alpha.1 (2021-08-28)](#073-alpha1-2021-08-28) - [Bug Fixes](#bug-fixes-20) - [Code Generation](#code-generation-23) - [Documentation](#documentation-17) - - [Features](#features-15) + - [Features](#features-16) - [0.7.1-alpha.1 (2021-07-22)](#071-alpha1-2021-07-22) - [Bug Fixes](#bug-fixes-21) - [Code Generation](#code-generation-24) @@ -164,7 +165,7 @@ - [Code Generation](#code-generation-25) - [Code Refactoring](#code-refactoring-6) - [Documentation](#documentation-19) - - [Features](#features-16) + - [Features](#features-17) - [Tests](#tests-15) - [Unclassified](#unclassified-9) - [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17) @@ -177,25 +178,25 @@ - [Documentation](#documentation-20) - [0.6.1-alpha.1 (2021-05-11)](#061-alpha1-2021-05-11) - [Code Generation](#code-generation-28) - - [Features](#features-17) + - [Features](#features-18) - [0.6.0-alpha.2 (2021-05-07)](#060-alpha2-2021-05-07) - [Bug Fixes](#bug-fixes-24) - [Code Generation](#code-generation-29) - - [Features](#features-18) + - [Features](#features-19) - [0.6.0-alpha.1 (2021-05-05)](#060-alpha1-2021-05-05) - [Breaking Changes](#breaking-changes-14) - [Bug Fixes](#bug-fixes-25) - [Code Generation](#code-generation-30) - [Code Refactoring](#code-refactoring-8) - [Documentation](#documentation-21) - - [Features](#features-19) + - [Features](#features-20) - [Tests](#tests-16) - [Unclassified](#unclassified-10) - [0.5.5-alpha.1 (2020-12-09)](#055-alpha1-2020-12-09) - [Bug Fixes](#bug-fixes-26) - [Code Generation](#code-generation-31) - [Documentation](#documentation-22) - - [Features](#features-20) + - [Features](#features-21) - [Tests](#tests-17) - [Unclassified](#unclassified-11) - [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11) @@ -203,12 +204,12 @@ - [Code Generation](#code-generation-32) - [Code Refactoring](#code-refactoring-9) - [Documentation](#documentation-23) - - [Features](#features-21) + - [Features](#features-22) - [0.5.3-alpha.1 (2020-10-27)](#053-alpha1-2020-10-27) - [Bug Fixes](#bug-fixes-28) - [Code Generation](#code-generation-33) - [Documentation](#documentation-24) - - [Features](#features-22) + - [Features](#features-23) - [Tests](#tests-18) - [0.5.2-alpha.1 (2020-10-22)](#052-alpha1-2020-10-22) - [Bug Fixes](#bug-fixes-29) @@ -219,7 +220,7 @@ - [Bug Fixes](#bug-fixes-30) - [Code Generation](#code-generation-35) - [Documentation](#documentation-26) - - [Features](#features-23) + - [Features](#features-24) - [Tests](#tests-20) - [Unclassified](#unclassified-12) - [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15) @@ -228,7 +229,7 @@ - [Code Generation](#code-generation-36) - [Code Refactoring](#code-refactoring-10) - [Documentation](#documentation-27) - - [Features](#features-24) + - [Features](#features-25) - [Tests](#tests-21) - [Unclassified](#unclassified-13) - [0.4.6-alpha.1 (2020-07-13)](#046-alpha1-2020-07-13) @@ -253,7 +254,7 @@ - [Code Generation](#code-generation-42) - [Code Refactoring](#code-refactoring-11) - [Documentation](#documentation-29) - - [Features](#features-25) + - [Features](#features-26) - [Unclassified](#unclassified-14) - [0.3.0-alpha.1 (2020-05-15)](#030-alpha1-2020-05-15) - [Breaking Changes](#breaking-changes-17) @@ -261,7 +262,7 @@ - [Chores](#chores) - [Code Refactoring](#code-refactoring-12) - [Documentation](#documentation-30) - - [Features](#features-26) + - [Features](#features-27) - [Unclassified](#unclassified-15) - [0.2.1-alpha.1 (2020-05-05)](#021-alpha1-2020-05-05) - [Chores](#chores-1) @@ -272,7 +273,7 @@ - [Chores](#chores-2) - [Code Refactoring](#code-refactoring-13) - [Documentation](#documentation-32) - - [Features](#features-27) + - [Features](#features-28) - [Unclassified](#unclassified-16) - [0.1.1-alpha.1 (2020-02-18)](#011-alpha1-2020-02-18) - [Bug Fixes](#bug-fixes-40) @@ -282,10 +283,10 @@ - [Bug Fixes](#bug-fixes-41) - [Code Refactoring](#code-refactoring-15) - [Documentation](#documentation-34) - - [Features](#features-28) + - [Features](#features-29) - [0.1.0-alpha.5 (2020-02-06)](#010-alpha5-2020-02-06) - [Documentation](#documentation-35) - - [Features](#features-29) + - [Features](#features-30) - [0.1.0-alpha.4 (2020-02-06)](#010-alpha4-2020-02-06) - [Continuous Integration](#continuous-integration) - [Documentation](#documentation-36) @@ -294,7 +295,7 @@ - [0.1.0-alpha.2 (2020-02-03)](#010-alpha2-2020-02-03) - [Bug Fixes](#bug-fixes-42) - [Documentation](#documentation-37) - - [Features](#features-30) + - [Features](#features-31) - [Unclassified](#unclassified-17) - [0.1.0-alpha.1 (2020-01-31)](#010-alpha1-2020-01-31) - [Documentation](#documentation-38) @@ -335,7 +336,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-03) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-04) ### Bug Fixes @@ -349,6 +350,22 @@ ([#4086](https://github.com/ory/kratos/issues/4086)) ([b22135f](https://github.com/ory/kratos/commit/b22135fa05d7fb47dfeaccd7cdc183d16921a7ac)) +### Features + +- Passwordless SMS and expiry notice in code / link templates + ([#4104](https://github.com/ory/kratos/issues/4104)) + ([462cea9](https://github.com/ory/kratos/commit/462cea91448a00a0db21e20c2c347bf74957dc8f)): + + This feature allows Ory Kratos to use the SMS gateway for login and + registration with code via SMS. + + Additionally, the default email and sms templates have been updated. We now + also expose `ExpiresInMinutes` / `expires_in_minutes` in the templates, making + it easier to remind the user how long the code or link is valid for. + + Closes https://github.com/ory/kratos/issues/1570 Closes + https://github.com/ory/kratos/issues/3779 + # [1.3.0](https://github.com/ory/kratos/compare/v1.2.0...v1.3.0) (2024-09-26) We are thrilled to announce the release From 2f8aaee0716835caaba0dff9b6cc457c2cdff5d4 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:04:54 +0200 Subject: [PATCH 023/158] fix: truncate updated at (#4149) --- persistence/sql/identity/persister_identity.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index de21fc1766e7..3e982730398d 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -1101,7 +1101,7 @@ func (p *IdentityPersister) UpdateIdentity(ctx context.Context, i *identity.Iden } i.NID = p.NetworkID(ctx) - i.UpdatedAt = time.Now().UTC() + i.UpdatedAt = time.Now().UTC().Truncate(time.Microsecond) if err := sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { // This returns "ErrNoRows" if the identity does not exist if err := update.Generic(WithTransaction(ctx, tx), tx, p.r.Tracer(ctx).Tracer(), i); err != nil { From 18056a0f1cfdf42769e5a974b2526ccf5c608cc2 Mon Sep 17 00:00:00 2001 From: Patrik Date: Wed, 9 Oct 2024 14:12:09 +0200 Subject: [PATCH 024/158] fix: registration post persist hooks should not be cancelable (#4148) --- selfservice/flow/registration/hook.go | 47 +++++++++++++++------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index 01ca1847d5d5..a002da524c31 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -111,14 +111,14 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque WithField("identity_id", i.ID). WithField("flow_method", ct). Debug("Running PostRegistrationPrePersistHooks.") - for k, executor := range e.d.PostRegistrationPrePersistHooks(r.Context(), ct) { + for k, executor := range e.d.PostRegistrationPrePersistHooks(ctx, ct) { if err := executor.ExecutePostRegistrationPrePersistHook(w, r, registrationFlow, i); err != nil { if errors.Is(err, ErrHookAbortFlow) { e.d.Logger(). WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(r.Context(), ct))). + WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(ctx, ct))). WithField("identity_id", i.ID). WithField("flow_method", ct). Debug("A ExecutePostRegistrationPrePersistHook hook aborted early.") @@ -129,7 +129,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(r.Context(), ct))). + WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(ctx, ct))). WithField("identity_id", i.ID). WithField("flow_method", ct). WithError(err). @@ -142,18 +142,19 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque e.d.Logger().WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(r.Context(), ct))). + WithField("executors", ExecutorNames(e.d.PostRegistrationPrePersistHooks(ctx, ct))). WithField("identity_id", i.ID). WithField("flow_method", ct). Debug("ExecutePostRegistrationPrePersistHook completed successfully.") } // We need to make sure that the identity has a valid schema before passing it down to the identity pool. - if err := e.d.IdentityValidator().Validate(r.Context(), i); err != nil { + if err := e.d.IdentityValidator().Validate(ctx, i); err != nil { return err - // We're now creating the identity because any of the hooks could trigger a "redirect" or a "session" which - // would imply that the identity has to exist already. - } else if err := e.d.IdentityManager().Create(r.Context(), i); err != nil { + } + // We're now creating the identity because any of the hooks could trigger a "redirect" or a "session" which + // would imply that the identity has to exist already. + if err := e.d.IdentityManager().Create(ctx, i); err != nil { if errors.Is(err, sqlcon.ErrUniqueViolation) { strategy, err := e.d.AllLoginStrategies().Strategy(ct) if err != nil { @@ -161,7 +162,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque } if _, ok := strategy.(login.LinkableStrategy); ok { - duplicateIdentifier, err := e.getDuplicateIdentifier(r.Context(), i) + duplicateIdentifier, err := e.getDuplicateIdentifier(ctx, i) if err != nil { return err } @@ -179,14 +180,19 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque return err } + // At this point the identity is already created and will not be rolled back, so + // we want all PostPersist hooks to be able to continue even when the client cancels the request. + ctx = context.WithoutCancel(ctx) + r = r.WithContext(ctx) + // Verify the redirect URL before we do any other processing. c := e.d.Config() - returnTo, err := x.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(r.Context()), + returnTo, err := x.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx), x.SecureRedirectReturnTo(registrationFlow.ReturnTo), x.SecureRedirectUseSourceURL(registrationFlow.RequestURL), - x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(r.Context())), - x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(r.Context())), - x.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowRegistrationReturnTo(r.Context(), ct.String())), + x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)), + x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)), + x.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowRegistrationReturnTo(ctx, ct.String())), ) if err != nil { return err @@ -207,7 +213,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque WithField("identity_id", i.ID). Info("A new identity has registered using self-service registration.") - span.AddEvent(events.NewRegistrationSucceeded(r.Context(), i.ID, string(registrationFlow.Type), registrationFlow.Active.String(), provider)) + span.AddEvent(events.NewRegistrationSucceeded(ctx, i.ID, string(registrationFlow.Type), registrationFlow.Active.String(), provider)) s := session.NewInactiveSession() @@ -217,8 +223,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque } // We persist the session here so that subsequent hooks (like verification) can use it. - s.AuthenticatedAt = time.Now().UTC() - if err := e.d.SessionPersister().UpsertSession(r.Context(), s); err != nil { + if err := e.d.SessionPersister().UpsertSession(ctx, s); err != nil { return err } @@ -227,14 +232,14 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque WithField("identity_id", i.ID). WithField("flow_method", ct). Debug("Running PostRegistrationPostPersistHooks.") - for k, executor := range e.d.PostRegistrationPostPersistHooks(r.Context(), ct) { + for k, executor := range e.d.PostRegistrationPostPersistHooks(ctx, ct) { if err := executor.ExecutePostRegistrationPostPersistHook(w, r, registrationFlow, s); err != nil { if errors.Is(err, ErrHookAbortFlow) { e.d.Logger(). WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(r.Context(), ct))). + WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(ctx, ct))). WithField("identity_id", i.ID). WithField("flow_method", ct). Debug("A ExecutePostRegistrationPostPersistHook hook aborted early.") @@ -248,7 +253,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(r.Context(), ct))). + WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(ctx, ct))). WithField("identity_id", i.ID). WithField("flow_method", ct). WithError(err). @@ -263,7 +268,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque e.d.Logger().WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(r.Context(), ct))). + WithField("executors", ExecutorNames(e.d.PostRegistrationPostPersistHooks(ctx, ct))). WithField("identity_id", i.ID). WithField("flow_method", ct). Debug("ExecutePostRegistrationPostPersistHook completed successfully.") @@ -301,7 +306,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque // redirect to the verification URL first and then return to Hydra. finalReturnTo = registrationFlow.ReturnToVerification } else { - callbackURL, err := e.d.Hydra().AcceptLoginRequest(r.Context(), + callbackURL, err := e.d.Hydra().AcceptLoginRequest(ctx, hydra.AcceptLoginRequestParams{ LoginChallenge: string(registrationFlow.OAuth2LoginChallenge), IdentityID: i.ID.String(), From 7e0b500aada9c1931c759a43db7360e85afb57e3 Mon Sep 17 00:00:00 2001 From: Patrik Date: Wed, 9 Oct 2024 14:25:55 +0200 Subject: [PATCH 025/158] fix: pass on correct context during verification (#4151) --- internal/client-go/go.sum | 1 + .../strategy/code/strategy_verification.go | 72 +++++++++---------- .../strategy/link/strategy_verification.go | 10 +-- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/selfservice/strategy/code/strategy_verification.go b/selfservice/strategy/code/strategy_verification.go index 4a41991e2a68..9cfb6eb74535 100644 --- a/selfservice/strategy/code/strategy_verification.go +++ b/selfservice/strategy/code/strategy_verification.go @@ -146,7 +146,7 @@ func (s *Strategy) Verify(w http.ResponseWriter, r *http.Request, f *verificatio f.TransientPayload = body.TransientPayload - if err := flow.MethodEnabledAndAllowed(r.Context(), f.GetFlowName(), s.VerificationStrategyID(), string(body.getMethod()), s.deps); err != nil { + if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.VerificationStrategyID(), string(body.getMethod()), s.deps); err != nil { return s.handleVerificationError(r, f, body, err) } @@ -158,15 +158,15 @@ func (s *Strategy) Verify(w http.ResponseWriter, r *http.Request, f *verificatio case flow.StateChooseMethod: fallthrough case flow.StateEmailSent: - return s.verificationHandleFormSubmission(w, r, f, body) + return s.verificationHandleFormSubmission(ctx, w, r, f, body) case flow.StatePassedChallenge: - return s.retryVerificationFlowWithMessage(w, r, f.Type, text.NewErrorValidationVerificationRetrySuccess()) + return s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationRetrySuccess()) default: - return s.retryVerificationFlowWithMessage(w, r, f.Type, text.NewErrorValidationVerificationStateFailure()) + return s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationStateFailure()) } } -func (s *Strategy) handleLinkClick(w http.ResponseWriter, r *http.Request, f *verification.Flow, code string) error { +func (s *Strategy) handleLinkClick(ctx context.Context, w http.ResponseWriter, r *http.Request, f *verification.Flow, code string) error { // Pre-fill the code if codeField := f.UI.Nodes.Find("code"); codeField != nil { codeField.Attributes.SetValue(code) @@ -178,41 +178,41 @@ func (s *Strategy) handleLinkClick(w http.ResponseWriter, r *http.Request, f *ve f.UI.SetCSRF(csrfToken) f.CSRFToken = csrfToken - if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil { + if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil { return err } // we always redirect to the browser UI here to allow API flows to complete aswell // TODO: In the future, we might want to redirect to a custom URI scheme here, to allow to open an app on the device of // the user to handle the flow directly. - http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther) + http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther) return errors.WithStack(flow.ErrCompletedByStrategy) } -func (s *Strategy) verificationHandleFormSubmission(w http.ResponseWriter, r *http.Request, f *verification.Flow, body *updateVerificationFlowWithCodeMethod) error { +func (s *Strategy) verificationHandleFormSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request, f *verification.Flow, body *updateVerificationFlowWithCodeMethod) error { if len(body.Code) > 0 { if r.Method == http.MethodGet { // Special case: in the code strategy we send out links as well, that contain the code - return s.handleLinkClick(w, r, f, body.Code) + return s.handleLinkClick(ctx, w, r, f, body.Code) } // If not GET: try to use the submitted code - return s.verificationUseCode(w, r, body.Code, f) + return s.verificationUseCode(ctx, w, r, body.Code, f) } else if len(body.Email) == 0 { // If no code and no email was provided, fail with a validation error return s.handleVerificationError(r, f, body, schema.NewRequiredError("#/email", "email")) } - if err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(r.Context()), s.deps.GenerateCSRFToken, body.CSRFToken); err != nil { + if err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(ctx), s.deps.GenerateCSRFToken, body.CSRFToken); err != nil { return s.handleVerificationError(r, f, body, err) } - if err := s.deps.VerificationCodePersister().DeleteVerificationCodesOfFlow(r.Context(), f.ID); err != nil { + if err := s.deps.VerificationCodePersister().DeleteVerificationCodesOfFlow(ctx, f.ID); err != nil { return s.handleVerificationError(r, f, body, err) } - if err := s.deps.CodeSender().SendVerificationCode(r.Context(), f, identity.VerifiableAddressTypeEmail, body.Email); err != nil { + if err := s.deps.CodeSender().SendVerificationCode(ctx, f, identity.VerifiableAddressTypeEmail, body.Email); err != nil { if !errors.Is(err, ErrUnknownAddress) { return s.handleVerificationError(r, f, body, err) } @@ -232,30 +232,30 @@ func (s *Strategy) verificationHandleFormSubmission(w http.ResponseWriter, r *ht ) } - if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil { + if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil { return s.handleVerificationError(r, f, body, err) } return nil } -func (s *Strategy) verificationUseCode(w http.ResponseWriter, r *http.Request, codeString string, f *verification.Flow) error { - code, err := s.deps.VerificationCodePersister().UseVerificationCode(r.Context(), f.ID, codeString) +func (s *Strategy) verificationUseCode(ctx context.Context, w http.ResponseWriter, r *http.Request, codeString string, f *verification.Flow) error { + code, err := s.deps.VerificationCodePersister().UseVerificationCode(ctx, f.ID, codeString) if errors.Is(err, ErrCodeNotFound) { f.UI.Messages.Clear() f.UI.Messages.Add(text.NewErrorValidationVerificationCodeInvalidOrAlreadyUsed()) - if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil { - return s.retryVerificationFlowWithError(w, r, f.Type, err) + if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil { + return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err) } if x.IsBrowserRequest(r) { - http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther) + http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther) } else { s.deps.Writer().Write(w, r, f) } return errors.WithStack(flow.ErrCompletedByStrategy) } else if err != nil { - return s.retryVerificationFlowWithError(w, r, f.Type, err) + return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err) } address := code.VerifiableAddress @@ -263,16 +263,16 @@ func (s *Strategy) verificationUseCode(w http.ResponseWriter, r *http.Request, c verifiedAt := sqlxx.NullTime(time.Now().UTC()) address.VerifiedAt = &verifiedAt address.Status = identity.VerifiableAddressStatusCompleted - if err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), address); err != nil { - return s.retryVerificationFlowWithError(w, r, f.Type, err) + if err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address); err != nil { + return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err) } - i, err := s.deps.IdentityPool().GetIdentity(r.Context(), code.VerifiableAddress.IdentityID, identity.ExpandDefault) + i, err := s.deps.IdentityPool().GetIdentity(ctx, code.VerifiableAddress.IdentityID, identity.ExpandDefault) if err != nil { - return s.retryVerificationFlowWithError(w, r, f.Type, err) + return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err) } - returnTo := f.ContinueURL(r.Context(), s.deps.Config()) + returnTo := f.ContinueURL(ctx, s.deps.Config()) f.UI = &container.Container{ Method: "GET", @@ -288,18 +288,18 @@ func (s *Strategy) verificationUseCode(w http.ResponseWriter, r *http.Request, c Append(node.NewAnchorField("continue", returnTo.String(), node.CodeGroup, text.NewInfoNodeLabelContinue()). WithMetaLabel(text.NewInfoNodeLabelContinue())) - if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil { - return s.retryVerificationFlowWithError(w, r, flow.TypeBrowser, err) + if err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil { + return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err) } if err := s.deps.VerificationExecutor().PostVerificationHook(w, r, f, i); err != nil { - return s.retryVerificationFlowWithError(w, r, f.Type, err) + return s.retryVerificationFlowWithError(ctx, w, r, f.Type, err) } return nil } -func (s *Strategy) retryVerificationFlowWithMessage(w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error { +func (s *Strategy) retryVerificationFlowWithMessage(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error { s.deps. Logger(). WithRequest(r). @@ -307,27 +307,27 @@ func (s *Strategy) retryVerificationFlowWithMessage(w http.ResponseWriter, r *ht Debug("A verification flow is being retried because a validation error occurred.") f, err := verification.NewFlow(s.deps.Config(), - s.deps.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft) + s.deps.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft) if err != nil { return s.handleVerificationError(r, f, nil, err) } f.UI.Messages.Add(message) - if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(r.Context(), f); err != nil { + if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil { return s.handleVerificationError(r, f, nil, err) } if x.IsJSONRequest(r) { s.deps.Writer().WriteError(w, r, flow.NewFlowReplacedError(text.NewErrorSystemGeneric("An error occured, please use the new flow.")).WithFlow(f)) } else { - http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther) + http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther) } return errors.WithStack(flow.ErrCompletedByStrategy) } -func (s *Strategy) retryVerificationFlowWithError(w http.ResponseWriter, r *http.Request, ft flow.Type, verErr error) error { +func (s *Strategy) retryVerificationFlowWithError(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, verErr error) error { s.deps. Logger(). WithRequest(r). @@ -335,7 +335,7 @@ func (s *Strategy) retryVerificationFlowWithError(w http.ResponseWriter, r *http Debug("A verification flow is being retried because an error occurred.") f, err := verification.NewFlow(s.deps.Config(), - s.deps.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft) + s.deps.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.deps.CSRFHandler().RegenerateToken(w, r), r, s, ft) if err != nil { return s.handleVerificationError(r, f, nil, err) } @@ -349,7 +349,7 @@ func (s *Strategy) retryVerificationFlowWithError(w http.ResponseWriter, r *http return err } - if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(r.Context(), f); err != nil { + if err := s.deps.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil { return s.handleVerificationError(r, f, nil, err) } @@ -360,7 +360,7 @@ func (s *Strategy) retryVerificationFlowWithError(w http.ResponseWriter, r *http } s.deps.Writer().WriteError(w, r, toReturn) } else { - http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther) + http.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther) } return errors.WithStack(flow.ErrCompletedByStrategy) diff --git a/selfservice/strategy/link/strategy_verification.go b/selfservice/strategy/link/strategy_verification.go index 2ab187c7b0d5..7dee3f85a8a8 100644 --- a/selfservice/strategy/link/strategy_verification.go +++ b/selfservice/strategy/link/strategy_verification.go @@ -195,7 +195,7 @@ func (s *Strategy) verificationHandleFormSubmission(ctx context.Context, r *http f.Active = sqlxx.NullString(s.NodeGroup()) f.State = flow.StateEmailSent f.UI.Messages.Set(text.NewVerificationEmailSent()) - if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil { + if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil { return s.handleVerificationError(r, f, body, err) } @@ -221,16 +221,16 @@ func (s *Strategy) verificationUseToken(ctx context.Context, w http.ResponseWrit verifiedAt := sqlxx.NullTime(time.Now().UTC()) address.VerifiedAt = &verifiedAt address.Status = identity.VerifiableAddressStatusCompleted - if err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), address); err != nil { + if err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address); err != nil { return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err) } - i, err := s.d.IdentityPool().GetIdentity(r.Context(), token.VerifiableAddress.IdentityID, identity.ExpandDefault) + i, err := s.d.IdentityPool().GetIdentity(ctx, token.VerifiableAddress.IdentityID, identity.ExpandDefault) if err != nil { return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err) } - returnTo := f.ContinueURL(r.Context(), s.d.Config()) + returnTo := f.ContinueURL(ctx, s.d.Config()) f.UI. Nodes. @@ -251,7 +251,7 @@ func (s *Strategy) verificationUseToken(ctx context.Context, w http.ResponseWrit Append(node.NewAnchorField("continue", returnTo.String(), node.LinkGroup, text.NewInfoNodeLabelContinue()). WithMetaLabel(text.NewInfoNodeLabelContinue())) - if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil { + if err := s.d.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil { return s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err) } From 5621ec56d2d072396ab29e33b132f1494da68c96 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:27:42 +0000 Subject: [PATCH 026/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 8cce35b61b5cfbd533b2daa24bc29fa4051a8c10 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:20:39 +0000 Subject: [PATCH 027/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5207690f392..bf4ae22c48b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-10-04)](#2024-10-04) +- [ (2024-10-09)](#2024-10-09) - [Bug Fixes](#bug-fixes) - [Documentation](#documentation) - [Features](#features) @@ -336,13 +336,21 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-04) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-09) ### Bug Fixes - Explicity set updated_at field when updating identity ([#4131](https://github.com/ory/kratos/issues/4131)) ([66afac1](https://github.com/ory/kratos/commit/66afac173dc08b1d6666b107cf7050a2b0b27774)) +- Pass on correct context during verification + ([#4151](https://github.com/ory/kratos/issues/4151)) + ([7e0b500](https://github.com/ory/kratos/commit/7e0b500aada9c1931c759a43db7360e85afb57e3)) +- Registration post persist hooks should not be cancelable + ([#4148](https://github.com/ory/kratos/issues/4148)) + ([18056a0](https://github.com/ory/kratos/commit/18056a0f1cfdf42769e5a974b2526ccf5c608cc2)) +- Truncate updated at ([#4149](https://github.com/ory/kratos/issues/4149)) + ([2f8aaee](https://github.com/ory/kratos/commit/2f8aaee0716835caaba0dff9b6cc457c2cdff5d4)) ### Documentation From f9640ffb4034f2465f0ed9e53420f7dfa635d42f Mon Sep 17 00:00:00 2001 From: Ferdynand Naczynski Date: Thu, 10 Oct 2024 10:19:22 +0200 Subject: [PATCH 028/158] chore: adjust project automation (#4143) --- .github/workflows/pm.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pm.yml b/.github/workflows/pm.yml index 09b9fd6f83a2..b661cd23126e 100644 --- a/.github/workflows/pm.yml +++ b/.github/workflows/pm.yml @@ -7,14 +7,23 @@ on: pull_request: types: - opened + - ready_for_review jobs: - add-to-project: + automate: if: github.event.pull_request.head.repo.fork == false name: Add issue to project runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - uses: actions/add-to-project@v0.5.0 + - uses: ory-corp/planning-automation-action@main with: - project-url: https://github.com/orgs/ory-corp/projects/5 - github-token: ${{ secrets.ORY_BOT_PAT }} + project: 5 + organization: ory-corp + token: ${{ secrets.ORY_BOT_PAT }} + todoLabel: "Needs Triage" + statusName: Status + statusValue: "Needs Triage" + includeEffort: "false" + monthlyMilestoneName: Roadmap Monthly + quarterlyMilestoneName: Roadmap From 6bbf91593a37e4973a86f610290ebab44df8dc81 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Sat, 12 Oct 2024 14:34:53 +0200 Subject: [PATCH 029/158] fix: duplicate autocomplete trigger --- internal/client-go/go.sum | 1 + selfservice/strategy/passkey/passkey_login.go | 4 ---- x/webauthnx/js/webauthn.js | 6 +++++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/selfservice/strategy/passkey/passkey_login.go b/selfservice/strategy/passkey/passkey_login.go index 759d8f235230..b7957a85ad74 100644 --- a/selfservice/strategy/passkey/passkey_login.go +++ b/selfservice/strategy/passkey/passkey_login.go @@ -418,10 +418,6 @@ func (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, f *login.Flow //nolint:staticcheck attr.OnClick = js.WebAuthnTriggersPasskeyLogin.String() + "()" // this function is defined in webauthn.js attr.OnClickTrigger = js.WebAuthnTriggersPasskeyLogin - - //nolint:staticcheck - attr.OnLoad = js.WebAuthnTriggersPasskeyLoginAutocompleteInit.String() + "()" // same here - attr.OnLoadTrigger = js.WebAuthnTriggersPasskeyLoginAutocompleteInit }), ).WithMetaLabel(text.NewInfoSelfServiceLoginPasskey())) diff --git a/x/webauthnx/js/webauthn.js b/x/webauthnx/js/webauthn.js index 638bd4ece082..052ce7f355c5 100644 --- a/x/webauthnx/js/webauthn.js +++ b/x/webauthnx/js/webauthn.js @@ -182,6 +182,7 @@ signal: abortPasskeyConditionalUI.signal, }) .then(function (credential) { + console.trace(credential) resultEl.value = JSON.stringify({ id: credential.id, rawId: __oryWebAuthnBufferEncode(credential.rawId), @@ -203,6 +204,7 @@ resultEl.closest("form").submit() }) .catch((err) => { + console.trace(err) console.log(err) }) } @@ -239,7 +241,7 @@ window.abortPasskeyConditionalUI && window.abortPasskeyConditionalUI.abort( - "only one credentials.get allowed at a time", + "Canceling Passkey autocomplete to complete trigger-based passkey login.", ) navigator.credentials @@ -247,6 +249,7 @@ publicKey: opt.publicKey, }) .then(function (credential) { + console.trace('login',credential) resultEl.value = JSON.stringify({ id: credential.id, rawId: __oryWebAuthnBufferEncode(credential.rawId), @@ -270,6 +273,7 @@ .catch((err) => { // Calling this again will enable the autocomplete once again. console.error(err) + console.trace(err) window.abortPasskeyConditionalUI && __oryPasskeyLoginAutocompleteInit() }) } From f3958a7bad797679484d584d680190b0746389c9 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Sat, 12 Oct 2024 12:36:21 +0000 Subject: [PATCH 030/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From f019a1c4cc359cab9ec48ecf64dc33d73cf90fe5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Oct 2024 11:08:54 +0200 Subject: [PATCH 031/158] chore(deps): bump cookie and express in /test/e2e/proxy (#4153) Bumps [cookie](https://github.com/jshttp/cookie) to 0.7.1 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together. Updates `cookie` from 0.6.0 to 0.7.1 - [Release notes](https://github.com/jshttp/cookie/releases) - [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1) Updates `express` from 4.20.0 to 4.21.1 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md) - [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1) --- updated-dependencies: - dependency-name: cookie dependency-type: indirect - dependency-name: express dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/e2e/proxy/package-lock.json | 191 ++++++++++++------------------- test/e2e/proxy/package.json | 2 +- 2 files changed, 72 insertions(+), 121 deletions(-) diff --git a/test/e2e/proxy/package-lock.json b/test/e2e/proxy/package-lock.json index ba6fc030407c..493b18293584 100644 --- a/test/e2e/proxy/package-lock.json +++ b/test/e2e/proxy/package-lock.json @@ -8,7 +8,7 @@ "name": "proxy", "version": "1.0.0", "dependencies": { - "express": "4.20.0", + "express": "4.21.1", "nodemon": "2.0.22", "request": "2.88.2", "url-join": "5.0.0" @@ -137,20 +137,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -264,9 +250,9 @@ } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -396,23 +382,23 @@ } }, "node_modules/express": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", - "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", @@ -421,11 +407,11 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", - "serve-static": "1.16.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -479,12 +465,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -495,6 +481,14 @@ "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1040,11 +1034,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -1185,45 +1179,25 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", - "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/serve-static/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static/node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.8" } }, "node_modules/set-function-length": { @@ -1566,16 +1540,6 @@ "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" - }, - "dependencies": { - "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { - "side-channel": "^1.0.6" - } - } } }, "brace-expansion": { @@ -1659,9 +1623,9 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" }, "cookie-signature": { "version": "1.0.6", @@ -1757,23 +1721,23 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "express": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", - "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", @@ -1782,11 +1746,11 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", - "serve-static": "1.16.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -1830,17 +1794,24 @@ } }, "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" + }, + "dependencies": { + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + } } }, "forever-agent": { @@ -2231,11 +2202,11 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } }, "range-parser": { @@ -2339,40 +2310,20 @@ } }, "serve-static": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", - "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "requires": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - } + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" } } }, diff --git a/test/e2e/proxy/package.json b/test/e2e/proxy/package.json index 22d4da0be7f7..05ae624dea9e 100644 --- a/test/e2e/proxy/package.json +++ b/test/e2e/proxy/package.json @@ -8,7 +8,7 @@ "start": "nodemon ./proxy.js" }, "dependencies": { - "express": "4.20.0", + "express": "4.21.1", "nodemon": "2.0.22", "request": "2.88.2", "url-join": "5.0.0" From 88c68aa07281a638c9897e76d300d1095b17601d Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:35:04 +0200 Subject: [PATCH 032/158] fix(sdk): remove incorrect attributes (#4163) --- .schema/openapi/patches/selfservice.yaml | 8 +++++ internal/client-go/model_o_auth2_client.go | 36 ------------------- ...consent_request_open_id_connect_context.go | 36 ------------------- .../client-go/model_o_auth2_login_request.go | 36 ------------------- internal/httpclient/model_o_auth2_client.go | 36 ------------------- ...consent_request_open_id_connect_context.go | 36 ------------------- .../httpclient/model_o_auth2_login_request.go | 36 ------------------- spec/api.json | 12 ------- 8 files changed, 8 insertions(+), 228 deletions(-) diff --git a/.schema/openapi/patches/selfservice.yaml b/.schema/openapi/patches/selfservice.yaml index 7887c1c2da74..3fe24b62fb5a 100644 --- a/.schema/openapi/patches/selfservice.yaml +++ b/.schema/openapi/patches/selfservice.yaml @@ -151,3 +151,11 @@ - show_form - success # end + +# Some issues with AdditionalProperties +- op: remove + path: "#/components/schemas/OAuth2LoginRequest/properties/AdditionalProperties" +- op: remove + path: "#/components/schemas/OAuth2ConsentRequestOpenIDConnectContext/properties/AdditionalProperties" +- op: remove + path: "#/components/schemas/OAuth2Client/properties/AdditionalProperties" diff --git a/internal/client-go/model_o_auth2_client.go b/internal/client-go/model_o_auth2_client.go index bbf75b334104..be48d3217ade 100644 --- a/internal/client-go/model_o_auth2_client.go +++ b/internal/client-go/model_o_auth2_client.go @@ -18,7 +18,6 @@ import ( // OAuth2Client struct for OAuth2Client type OAuth2Client struct { - AdditionalPropertiesField map[string]interface{} `json:"AdditionalProperties,omitempty"` // OAuth 2.0 Access Token Strategy AccessTokenStrategy is the strategy used to generate access tokens. Valid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens Setting the stragegy here overrides the global setting in `strategies.access_token`. AccessTokenStrategy *string `json:"access_token_strategy,omitempty"` AllowedCorsOrigins []string `json:"allowed_cors_origins,omitempty"` @@ -125,38 +124,6 @@ func NewOAuth2ClientWithDefaults() *OAuth2Client { return &this } -// GetAdditionalPropertiesField returns the AdditionalPropertiesField field value if set, zero value otherwise. -func (o *OAuth2Client) GetAdditionalPropertiesField() map[string]interface{} { - if o == nil || o.AdditionalPropertiesField == nil { - var ret map[string]interface{} - return ret - } - return o.AdditionalPropertiesField -} - -// GetAdditionalPropertiesFieldOk returns a tuple with the AdditionalPropertiesField field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *OAuth2Client) GetAdditionalPropertiesFieldOk() (map[string]interface{}, bool) { - if o == nil || o.AdditionalPropertiesField == nil { - return nil, false - } - return o.AdditionalPropertiesField, true -} - -// HasAdditionalPropertiesField returns a boolean if a field has been set. -func (o *OAuth2Client) HasAdditionalPropertiesField() bool { - if o != nil && o.AdditionalPropertiesField != nil { - return true - } - - return false -} - -// SetAdditionalPropertiesField gets a reference to the given map[string]interface{} and assigns it to the AdditionalPropertiesField field. -func (o *OAuth2Client) SetAdditionalPropertiesField(v map[string]interface{}) { - o.AdditionalPropertiesField = v -} - // GetAccessTokenStrategy returns the AccessTokenStrategy field value if set, zero value otherwise. func (o *OAuth2Client) GetAccessTokenStrategy() string { if o == nil || o.AccessTokenStrategy == nil { @@ -1697,9 +1664,6 @@ func (o *OAuth2Client) SetUserinfoSignedResponseAlg(v string) { func (o OAuth2Client) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} - if o.AdditionalPropertiesField != nil { - toSerialize["AdditionalProperties"] = o.AdditionalPropertiesField - } if o.AccessTokenStrategy != nil { toSerialize["access_token_strategy"] = o.AccessTokenStrategy } diff --git a/internal/client-go/model_o_auth2_consent_request_open_id_connect_context.go b/internal/client-go/model_o_auth2_consent_request_open_id_connect_context.go index 68884830a9ee..c0cbf7f3129e 100644 --- a/internal/client-go/model_o_auth2_consent_request_open_id_connect_context.go +++ b/internal/client-go/model_o_auth2_consent_request_open_id_connect_context.go @@ -17,7 +17,6 @@ import ( // OAuth2ConsentRequestOpenIDConnectContext OAuth2ConsentRequestOpenIDConnectContext struct for OAuth2ConsentRequestOpenIDConnectContext type OAuth2ConsentRequestOpenIDConnectContext struct { - AdditionalPropertiesField map[string]interface{} `json:"AdditionalProperties,omitempty"` // ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required. OpenID Connect defines it as follows: > Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter. AcrValues []string `json:"acr_values,omitempty"` // Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \\\"feature phone\\\" type display. The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display. @@ -47,38 +46,6 @@ func NewOAuth2ConsentRequestOpenIDConnectContextWithDefaults() *OAuth2ConsentReq return &this } -// GetAdditionalPropertiesField returns the AdditionalPropertiesField field value if set, zero value otherwise. -func (o *OAuth2ConsentRequestOpenIDConnectContext) GetAdditionalPropertiesField() map[string]interface{} { - if o == nil || o.AdditionalPropertiesField == nil { - var ret map[string]interface{} - return ret - } - return o.AdditionalPropertiesField -} - -// GetAdditionalPropertiesFieldOk returns a tuple with the AdditionalPropertiesField field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *OAuth2ConsentRequestOpenIDConnectContext) GetAdditionalPropertiesFieldOk() (map[string]interface{}, bool) { - if o == nil || o.AdditionalPropertiesField == nil { - return nil, false - } - return o.AdditionalPropertiesField, true -} - -// HasAdditionalPropertiesField returns a boolean if a field has been set. -func (o *OAuth2ConsentRequestOpenIDConnectContext) HasAdditionalPropertiesField() bool { - if o != nil && o.AdditionalPropertiesField != nil { - return true - } - - return false -} - -// SetAdditionalPropertiesField gets a reference to the given map[string]interface{} and assigns it to the AdditionalPropertiesField field. -func (o *OAuth2ConsentRequestOpenIDConnectContext) SetAdditionalPropertiesField(v map[string]interface{}) { - o.AdditionalPropertiesField = v -} - // GetAcrValues returns the AcrValues field value if set, zero value otherwise. func (o *OAuth2ConsentRequestOpenIDConnectContext) GetAcrValues() []string { if o == nil || o.AcrValues == nil { @@ -241,9 +208,6 @@ func (o *OAuth2ConsentRequestOpenIDConnectContext) SetUiLocales(v []string) { func (o OAuth2ConsentRequestOpenIDConnectContext) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} - if o.AdditionalPropertiesField != nil { - toSerialize["AdditionalProperties"] = o.AdditionalPropertiesField - } if o.AcrValues != nil { toSerialize["acr_values"] = o.AcrValues } diff --git a/internal/client-go/model_o_auth2_login_request.go b/internal/client-go/model_o_auth2_login_request.go index a788c66ed3ca..9fcd87be72fa 100644 --- a/internal/client-go/model_o_auth2_login_request.go +++ b/internal/client-go/model_o_auth2_login_request.go @@ -17,7 +17,6 @@ import ( // OAuth2LoginRequest OAuth2LoginRequest struct for OAuth2LoginRequest type OAuth2LoginRequest struct { - AdditionalPropertiesField map[string]interface{} `json:"AdditionalProperties,omitempty"` // ID is the identifier (\\\"login challenge\\\") of the login request. It is used to identify the session. Challenge *string `json:"challenge,omitempty"` Client *OAuth2Client `json:"client,omitempty"` @@ -51,38 +50,6 @@ func NewOAuth2LoginRequestWithDefaults() *OAuth2LoginRequest { return &this } -// GetAdditionalPropertiesField returns the AdditionalPropertiesField field value if set, zero value otherwise. -func (o *OAuth2LoginRequest) GetAdditionalPropertiesField() map[string]interface{} { - if o == nil || o.AdditionalPropertiesField == nil { - var ret map[string]interface{} - return ret - } - return o.AdditionalPropertiesField -} - -// GetAdditionalPropertiesFieldOk returns a tuple with the AdditionalPropertiesField field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *OAuth2LoginRequest) GetAdditionalPropertiesFieldOk() (map[string]interface{}, bool) { - if o == nil || o.AdditionalPropertiesField == nil { - return nil, false - } - return o.AdditionalPropertiesField, true -} - -// HasAdditionalPropertiesField returns a boolean if a field has been set. -func (o *OAuth2LoginRequest) HasAdditionalPropertiesField() bool { - if o != nil && o.AdditionalPropertiesField != nil { - return true - } - - return false -} - -// SetAdditionalPropertiesField gets a reference to the given map[string]interface{} and assigns it to the AdditionalPropertiesField field. -func (o *OAuth2LoginRequest) SetAdditionalPropertiesField(v map[string]interface{}) { - o.AdditionalPropertiesField = v -} - // GetChallenge returns the Challenge field value if set, zero value otherwise. func (o *OAuth2LoginRequest) GetChallenge() string { if o == nil || o.Challenge == nil { @@ -373,9 +340,6 @@ func (o *OAuth2LoginRequest) SetSubject(v string) { func (o OAuth2LoginRequest) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} - if o.AdditionalPropertiesField != nil { - toSerialize["AdditionalProperties"] = o.AdditionalPropertiesField - } if o.Challenge != nil { toSerialize["challenge"] = o.Challenge } diff --git a/internal/httpclient/model_o_auth2_client.go b/internal/httpclient/model_o_auth2_client.go index bbf75b334104..be48d3217ade 100644 --- a/internal/httpclient/model_o_auth2_client.go +++ b/internal/httpclient/model_o_auth2_client.go @@ -18,7 +18,6 @@ import ( // OAuth2Client struct for OAuth2Client type OAuth2Client struct { - AdditionalPropertiesField map[string]interface{} `json:"AdditionalProperties,omitempty"` // OAuth 2.0 Access Token Strategy AccessTokenStrategy is the strategy used to generate access tokens. Valid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens Setting the stragegy here overrides the global setting in `strategies.access_token`. AccessTokenStrategy *string `json:"access_token_strategy,omitempty"` AllowedCorsOrigins []string `json:"allowed_cors_origins,omitempty"` @@ -125,38 +124,6 @@ func NewOAuth2ClientWithDefaults() *OAuth2Client { return &this } -// GetAdditionalPropertiesField returns the AdditionalPropertiesField field value if set, zero value otherwise. -func (o *OAuth2Client) GetAdditionalPropertiesField() map[string]interface{} { - if o == nil || o.AdditionalPropertiesField == nil { - var ret map[string]interface{} - return ret - } - return o.AdditionalPropertiesField -} - -// GetAdditionalPropertiesFieldOk returns a tuple with the AdditionalPropertiesField field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *OAuth2Client) GetAdditionalPropertiesFieldOk() (map[string]interface{}, bool) { - if o == nil || o.AdditionalPropertiesField == nil { - return nil, false - } - return o.AdditionalPropertiesField, true -} - -// HasAdditionalPropertiesField returns a boolean if a field has been set. -func (o *OAuth2Client) HasAdditionalPropertiesField() bool { - if o != nil && o.AdditionalPropertiesField != nil { - return true - } - - return false -} - -// SetAdditionalPropertiesField gets a reference to the given map[string]interface{} and assigns it to the AdditionalPropertiesField field. -func (o *OAuth2Client) SetAdditionalPropertiesField(v map[string]interface{}) { - o.AdditionalPropertiesField = v -} - // GetAccessTokenStrategy returns the AccessTokenStrategy field value if set, zero value otherwise. func (o *OAuth2Client) GetAccessTokenStrategy() string { if o == nil || o.AccessTokenStrategy == nil { @@ -1697,9 +1664,6 @@ func (o *OAuth2Client) SetUserinfoSignedResponseAlg(v string) { func (o OAuth2Client) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} - if o.AdditionalPropertiesField != nil { - toSerialize["AdditionalProperties"] = o.AdditionalPropertiesField - } if o.AccessTokenStrategy != nil { toSerialize["access_token_strategy"] = o.AccessTokenStrategy } diff --git a/internal/httpclient/model_o_auth2_consent_request_open_id_connect_context.go b/internal/httpclient/model_o_auth2_consent_request_open_id_connect_context.go index 68884830a9ee..c0cbf7f3129e 100644 --- a/internal/httpclient/model_o_auth2_consent_request_open_id_connect_context.go +++ b/internal/httpclient/model_o_auth2_consent_request_open_id_connect_context.go @@ -17,7 +17,6 @@ import ( // OAuth2ConsentRequestOpenIDConnectContext OAuth2ConsentRequestOpenIDConnectContext struct for OAuth2ConsentRequestOpenIDConnectContext type OAuth2ConsentRequestOpenIDConnectContext struct { - AdditionalPropertiesField map[string]interface{} `json:"AdditionalProperties,omitempty"` // ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required. OpenID Connect defines it as follows: > Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter. AcrValues []string `json:"acr_values,omitempty"` // Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \\\"feature phone\\\" type display. The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display. @@ -47,38 +46,6 @@ func NewOAuth2ConsentRequestOpenIDConnectContextWithDefaults() *OAuth2ConsentReq return &this } -// GetAdditionalPropertiesField returns the AdditionalPropertiesField field value if set, zero value otherwise. -func (o *OAuth2ConsentRequestOpenIDConnectContext) GetAdditionalPropertiesField() map[string]interface{} { - if o == nil || o.AdditionalPropertiesField == nil { - var ret map[string]interface{} - return ret - } - return o.AdditionalPropertiesField -} - -// GetAdditionalPropertiesFieldOk returns a tuple with the AdditionalPropertiesField field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *OAuth2ConsentRequestOpenIDConnectContext) GetAdditionalPropertiesFieldOk() (map[string]interface{}, bool) { - if o == nil || o.AdditionalPropertiesField == nil { - return nil, false - } - return o.AdditionalPropertiesField, true -} - -// HasAdditionalPropertiesField returns a boolean if a field has been set. -func (o *OAuth2ConsentRequestOpenIDConnectContext) HasAdditionalPropertiesField() bool { - if o != nil && o.AdditionalPropertiesField != nil { - return true - } - - return false -} - -// SetAdditionalPropertiesField gets a reference to the given map[string]interface{} and assigns it to the AdditionalPropertiesField field. -func (o *OAuth2ConsentRequestOpenIDConnectContext) SetAdditionalPropertiesField(v map[string]interface{}) { - o.AdditionalPropertiesField = v -} - // GetAcrValues returns the AcrValues field value if set, zero value otherwise. func (o *OAuth2ConsentRequestOpenIDConnectContext) GetAcrValues() []string { if o == nil || o.AcrValues == nil { @@ -241,9 +208,6 @@ func (o *OAuth2ConsentRequestOpenIDConnectContext) SetUiLocales(v []string) { func (o OAuth2ConsentRequestOpenIDConnectContext) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} - if o.AdditionalPropertiesField != nil { - toSerialize["AdditionalProperties"] = o.AdditionalPropertiesField - } if o.AcrValues != nil { toSerialize["acr_values"] = o.AcrValues } diff --git a/internal/httpclient/model_o_auth2_login_request.go b/internal/httpclient/model_o_auth2_login_request.go index a788c66ed3ca..9fcd87be72fa 100644 --- a/internal/httpclient/model_o_auth2_login_request.go +++ b/internal/httpclient/model_o_auth2_login_request.go @@ -17,7 +17,6 @@ import ( // OAuth2LoginRequest OAuth2LoginRequest struct for OAuth2LoginRequest type OAuth2LoginRequest struct { - AdditionalPropertiesField map[string]interface{} `json:"AdditionalProperties,omitempty"` // ID is the identifier (\\\"login challenge\\\") of the login request. It is used to identify the session. Challenge *string `json:"challenge,omitempty"` Client *OAuth2Client `json:"client,omitempty"` @@ -51,38 +50,6 @@ func NewOAuth2LoginRequestWithDefaults() *OAuth2LoginRequest { return &this } -// GetAdditionalPropertiesField returns the AdditionalPropertiesField field value if set, zero value otherwise. -func (o *OAuth2LoginRequest) GetAdditionalPropertiesField() map[string]interface{} { - if o == nil || o.AdditionalPropertiesField == nil { - var ret map[string]interface{} - return ret - } - return o.AdditionalPropertiesField -} - -// GetAdditionalPropertiesFieldOk returns a tuple with the AdditionalPropertiesField field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *OAuth2LoginRequest) GetAdditionalPropertiesFieldOk() (map[string]interface{}, bool) { - if o == nil || o.AdditionalPropertiesField == nil { - return nil, false - } - return o.AdditionalPropertiesField, true -} - -// HasAdditionalPropertiesField returns a boolean if a field has been set. -func (o *OAuth2LoginRequest) HasAdditionalPropertiesField() bool { - if o != nil && o.AdditionalPropertiesField != nil { - return true - } - - return false -} - -// SetAdditionalPropertiesField gets a reference to the given map[string]interface{} and assigns it to the AdditionalPropertiesField field. -func (o *OAuth2LoginRequest) SetAdditionalPropertiesField(v map[string]interface{}) { - o.AdditionalPropertiesField = v -} - // GetChallenge returns the Challenge field value if set, zero value otherwise. func (o *OAuth2LoginRequest) GetChallenge() string { if o == nil || o.Challenge == nil { @@ -373,9 +340,6 @@ func (o *OAuth2LoginRequest) SetSubject(v string) { func (o OAuth2LoginRequest) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} - if o.AdditionalPropertiesField != nil { - toSerialize["AdditionalProperties"] = o.AdditionalPropertiesField - } if o.Challenge != nil { toSerialize["challenge"] = o.Challenge } diff --git a/spec/api.json b/spec/api.json index 442e8a4e785c..2363d0632564 100644 --- a/spec/api.json +++ b/spec/api.json @@ -122,10 +122,6 @@ }, "OAuth2Client": { "properties": { - "AdditionalProperties": { - "additionalProperties": {}, - "type": "object" - }, "access_token_strategy": { "description": "OAuth 2.0 Access Token Strategy AccessTokenStrategy is the strategy used to generate access tokens. Valid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens Setting the stragegy here overrides the global setting in `strategies.access_token`.", "type": "string" @@ -340,10 +336,6 @@ "OAuth2ConsentRequestOpenIDConnectContext": { "description": "OAuth2ConsentRequestOpenIDConnectContext struct for OAuth2ConsentRequestOpenIDConnectContext", "properties": { - "AdditionalProperties": { - "additionalProperties": {}, - "type": "object" - }, "acr_values": { "description": "ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required. OpenID Connect defines it as follows: \u003e Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.", "items": { @@ -380,10 +372,6 @@ "OAuth2LoginRequest": { "description": "OAuth2LoginRequest struct for OAuth2LoginRequest", "properties": { - "AdditionalProperties": { - "additionalProperties": {}, - "type": "object" - }, "challenge": { "description": "ID is the identifier (\\\"login challenge\\\") of the login request. It is used to identify the session.", "type": "string" From b51f780b7e4abc79a757ac1efe1cb65b3d35c8a4 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:41:19 +0200 Subject: [PATCH 033/158] test: update snapshots (#4167) --- ...eteLogin-flow=passwordless-case=passkey_button_exists.json | 4 +--- ...refresh-case=refresh_passwordless_credentials-browser.json | 2 +- ...low=refresh-case=refresh_passwordless_credentials-spa.json | 2 +- ...Settings-case=a_device_is_shown_which_can_be_unlinked.json | 2 +- ...CompleteSettings-case=one_activation_element_is_shown.json | 2 +- ...stFormHydration-method=PopulateLoginMethodFirstFactor.json | 4 +--- ...ydration-method=PopulateLoginMethodFirstFactorRefresh.json | 2 +- ...thod=PopulateLoginMethodIdentifierFirstIdentification.json | 2 +- .../TestRegistration-case=passkey_button_exists-browser.json | 2 +- .../TestRegistration-case=passkey_button_exists-spa.json | 2 +- ...se=webauthn_payload_is_set_when_identity_has_webauthn.json | 2 +- ...should_fail_if_webauthn_login_is_invalid-type=browser.json | 2 +- ...ase=should_fail_if_webauthn_login_is_invalid-type=spa.json | 2 +- ...ordless_enabled=false-case=mfa_v0_credentials-browser.json | 2 +- ...asswordless_enabled=false-case=mfa_v0_credentials-spa.json | 2 +- ...ordless_enabled=false-case=mfa_v1_credentials-browser.json | 2 +- ...asswordless_enabled=false-case=mfa_v1_credentials-spa.json | 2 +- ...ss_enabled=true-case=passwordless_credentials-browser.json | 2 +- ...rdless_enabled=true-case=passwordless_credentials-spa.json | 2 +- ...Settings-case=a_device_is_shown_which_can_be_unlinked.json | 2 +- ...CompleteSettings-case=one_activation_element_is_shown.json | 2 +- ...Refresh-case=mfa_enabled_and_user_has_mfa_credentials.json | 2 +- ...ordless_enabled_and_user_has_passwordless_credentials.json | 2 +- ...thod=PopulateLoginMethodSecondFactor-case=mfa_enabled.json | 2 +- .../TestRegistration-case=webauthn_button_exists-browser.json | 2 +- .../TestRegistration-case=webauthn_button_exists-spa.json | 2 +- 26 files changed, 26 insertions(+), 30 deletions(-) diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json index ffb5ec222642..8e6ca347223f 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json @@ -38,7 +38,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" @@ -55,8 +55,6 @@ "node_type": "input", "onclick": "window.oryPasskeyLogin()", "onclickTrigger": "oryPasskeyLogin", - "onload": "window.oryPasskeyLoginAutocompleteInit()", - "onloadTrigger": "oryPasskeyLoginAutocompleteInit", "type": "button", "value": "" }, diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json index 1e026fb9979a..83a0dab00cf1 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json index 1e026fb9979a..83a0dab00cf1 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index a0383567eda4..b8193ddec074 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -110,7 +110,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index 8d91edf04ce5..f670fa605662 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -62,7 +62,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json index 3e9aa5b5199e..5ca8b52290f1 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" @@ -84,8 +84,6 @@ "disabled": false, "onclick": "window.oryPasskeyLogin()", "onclickTrigger": "oryPasskeyLogin", - "onload": "window.oryPasskeyLoginAutocompleteInit()", - "onloadTrigger": "oryPasskeyLoginAutocompleteInit", "node_type": "input" }, "messages": [], diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json index 33d9f8afd952..d2838777ddb8 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json @@ -18,7 +18,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json index 222443d4988b..1fe32d3cd487 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json index e4c5160c9697..bba362f0c308 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json index e4c5160c9697..bba362f0c308 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json index b180cf04a403..08d46bc5ee98 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json @@ -42,7 +42,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json index 399562e7015d..68c962a81650 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json index 399562e7015d..68c962a81650 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json index 5021f44d2f94..a3dd14c42d98 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json index 5021f44d2f94..a3dd14c42d98 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json index 5021f44d2f94..a3dd14c42d98 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json index 5021f44d2f94..a3dd14c42d98 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json index 5021f44d2f94..a3dd14c42d98 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json index 5021f44d2f94..a3dd14c42d98 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index f0edfe3c5966..c905ffda56b7 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -116,7 +116,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index c15a847d4703..e6fb889e7262 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -68,7 +68,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json index 1be62bb13f42..e4ca52133186 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json index 1be62bb13f42..e4ca52133186 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json index 1be62bb13f42..e4ca52133186 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json index 20e3d3566fb0..d1236c755981 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json index 20e3d3566fb0..d1236c755981 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-MDzBlwh32rr+eus2Yf1BetIj94m+ULLbewYDulbZjczycs81klNed+qQWG2yi2N03KV5uZlRJJtWdV2x9JNHzQ==", + "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" From 4d5f644621545a21279aaf7da047f0eb05e46cb8 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:33:56 +0000 Subject: [PATCH 034/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 59 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf4ae22c48b2..feebcd64461d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,17 +5,18 @@ **Table of Contents** -- [ (2024-10-09)](#2024-10-09) +- [ (2024-10-23)](#2024-10-23) - [Bug Fixes](#bug-fixes) - [Documentation](#documentation) - [Features](#features) + - [Tests](#tests) - [1.3.0 (2024-09-26)](#130-2024-09-26) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes-1) - [Code Generation](#code-generation) - [Documentation](#documentation-1) - [Features](#features-1) - - [Tests](#tests) + - [Tests](#tests-1) - [Unclassified](#unclassified) - [1.2.0 (2024-06-05)](#120-2024-06-05) - [Breaking Changes](#breaking-changes-1) @@ -23,7 +24,7 @@ - [Code Generation](#code-generation-1) - [Documentation](#documentation-2) - [Features](#features-2) - - [Tests](#tests-1) + - [Tests](#tests-2) - [Unclassified](#unclassified-1) - [1.1.0 (2024-02-20)](#110-2024-02-20) - [Breaking Changes](#breaking-changes-2) @@ -32,14 +33,14 @@ - [Documentation](#documentation-3) - [Features](#features-3) - [Reverts](#reverts) - - [Tests](#tests-2) + - [Tests](#tests-3) - [Unclassified](#unclassified-2) - [1.0.0 (2023-07-12)](#100-2023-07-12) - [Bug Fixes](#bug-fixes-4) - [Code Generation](#code-generation-3) - [Documentation](#documentation-4) - [Features](#features-4) - - [Tests](#tests-3) + - [Tests](#tests-4) - [Unclassified](#unclassified-3) - [0.13.0 (2023-04-18)](#0130-2023-04-18) - [Breaking Changes](#breaking-changes-3) @@ -48,7 +49,7 @@ - [Code Refactoring](#code-refactoring) - [Documentation](#documentation-5) - [Features](#features-5) - - [Tests](#tests-4) + - [Tests](#tests-5) - [Unclassified](#unclassified-4) - [0.11.1 (2023-01-14)](#0111-2023-01-14) - [Breaking Changes](#breaking-changes-4) @@ -56,7 +57,7 @@ - [Code Generation](#code-generation-5) - [Documentation](#documentation-6) - [Features](#features-6) - - [Tests](#tests-5) + - [Tests](#tests-6) - [0.11.0 (2022-12-02)](#0110-2022-12-02) - [Code Generation](#code-generation-6) - [Features](#features-7) @@ -68,7 +69,7 @@ - [Documentation](#documentation-7) - [Features](#features-8) - [Reverts](#reverts-1) - - [Tests](#tests-6) + - [Tests](#tests-7) - [Unclassified](#unclassified-5) - [0.10.1 (2022-06-01)](#0101-2022-06-01) - [Bug Fixes](#bug-fixes-8) @@ -80,7 +81,7 @@ - [Code Refactoring](#code-refactoring-2) - [Documentation](#documentation-8) - [Features](#features-9) - - [Tests](#tests-7) + - [Tests](#tests-8) - [Unclassified](#unclassified-6) - [0.9.0-alpha.3 (2022-03-25)](#090-alpha3-2022-03-25) - [Breaking Changes](#breaking-changes-7) @@ -97,7 +98,7 @@ - [Code Refactoring](#code-refactoring-3) - [Documentation](#documentation-10) - [Features](#features-10) - - [Tests](#tests-8) + - [Tests](#tests-9) - [Unclassified](#unclassified-7) - [0.8.3-alpha.1.pre.0 (2022-01-21)](#083-alpha1pre0-2022-01-21) - [Breaking Changes](#breaking-changes-9) @@ -106,7 +107,7 @@ - [Code Refactoring](#code-refactoring-4) - [Documentation](#documentation-11) - [Features](#features-11) - - [Tests](#tests-9) + - [Tests](#tests-10) - [0.8.2-alpha.1 (2021-12-17)](#082-alpha1-2021-12-17) - [Bug Fixes](#bug-fixes-14) - [Code Generation](#code-generation-14) @@ -116,14 +117,14 @@ - [Code Generation](#code-generation-15) - [Documentation](#documentation-13) - [Features](#features-12) - - [Tests](#tests-10) + - [Tests](#tests-11) - [0.8.0-alpha.4.pre.0 (2021-11-09)](#080-alpha4pre0-2021-11-09) - [Breaking Changes](#breaking-changes-10) - [Bug Fixes](#bug-fixes-16) - [Code Generation](#code-generation-16) - [Documentation](#documentation-14) - [Features](#features-13) - - [Tests](#tests-11) + - [Tests](#tests-12) - [0.8.0-alpha.3 (2021-10-28)](#080-alpha3-2021-10-28) - [Bug Fixes](#bug-fixes-17) - [Code Generation](#code-generation-17) @@ -137,7 +138,7 @@ - [Documentation](#documentation-15) - [Features](#features-14) - [Reverts](#reverts-2) - - [Tests](#tests-12) + - [Tests](#tests-13) - [Unclassified](#unclassified-8) - [0.7.6-alpha.1 (2021-09-12)](#076-alpha1-2021-09-12) - [Code Generation](#code-generation-20) @@ -148,7 +149,7 @@ - [Code Generation](#code-generation-22) - [Documentation](#documentation-16) - [Features](#features-15) - - [Tests](#tests-13) + - [Tests](#tests-14) - [0.7.3-alpha.1 (2021-08-28)](#073-alpha1-2021-08-28) - [Bug Fixes](#bug-fixes-20) - [Code Generation](#code-generation-23) @@ -158,7 +159,7 @@ - [Bug Fixes](#bug-fixes-21) - [Code Generation](#code-generation-24) - [Documentation](#documentation-18) - - [Tests](#tests-14) + - [Tests](#tests-15) - [0.7.0-alpha.1 (2021-07-13)](#070-alpha1-2021-07-13) - [Breaking Changes](#breaking-changes-12) - [Bug Fixes](#bug-fixes-22) @@ -166,7 +167,7 @@ - [Code Refactoring](#code-refactoring-6) - [Documentation](#documentation-19) - [Features](#features-17) - - [Tests](#tests-15) + - [Tests](#tests-16) - [Unclassified](#unclassified-9) - [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17) - [Breaking Changes](#breaking-changes-13) @@ -190,14 +191,14 @@ - [Code Refactoring](#code-refactoring-8) - [Documentation](#documentation-21) - [Features](#features-20) - - [Tests](#tests-16) + - [Tests](#tests-17) - [Unclassified](#unclassified-10) - [0.5.5-alpha.1 (2020-12-09)](#055-alpha1-2020-12-09) - [Bug Fixes](#bug-fixes-26) - [Code Generation](#code-generation-31) - [Documentation](#documentation-22) - [Features](#features-21) - - [Tests](#tests-17) + - [Tests](#tests-18) - [Unclassified](#unclassified-11) - [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11) - [Bug Fixes](#bug-fixes-27) @@ -210,18 +211,18 @@ - [Code Generation](#code-generation-33) - [Documentation](#documentation-24) - [Features](#features-23) - - [Tests](#tests-18) + - [Tests](#tests-19) - [0.5.2-alpha.1 (2020-10-22)](#052-alpha1-2020-10-22) - [Bug Fixes](#bug-fixes-29) - [Code Generation](#code-generation-34) - [Documentation](#documentation-25) - - [Tests](#tests-19) + - [Tests](#tests-20) - [0.5.1-alpha.1 (2020-10-20)](#051-alpha1-2020-10-20) - [Bug Fixes](#bug-fixes-30) - [Code Generation](#code-generation-35) - [Documentation](#documentation-26) - [Features](#features-24) - - [Tests](#tests-20) + - [Tests](#tests-21) - [Unclassified](#unclassified-12) - [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15) - [Breaking Changes](#breaking-changes-15) @@ -230,7 +231,7 @@ - [Code Refactoring](#code-refactoring-10) - [Documentation](#documentation-27) - [Features](#features-25) - - [Tests](#tests-21) + - [Tests](#tests-22) - [Unclassified](#unclassified-13) - [0.4.6-alpha.1 (2020-07-13)](#046-alpha1-2020-07-13) - [Bug Fixes](#bug-fixes-32) @@ -336,10 +337,12 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-09) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-23) ### Bug Fixes +- Duplicate autocomplete trigger + ([6bbf915](https://github.com/ory/kratos/commit/6bbf91593a37e4973a86f610290ebab44df8dc81)) - Explicity set updated_at field when updating identity ([#4131](https://github.com/ory/kratos/issues/4131)) ([66afac1](https://github.com/ory/kratos/commit/66afac173dc08b1d6666b107cf7050a2b0b27774)) @@ -349,6 +352,9 @@ - Registration post persist hooks should not be cancelable ([#4148](https://github.com/ory/kratos/issues/4148)) ([18056a0](https://github.com/ory/kratos/commit/18056a0f1cfdf42769e5a974b2526ccf5c608cc2)) +- **sdk:** Remove incorrect attributes + ([#4163](https://github.com/ory/kratos/issues/4163)) + ([88c68aa](https://github.com/ory/kratos/commit/88c68aa07281a638c9897e76d300d1095b17601d)) - Truncate updated at ([#4149](https://github.com/ory/kratos/issues/4149)) ([2f8aaee](https://github.com/ory/kratos/commit/2f8aaee0716835caaba0dff9b6cc457c2cdff5d4)) @@ -374,6 +380,11 @@ Closes https://github.com/ory/kratos/issues/1570 Closes https://github.com/ory/kratos/issues/3779 +### Tests + +- Update snapshots ([#4167](https://github.com/ory/kratos/issues/4167)) + ([b51f780](https://github.com/ory/kratos/commit/b51f780b7e4abc79a757ac1efe1cb65b3d35c8a4)) + # [1.3.0](https://github.com/ory/kratos/compare/v1.2.0...v1.3.0) (2024-09-26) We are thrilled to announce the release From b4c453b0472f67d0a52b345691f66aa48777a897 Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Mon, 28 Oct 2024 10:13:30 +0100 Subject: [PATCH 035/158] feat: allow listing identities by organization ID (#4115) --- identity/handler.go | 16 ++++++++ identity/handler_test.go | 38 +++++++++++++++++++ identity/pool.go | 1 + internal/client-go/api_identity.go | 8 ++++ internal/httpclient/api_identity.go | 8 ++++ .../sql/identity/persister_identity.go | 5 +++ ...95000000001_organization_id_index.down.sql | 1 + ...00001_organization_id_index.mysql.down.sql | 1 + ...3095000000001_organization_id_index.up.sql | 1 + spec/api.json | 8 ++++ spec/swagger.json | 6 +++ 11 files changed, 93 insertions(+) create mode 100644 persistence/sql/migrations/sql/20240923095000000001_organization_id_index.down.sql create mode 100644 persistence/sql/migrations/sql/20240923095000000001_organization_id_index.mysql.down.sql create mode 100644 persistence/sql/migrations/sql/20240923095000000001_organization_id_index.up.sql diff --git a/identity/handler.go b/identity/handler.go index 58578d56e72c..7560724899db 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -173,6 +173,13 @@ type listIdentitiesParameters struct { // in: query DeclassifyCredentials []string `json:"include_credential"` + // OrganizationID is the organization id to filter identities by. + // + // If `ids` is set, this parameter is ignored. + // required: false + // in: query + OrganizationID string `json:"organization_id"` + crdbx.ConsistencyRequestParameters } @@ -207,6 +214,14 @@ func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Para } } + var orgId uuid.UUID + if orgIdStr := r.URL.Query().Get("organization_id"); orgIdStr != "" { + orgId, err = uuid.FromString(r.URL.Query().Get("organization_id")) + if err != nil { + h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `organization_id`.", r.URL.Query().Get("organization_id")))) + return + } + } var idsFilter []uuid.UUID for _, v := range r.URL.Query()["ids"] { id, err := uuid.FromString(v) @@ -222,6 +237,7 @@ func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Para IdsFilter: idsFilter, CredentialsIdentifier: r.URL.Query().Get("credentials_identifier"), CredentialsIdentifierSimilar: r.URL.Query().Get("preview_credentials_identifier_similar"), + OrganizationID: orgId, ConsistencyLevel: crdbx.ConsistencyLevelFromRequest(r), DeclassifyCredentials: declassify, } diff --git a/identity/handler_test.go b/identity/handler_test.go index 12bbde6cfa80..7f1c5285a8cf 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -1472,6 +1472,44 @@ func TestHandler(t *testing.T) { } }) + t.Run("organizations", func(t *testing.T) { + t.Run("case=should list organization identities", func(t *testing.T) { + for name, ts := range map[string]*httptest.Server{"admin": adminTS} { + t.Run("endpoint="+name, func(t *testing.T) { + orgID := uuid.Must(uuid.NewV4()) + email := x.NewUUID().String() + "@ory.sh" + reg.IdentityManager().Create(ctx, &identity.Identity{ + Traits: identity.Traits(`{"email":"` + email + `"}`), + OrganizationID: uuid.NullUUID{UUID: orgID, Valid: true}, + }) + + res := get(t, ts, "/identities?organization_id="+orgID.String(), http.StatusOK) + assert.Len(t, res.Array(), 1) + assert.EqualValues(t, email, res.Get(`0.traits.email`).String(), "%s", res.Raw) + }) + } + }) + + t.Run("case=malformed organization id should return an error", func(t *testing.T) { + for name, ts := range map[string]*httptest.Server{"admin": adminTS} { + t.Run("endpoint="+name, func(t *testing.T) { + res := get(t, ts, "/identities?organization_id=not-a-uuid", http.StatusBadRequest) + assert.Contains(t, res.Get("error.reason").String(), "Invalid UUID value `not-a-uuid` for parameter `organization_id`.", "%s", res.Raw) + }) + } + }) + + t.Run("case=unknown organization id should return an empty list", func(t *testing.T) { + for name, ts := range map[string]*httptest.Server{"admin": adminTS} { + t.Run("endpoint="+name, func(t *testing.T) { + id := x.NewUUID() + res := get(t, ts, "/identities?organization_id="+id.String(), http.StatusOK) + assert.Len(t, res.Array(), 0) + }) + } + }) + }) + t.Run("case=should list all identities with credentials", func(t *testing.T) { t.Run("include_credential=oidc should include OIDC credentials config", func(t *testing.T) { res := get(t, adminTS, "/identities?include_credential=oidc&credentials_identifier=bar:foo.oidc@bar.com", http.StatusOK) diff --git a/identity/pool.go b/identity/pool.go index e07a6b8ee83e..f57cd87ca475 100644 --- a/identity/pool.go +++ b/identity/pool.go @@ -23,6 +23,7 @@ type ( CredentialsIdentifierSimilar string DeclassifyCredentials []CredentialsType KeySetPagination []keysetpagination.Option + OrganizationID uuid.UUID ConsistencyLevel crdbx.ConsistencyLevel StatementTransformer func(string) string diff --git a/internal/client-go/api_identity.go b/internal/client-go/api_identity.go index 47c2eb6cbfc4..9e4aec1b6c58 100644 --- a/internal/client-go/api_identity.go +++ b/internal/client-go/api_identity.go @@ -2087,6 +2087,7 @@ type IdentityAPIApiListIdentitiesRequest struct { credentialsIdentifier *string previewCredentialsIdentifierSimilar *string includeCredential *[]string + organizationId *string } func (r IdentityAPIApiListIdentitiesRequest) PerPage(perPage int64) IdentityAPIApiListIdentitiesRequest { @@ -2125,6 +2126,10 @@ func (r IdentityAPIApiListIdentitiesRequest) IncludeCredential(includeCredential r.includeCredential = &includeCredential return r } +func (r IdentityAPIApiListIdentitiesRequest) OrganizationId(organizationId string) IdentityAPIApiListIdentitiesRequest { + r.organizationId = &organizationId + return r +} func (r IdentityAPIApiListIdentitiesRequest) Execute() ([]Identity, *http.Response, error) { return r.ApiService.ListIdentitiesExecute(r) @@ -2211,6 +2216,9 @@ func (a *IdentityAPIService) ListIdentitiesExecute(r IdentityAPIApiListIdentitie localVarQueryParams.Add("include_credential", parameterToString(t, "multi")) } } + if r.organizationId != nil { + localVarQueryParams.Add("organization_id", parameterToString(*r.organizationId, "")) + } // to determine the Content-Type header localVarHTTPContentTypes := []string{} diff --git a/internal/httpclient/api_identity.go b/internal/httpclient/api_identity.go index 47c2eb6cbfc4..9e4aec1b6c58 100644 --- a/internal/httpclient/api_identity.go +++ b/internal/httpclient/api_identity.go @@ -2087,6 +2087,7 @@ type IdentityAPIApiListIdentitiesRequest struct { credentialsIdentifier *string previewCredentialsIdentifierSimilar *string includeCredential *[]string + organizationId *string } func (r IdentityAPIApiListIdentitiesRequest) PerPage(perPage int64) IdentityAPIApiListIdentitiesRequest { @@ -2125,6 +2126,10 @@ func (r IdentityAPIApiListIdentitiesRequest) IncludeCredential(includeCredential r.includeCredential = &includeCredential return r } +func (r IdentityAPIApiListIdentitiesRequest) OrganizationId(organizationId string) IdentityAPIApiListIdentitiesRequest { + r.organizationId = &organizationId + return r +} func (r IdentityAPIApiListIdentitiesRequest) Execute() ([]Identity, *http.Response, error) { return r.ApiService.ListIdentitiesExecute(r) @@ -2211,6 +2216,9 @@ func (a *IdentityAPIService) ListIdentitiesExecute(r IdentityAPIApiListIdentitie localVarQueryParams.Add("include_credential", parameterToString(t, "multi")) } } + if r.organizationId != nil { + localVarQueryParams.Add("organization_id", parameterToString(*r.organizationId, "")) + } // to determine the Content-Type header localVarHTTPContentTypes := []string{} diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 3e982730398d..ed46af2924ea 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -977,6 +977,11 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. AND identities.id in (?) ` args = append(args, params.IdsFilter) + } else if !params.OrganizationID.IsNil() { + wheres += ` + AND identities.organization_id = ? + ` + args = append(args, params.OrganizationID.String()) } query := fmt.Sprintf(` diff --git a/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.down.sql b/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.down.sql new file mode 100644 index 000000000000..cca461b9eeab --- /dev/null +++ b/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.down.sql @@ -0,0 +1 @@ +DROP INDEX identities_nid_organization_id_idx; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.mysql.down.sql b/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.mysql.down.sql new file mode 100644 index 000000000000..c2fbff7ff081 --- /dev/null +++ b/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.mysql.down.sql @@ -0,0 +1 @@ +DROP INDEX identities_nid_organization_id_idx ON identities; diff --git a/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.up.sql b/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.up.sql new file mode 100644 index 000000000000..10a84cb55cd7 --- /dev/null +++ b/persistence/sql/migrations/sql/20240923095000000001_organization_id_index.up.sql @@ -0,0 +1 @@ +CREATE INDEX identities_nid_organization_id_idx ON identities (organization_id); diff --git a/spec/api.json b/spec/api.json index 2363d0632564..6fd8dd5cdfc7 100644 --- a/spec/api.json +++ b/spec/api.json @@ -4029,6 +4029,14 @@ }, "type": "array" } + }, + { + "description": "OrganizationID is the organization id to filter identities by.\n\nIf `ids` is set, this parameter is ignored.", + "in": "query", + "name": "organization_id", + "schema": { + "type": "string" + } } ], "responses": { diff --git a/spec/swagger.json b/spec/swagger.json index 3cbbf82b340b..6b57d6a50873 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -261,6 +261,12 @@ "description": "Include Credentials in Response\n\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.", "name": "include_credential", "in": "query" + }, + { + "type": "string", + "description": "OrganizationID is the organization id to filter identities by.\n\nIf `ids` is set, this parameter is ignored.", + "name": "organization_id", + "in": "query" } ], "responses": { From 1a78af052ff446625dda274704c7d107f7390999 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:22:08 +0000 Subject: [PATCH 036/158] autogen(docs): generate and bump docs [skip ci] --- quickstart.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quickstart.yml b/quickstart.yml index 51db8310a494..fc2ac34058f1 100644 --- a/quickstart.yml +++ b/quickstart.yml @@ -1,7 +1,7 @@ version: '3.7' services: kratos-migrate: - image: oryd/kratos:v1.3.0 + image: oryd/kratos:v1.3.1 environment: - DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc volumes: @@ -17,7 +17,7 @@ services: networks: - intranet kratos-selfservice-ui-node: - image: oryd/kratos-selfservice-ui-node:v1.3.0 + image: oryd/kratos-selfservice-ui-node:v1.3.1 environment: - KRATOS_PUBLIC_URL=http://kratos:4433/ - KRATOS_BROWSER_URL=http://127.0.0.1:4433/ @@ -30,7 +30,7 @@ services: kratos: depends_on: - kratos-migrate - image: oryd/kratos:v1.3.0 + image: oryd/kratos:v1.3.1 ports: - '4433:4433' # public - '4434:4434' # admin From 8e29b68a595d2ef18e48c2a01072335cefa36d86 Mon Sep 17 00:00:00 2001 From: Patrik Date: Tue, 29 Oct 2024 10:30:48 +0100 Subject: [PATCH 037/158] fix: account linking should only happen after 2fa when required (#4174) --- selfservice/flow/login/export_test.go | 6 +- selfservice/flow/login/hook.go | 59 +++--- selfservice/flow/login/hook_test.go | 201 +++++++++++++++++---- selfservice/flow/settings/handler.go | 66 +++---- selfservice/strategy/oidc/strategy_test.go | 6 +- session/handler.go | 2 +- session/manager.go | 2 +- session/manager_http.go | 12 +- session/manager_http_test.go | 10 +- 9 files changed, 250 insertions(+), 114 deletions(-) diff --git a/selfservice/flow/login/export_test.go b/selfservice/flow/login/export_test.go index 645bcaf7c000..eafe4a70d179 100644 --- a/selfservice/flow/login/export_test.go +++ b/selfservice/flow/login/export_test.go @@ -4,11 +4,11 @@ package login import ( - "net/http" + "context" "github.com/ory/kratos/session" ) -func RequiresAAL2ForTest(e HookExecutor, r *http.Request, s *session.Session) (bool, error) { - return e.requiresAAL2(r, s, nil) // *login.Flow is nil to avoid an import cycle +func CheckAALForTest(ctx context.Context, e *HookExecutor, s *session.Session, flow *Flow) error { + return e.checkAAL(ctx, s, flow) } diff --git a/selfservice/flow/login/hook.go b/selfservice/flow/login/hook.go index be25529241f0..402aacef665b 100644 --- a/selfservice/flow/login/hook.go +++ b/selfservice/flow/login/hook.go @@ -12,7 +12,6 @@ import ( "github.com/gofrs/uuid" "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/driver/config" "github.com/ory/kratos/hydra" @@ -81,19 +80,20 @@ func NewHookExecutor(d executorDependencies) *HookExecutor { return &HookExecutor{d: d} } -func (e *HookExecutor) requiresAAL2(r *http.Request, s *session.Session, a *Flow) (bool, error) { - err := e.d.SessionManager().DoesSessionSatisfy(r, s, e.d.Config().SessionWhoAmIAAL(r.Context())) +func (e *HookExecutor) checkAAL(ctx context.Context, s *session.Session, a *Flow) error { + err := e.d.SessionManager().DoesSessionSatisfy(ctx, s, e.d.Config().SessionWhoAmIAAL(ctx)) + if err == nil { + return nil + } if aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) { - if aalErr.PassReturnToAndLoginChallengeParameters(a.RequestURL) != nil { + if a != nil && aalErr.PassReturnToAndLoginChallengeParameters(a.RequestURL) != nil { _ = aalErr.WithDetail("pass_request_params_error", "failed to pass request parameters to aalErr.RedirectTo") } - return true, aalErr - } else if err != nil { - return true, errors.WithStack(err) + return aalErr } - return false, nil + return err } func (e *HookExecutor) handleLoginError(_ http.ResponseWriter, r *http.Request, g node.UiNodeGroup, f *Flow, i *identity.Identity, flowError error) error { @@ -133,7 +133,11 @@ func (e *HookExecutor) PostLoginHook( r = r.WithContext(ctx) defer otelx.End(span, &err) - if err := e.maybeLinkCredentials(r.Context(), s, i, f); err != nil { + // We need to set the identity here because we check the available AAL in maybeLinkCredentials. + s.IdentityID = i.ID + s.Identity = i + + if err := e.maybeLinkCredentials(ctx, s, i, f); err != nil { return err } @@ -144,12 +148,12 @@ func (e *HookExecutor) PostLoginHook( c := e.d.Config() // Verify the redirect URL before we do any other processing. returnTo, err := x.SecureRedirectTo(r, - c.SelfServiceBrowserDefaultReturnTo(r.Context()), + c.SelfServiceBrowserDefaultReturnTo(ctx), x.SecureRedirectReturnTo(f.ReturnTo), x.SecureRedirectUseSourceURL(f.RequestURL), - x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(r.Context())), - x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(r.Context())), - x.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowLoginReturnTo(r.Context(), f.Active.String())), + x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)), + x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)), + x.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowLoginReturnTo(ctx, f.Active.String())), ) if err != nil { return err @@ -172,14 +176,14 @@ func (e *HookExecutor) PostLoginHook( WithField("identity_id", i.ID). WithField("flow_method", f.Active). Debug("Running ExecuteLoginPostHook.") - for k, executor := range e.d.PostLoginHooks(r.Context(), f.Active) { + for k, executor := range e.d.PostLoginHooks(ctx, f.Active) { if err := executor.ExecuteLoginPostHook(w, r, g, f, s); err != nil { if errors.Is(err, ErrHookAbortFlow) { e.d.Logger(). WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", PostHookExecutorNames(e.d.PostLoginHooks(r.Context(), f.Active))). + WithField("executors", PostHookExecutorNames(e.d.PostLoginHooks(ctx, f.Active))). WithField("identity_id", i.ID). WithField("flow_method", f.Active). Debug("A ExecuteLoginPostHook hook aborted early.") @@ -195,7 +199,7 @@ func (e *HookExecutor) PostLoginHook( WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", PostHookExecutorNames(e.d.PostLoginHooks(r.Context(), f.Active))). + WithField("executors", PostHookExecutorNames(e.d.PostLoginHooks(ctx, f.Active))). WithField("identity_id", i.ID). WithField("flow_method", f.Active). Debug("ExecuteLoginPostHook completed successfully.") @@ -203,7 +207,7 @@ func (e *HookExecutor) PostLoginHook( if f.Type == flow.TypeAPI { span.SetAttributes(attribute.String("flow_type", string(flow.TypeAPI))) - if err := e.d.SessionPersister().UpsertSession(r.Context(), s); err != nil { + if err := e.d.SessionPersister().UpsertSession(ctx, s); err != nil { return errors.WithStack(err) } e.d.Audit(). @@ -212,7 +216,7 @@ func (e *HookExecutor) PostLoginHook( WithField("identity_id", i.ID). Info("Identity authenticated successfully and was issued an Ory Kratos Session Token.") - span.AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{ + span.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{ SessionID: s.ID, IdentityID: i.ID, FlowType: string(f.Type), @@ -235,7 +239,7 @@ func (e *HookExecutor) PostLoginHook( Token: s.Token, ContinueWith: f.ContinueWith(), } - if required, _ := e.requiresAAL2(r, classified, f); required { + if e.checkAAL(ctx, classified, f) != nil { // If AAL is not satisfied, we omit the identity to preserve the user's privacy in case of a phishing attack. response.Session.Identity = nil } @@ -244,7 +248,7 @@ func (e *HookExecutor) PostLoginHook( return nil } - if err := e.d.SessionManager().UpsertAndIssueCookie(r.Context(), w, r, s); err != nil { + if err := e.d.SessionManager().UpsertAndIssueCookie(ctx, w, r, s); err != nil { return errors.WithStack(err) } @@ -254,7 +258,7 @@ func (e *HookExecutor) PostLoginHook( WithField("session_id", s.ID). Info("Identity authenticated successfully and was issued an Ory Kratos Session Cookie.") - trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{ + span.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{ SessionID: s.ID, IdentityID: i.ID, FlowType: string(f.Type), RequestedAAL: string(f.RequestedAAL), IsRefresh: f.Refresh, Method: f.Active.String(), SSOProvider: provider, @@ -267,7 +271,7 @@ func (e *HookExecutor) PostLoginHook( s.Token = "" // If we detect that whoami would require a higher AAL, we redirect! - if _, err := e.requiresAAL2(r, classified, f); err != nil { + if err := e.checkAAL(ctx, classified, f); err != nil { if aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) { span.SetAttributes(attribute.String("return_to", aalErr.RedirectTo), attribute.String("redirect_reason", "requires aal2")) e.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(aalErr.RedirectTo)) @@ -279,7 +283,7 @@ func (e *HookExecutor) PostLoginHook( // If Kratos is used as a Hydra login provider, we need to redirect back to Hydra by returning a 422 status // with the post login challenge URL as the body. if f.OAuth2LoginChallenge != "" { - postChallengeURL, err := e.d.Hydra().AcceptLoginRequest(r.Context(), + postChallengeURL, err := e.d.Hydra().AcceptLoginRequest(ctx, hydra.AcceptLoginRequestParams{ LoginChallenge: string(f.OAuth2LoginChallenge), IdentityID: i.ID.String(), @@ -303,7 +307,7 @@ func (e *HookExecutor) PostLoginHook( } // If we detect that whoami would require a higher AAL, we redirect! - if _, err := e.requiresAAL2(r, classified, f); err != nil { + if err := e.checkAAL(ctx, classified, f); err != nil { if aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) { http.Redirect(w, r, aalErr.RedirectTo, http.StatusSeeOther) return nil @@ -313,7 +317,7 @@ func (e *HookExecutor) PostLoginHook( finalReturnTo := returnTo.String() if f.OAuth2LoginChallenge != "" { - rt, err := e.d.Hydra().AcceptLoginRequest(r.Context(), + rt, err := e.d.Hydra().AcceptLoginRequest(ctx, hydra.AcceptLoginRequestParams{ LoginChallenge: string(f.OAuth2LoginChallenge), IdentityID: i.ID.String(), @@ -346,6 +350,11 @@ func (e *HookExecutor) PreLoginHook(w http.ResponseWriter, r *http.Request, a *F // maybeLinkCredentials links the identity with the credentials of the inner context of the login flow. func (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.Session, ident *identity.Identity, loginFlow *Flow) error { + if e.checkAAL(ctx, sess, loginFlow) != nil { + // we don't yet want to link credentials because the required AAL is not satisfied + return nil + } + lc, err := flow.DuplicateCredentials(loginFlow) if err != nil { return err diff --git a/selfservice/flow/login/hook_test.go b/selfservice/flow/login/hook_test.go index 7d7f8e158174..160373a47955 100644 --- a/selfservice/flow/login/hook_test.go +++ b/selfservice/flow/login/hook_test.go @@ -5,28 +5,28 @@ package login_test import ( "context" + "database/sql" "net/http" + "net/http/httptest" "net/url" "testing" "time" - "github.com/stretchr/testify/require" - - "github.com/gobuffalo/httptest" "github.com/julienschmidt/httprouter" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tidwall/gjson" - "github.com/ory/kratos/hydra" - "github.com/ory/kratos/schema" - "github.com/ory/kratos/session" - "github.com/ory/kratos/driver/config" + confighelpers "github.com/ory/kratos/driver/config/testhelpers" + "github.com/ory/kratos/hydra" "github.com/ory/kratos/identity" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" + "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" + "github.com/ory/kratos/session" "github.com/ory/kratos/x" ) @@ -35,8 +35,6 @@ func TestLoginExecutor(t *testing.T) { ctx := context.Background() for _, strategy := range identity.AllCredentialTypes { - strategy := strategy - t.Run("strategy="+strategy.String(), func(t *testing.T) { t.Parallel() @@ -75,6 +73,26 @@ func TestLoginExecutor(t *testing.T) { reg.LoginHookExecutor().PostLoginHook(w, r, strategy.ToUiNodeGroup(), loginFlow, useIdentity, sess, "")) }) + router.GET("/login/post2fa", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + loginFlow, err := login.NewFlow(conf, time.Minute, "", r, ft) + require.NoError(t, err) + loginFlow.Active = strategy + loginFlow.RequestURL = x.RequestURL(r).String() + for _, cb := range flowCallback { + cb(loginFlow) + } + + sess := session.NewInactiveSession() + sess.CompletedLoginFor(identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1) + sess.CompletedLoginFor(identity.CredentialsTypeTOTP, identity.AuthenticatorAssuranceLevel2) + if useIdentity == nil { + useIdentity = testhelpers.SelfServiceHookCreateFakeIdentity(t, reg) + } + + testhelpers.SelfServiceHookLoginErrorHandler(t, w, r, + reg.LoginHookExecutor().PostLoginHook(w, r, strategy.ToUiNodeGroup(), loginFlow, useIdentity, sess, "")) + }) + ts := httptest.NewServer(router) t.Cleanup(ts.Close) conf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL) @@ -98,10 +116,9 @@ func TestLoginExecutor(t *testing.T) { ts := newServer(t, flow.TypeBrowser, nil) res, body := makeRequestPost(t, ts, true, url.Values{}) - assert.EqualValues(t, http.StatusOK, res.StatusCode) + require.Equal(t, http.StatusOK, res.StatusCode) assert.Contains(t, res.Request.URL.String(), ts.URL) assert.EqualValues(t, gjson.Get(body, "continue_with").Raw, `[{"action":"redirect_browser_to","redirect_browser_to":"https://www.ory.sh/"}]`) - t.Logf("%s", body) }) t.Run("case=pass if hooks pass", func(t *testing.T) { @@ -109,17 +126,19 @@ func TestLoginExecutor(t *testing.T) { viperSetPost(t, conf, strategy.String(), []config.SelfServiceHook{{Name: "err", Config: []byte(`{}`)}}) res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{}) - assert.EqualValues(t, http.StatusOK, res.StatusCode) - assert.EqualValues(t, "https://www.ory.sh/", res.Request.URL.String()) + require.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, "https://www.ory.sh/", res.Request.URL.String()) }) t.Run("case=fail if hooks fail", func(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf)) viperSetPost(t, conf, strategy.String(), []config.SelfServiceHook{{Name: "err", Config: []byte(`{"ExecuteLoginPostHook": "abort"}`)}}) - res, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{}) - assert.EqualValues(t, http.StatusOK, res.StatusCode) - assert.Equal(t, "", body) + ts := newServer(t, flow.TypeBrowser, nil) + res, body := makeRequestPost(t, ts, false, url.Values{}) + require.Equal(t, http.StatusOK, res.StatusCode) + assert.Contains(t, res.Request.URL.String(), ts.URL) + assert.Empty(t, body) }) t.Run("case=use return_to value", func(t *testing.T) { @@ -127,8 +146,8 @@ func TestLoginExecutor(t *testing.T) { conf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{"https://www.ory.sh/"}) res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), 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()) + require.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, "https://www.ory.sh/kratos/", res.Request.URL.String()) }) t.Run("case=use nested config value", func(t *testing.T) { @@ -300,46 +319,100 @@ func TestLoginExecutor(t *testing.T) { t.Run("case=maybe links credential", func(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf)) + conf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL) - email := testhelpers.RandomEmail() - useIdentity := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{ + email1, email2 := testhelpers.RandomEmail(), testhelpers.RandomEmail() + passwordOnlyIdentity := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{ identity.CredentialsTypePassword: { Type: identity.CredentialsTypePassword, Config: []byte(`{"hashed_password": "$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw"}`), - Identifiers: []string{email}, + Identifiers: []string{email1}, }, }} - require.NoError(t, reg.Persister().CreateIdentity(context.Background(), useIdentity)) + twoFAIdentitiy := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{ + identity.CredentialsTypePassword: { + Type: identity.CredentialsTypePassword, + Config: []byte(`{"hashed_password": "$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw"}`), + Identifiers: []string{email2}, + }, + identity.CredentialsTypeTOTP: { + Type: identity.CredentialsTypeTOTP, + Config: []byte(`{"totp_url":"otpauth://totp/test"}`), + Identifiers: []string{email2}, + }, + }} + require.NoError(t, reg.Persister().CreateIdentity(ctx, passwordOnlyIdentity)) + require.NoError(t, reg.Persister().CreateIdentity(ctx, twoFAIdentitiy)) - credsOIDC, err := identity.NewCredentialsOIDC( + credsOIDCPWOnly, err := identity.NewCredentialsOIDC( + &identity.CredentialsOIDCEncryptedTokens{IDToken: "id-token", AccessToken: "access-token", RefreshToken: "refresh-token"}, + "my-provider", + email1, + "", + ) + require.NoError(t, err) + credsOIDC2FA, err := identity.NewCredentialsOIDC( &identity.CredentialsOIDCEncryptedTokens{IDToken: "id-token", AccessToken: "access-token", RefreshToken: "refresh-token"}, "my-provider", - email, + email2, "", ) require.NoError(t, err) + t.Run("sub-case=does not link after first factor when second factor is available", func(t *testing.T) { + ts := newServer(t, flow.TypeBrowser, twoFAIdentitiy, func(l *login.Flow) { + require.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{ + CredentialsType: identity.CredentialsTypeOIDC, + CredentialsConfig: credsOIDC2FA.Config, + DuplicateIdentifier: email2, + })) + }) + res, body := makeRequestPost(t, ts, false, url.Values{}) + assert.Equal(t, res.Request.URL.String(), ts.URL+login.RouteInitBrowserFlow+"?aal=aal2", "%s", body) + + ident, err := reg.Persister().GetIdentity(ctx, twoFAIdentitiy.ID, identity.ExpandCredentials) + require.NoError(t, err) + assert.Len(t, ident.Credentials, 2) + }) + + t.Run("sub-case=links after second factor when second factor is available", func(t *testing.T) { + ts := newServer(t, flow.TypeBrowser, twoFAIdentitiy, func(l *login.Flow) { + require.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{ + CredentialsType: identity.CredentialsTypeOIDC, + CredentialsConfig: credsOIDC2FA.Config, + DuplicateIdentifier: email2, + })) + }) + res, body := testhelpers.SelfServiceMakeHookRequest(t, ts, "/login/post2fa", false, url.Values{}) + assert.Equalf(t, http.StatusOK, res.StatusCode, "%s", body) + assert.Equalf(t, "https://www.ory.sh/", res.Request.URL.String(), "%s", body) + + ident, err := reg.Persister().GetIdentity(ctx, twoFAIdentitiy.ID, identity.ExpandCredentials) + require.NoError(t, err) + assert.Len(t, ident.Credentials, 3) + }) + t.Run("sub-case=links matching identity", func(t *testing.T) { - res, _ := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity, func(l *login.Flow) { + res, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, passwordOnlyIdentity, func(l *login.Flow) { require.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{ CredentialsType: identity.CredentialsTypeOIDC, - CredentialsConfig: credsOIDC.Config, - DuplicateIdentifier: email, + CredentialsConfig: credsOIDCPWOnly.Config, + DuplicateIdentifier: email1, })) }), false, url.Values{}) - assert.EqualValues(t, http.StatusOK, res.StatusCode) - assert.EqualValues(t, "https://www.ory.sh/", res.Request.URL.String()) + assert.Equalf(t, http.StatusOK, res.StatusCode, "%s", body) + assert.Equalf(t, "https://www.ory.sh/", res.Request.URL.String(), "%s", body) - ident, err := reg.Persister().GetIdentity(ctx, useIdentity.ID, identity.ExpandCredentials) + ident, err := reg.Persister().GetIdentity(ctx, passwordOnlyIdentity.ID, identity.ExpandCredentials) require.NoError(t, err) - assert.Equal(t, 2, len(ident.Credentials)) + assert.Len(t, ident.Credentials, 2) }) t.Run("sub-case=errors on non-matching identity", func(t *testing.T) { - res, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity, func(l *login.Flow) { + res, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, passwordOnlyIdentity, func(l *login.Flow) { require.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{ CredentialsType: identity.CredentialsTypeOIDC, - CredentialsConfig: credsOIDC.Config, + CredentialsConfig: credsOIDCPWOnly.Config, DuplicateIdentifier: "wrong@example.com", })) }), false, url.Values{}) @@ -369,12 +442,62 @@ func TestLoginExecutor(t *testing.T) { conf, )) }) - - t.Run("requiresAAL2 should return true if there's an error", func(t *testing.T) { - requiresAAL2, err := login.RequiresAAL2ForTest(*reg.LoginHookExecutor(), &http.Request{}, &session.Session{}) - require.NotNil(t, err) - require.True(t, requiresAAL2) - }) }) } + + t.Run("method=checkAAL", func(t *testing.T) { + ctx := confighelpers.WithConfigValue(ctx, config.ViperKeyPublicBaseURL, "https://www.ory.sh/") + + conf, reg := internal.NewFastRegistryWithMocks(t) + testhelpers.SetDefaultIdentitySchema(conf, "file://./stub/login.schema.json") + conf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, "https://www.ory.sh/") + + t.Run("returns no error when sufficient", func(t *testing.T) { + ctx := confighelpers.WithConfigValue(ctx, config.ViperKeySessionWhoAmIAAL, identity.AuthenticatorAssuranceLevel1) + assert.NoError(t, + login.CheckAALForTest(ctx, reg.LoginHookExecutor(), &session.Session{ + AMR: session.AuthenticationMethods{{ + Method: identity.CredentialsTypePassword, + AAL: identity.AuthenticatorAssuranceLevel1, + }}, + AuthenticatorAssuranceLevel: identity.AuthenticatorAssuranceLevel1, + }, nil), + ) + + ctx = confighelpers.WithConfigValue(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL) + assert.NoError(t, + login.CheckAALForTest(ctx, reg.LoginHookExecutor(), &session.Session{ + AMR: session.AuthenticationMethods{{ + Method: identity.CredentialsTypePassword, + AAL: identity.AuthenticatorAssuranceLevel1, + }, { + Method: identity.CredentialsTypeLookup, + AAL: identity.AuthenticatorAssuranceLevel2, + }}, + AuthenticatorAssuranceLevel: identity.AuthenticatorAssuranceLevel2, + }, nil), + ) + }) + + t.Run("copies parameters to redirect URL when AAL is not sufficient", func(t *testing.T) { + ctx := confighelpers.WithConfigValue(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL) + aalErr := new(session.ErrAALNotSatisfied) + require.ErrorAs(t, + login.CheckAALForTest(ctx, reg.LoginHookExecutor(), &session.Session{ + AMR: session.AuthenticationMethods{{ + Method: identity.CredentialsTypePassword, + AAL: identity.AuthenticatorAssuranceLevel1, + }}, + AuthenticatorAssuranceLevel: identity.AuthenticatorAssuranceLevel1, + Identity: &identity.Identity{ + InternalAvailableAAL: identity.NullableAuthenticatorAssuranceLevel{sql.NullString{String: string(identity.AuthenticatorAssuranceLevel2), Valid: true}}, + }, + }, &login.Flow{ + RequestURL: "https://www.ory.sh/?return_to=https://www.ory.sh/kratos&login_challenge=challenge", + }), + &aalErr, + ) + assert.Equal(t, "https://www.ory.sh/self-service/login/browser?aal=aal2&login_challenge=challenge&return_to=https%3A%2F%2Fwww.ory.sh%2Fkratos", aalErr.RedirectTo) + }) + }) } diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go index efc1e5a84bc3..6232f47b7a9b 100644 --- a/selfservice/flow/settings/handler.go +++ b/selfservice/flow/settings/handler.go @@ -213,13 +213,14 @@ type createNativeSettingsFlow struct { // 400: errorGeneric // default: errorGeneric func (h *Handler) createNativeSettingsFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - s, err := h.d.SessionManager().FetchFromRequest(r.Context(), r) + ctx := r.Context() + s, err := h.d.SessionManager().FetchFromRequest(ctx, r) if err != nil { h.d.Writer().WriteError(w, r, err) return } - if err := h.d.SessionManager().DoesSessionSatisfy(r, s, h.d.Config().SelfServiceSettingsRequiredAAL(r.Context())); err != nil { + if err := h.d.SessionManager().DoesSessionSatisfy(ctx, s, h.d.Config().SelfServiceSettingsRequiredAAL(ctx)); err != nil { h.d.Writer().WriteError(w, r, err) return } @@ -295,10 +296,11 @@ type createBrowserSettingsFlow struct { // 401: errorGeneric // 403: errorGeneric // default: errorGeneric -func (h *Handler) createBrowserSettingsFlow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - s, err := h.d.SessionManager().FetchFromRequest(r.Context(), r) +func (h *Handler) createBrowserSettingsFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + ctx := r.Context() + s, err := h.d.SessionManager().FetchFromRequest(ctx, r) if err != nil { - h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + h.d.SelfServiceErrorManager().Forward(ctx, w, r, err) return } @@ -308,18 +310,18 @@ func (h *Handler) createBrowserSettingsFlow(w http.ResponseWriter, r *http.Reque managerOptions = append(managerOptions, session.WithRequestURL(requestURL.String())) } - if err := h.d.SessionManager().DoesSessionSatisfy(r, s, h.d.Config().SelfServiceSettingsRequiredAAL(r.Context()), managerOptions...); err != nil { + if err := h.d.SessionManager().DoesSessionSatisfy(ctx, s, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), managerOptions...); err != nil { h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, nil, nil, err) return } f, err := h.NewFlow(w, r, s.Identity, flow.TypeBrowser) if err != nil { - h.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + h.d.SelfServiceErrorManager().Forward(ctx, w, r, err) return } - redirTo := f.AppendTo(h.d.Config().SelfServiceFlowSettingsUI(r.Context())).String() + redirTo := f.AppendTo(h.d.Config().SelfServiceFlowSettingsUI(ctx)).String() x.AcceptToRedirectOrJSON(w, r, h.d.Writer(), f, redirTo) } @@ -393,55 +395,54 @@ type getSettingsFlow struct { // 404: errorGeneric // 410: errorGeneric // default: errorGeneric -func (h *Handler) getSettingsFlow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if err := h.fetchFlow(w, r); err != nil { - h.d.Writer().WriteError(w, r, err) - return - } -} - -func (h *Handler) fetchFlow(w http.ResponseWriter, r *http.Request) error { +func (h *Handler) getSettingsFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + ctx := r.Context() rid := x.ParseUUID(r.URL.Query().Get("id")) - pr, err := h.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), rid) + pr, err := h.d.SettingsFlowPersister().GetSettingsFlow(ctx, rid) if err != nil { - return err + h.d.Writer().WriteError(w, r, err) + return } - sess, err := h.d.SessionManager().FetchFromRequest(r.Context(), r) + sess, err := h.d.SessionManager().FetchFromRequest(ctx, r) if err != nil { - return err + h.d.Writer().WriteError(w, r, err) + return } if pr.IdentityID != sess.Identity.ID { - return errors.WithStack(herodot.ErrForbidden.WithID(text.ErrIDInitiatedBySomeoneElse).WithReasonf("The request was made for another identity and has been blocked for security reasons.")) + h.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrForbidden. + WithID(text.ErrIDInitiatedBySomeoneElse). + WithReasonf("The request was made for another identity and has been blocked for security reasons."))) + return } // we cannot redirect back to the request URL (/self-service/settings/flows?id=...) since it would just redirect // to a page displaying raw JSON to the client (browser), which is not what we want. // Let's rather carry over the flow ID as a query parameter and redirect to the settings UI URL. - requestURL := urlx.CopyWithQuery(h.d.Config().SelfServiceFlowSettingsUI(r.Context()), url.Values{"flow": {rid.String()}}) - if err := h.d.SessionManager().DoesSessionSatisfy(r, sess, h.d.Config().SelfServiceSettingsRequiredAAL(r.Context()), session.WithRequestURL(requestURL.String())); err != nil { - return err + requestURL := urlx.CopyWithQuery(h.d.Config().SelfServiceFlowSettingsUI(ctx), url.Values{"flow": {rid.String()}}) + if err := h.d.SessionManager().DoesSessionSatisfy(ctx, sess, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), session.WithRequestURL(requestURL.String())); err != nil { + h.d.Writer().WriteError(w, r, err) + return } if pr.ExpiresAt.Before(time.Now().UTC()) { if pr.Type == flow.TypeBrowser { - redirectURL := flow.GetFlowExpiredRedirectURL(r.Context(), h.d.Config(), RouteInitBrowserFlow, pr.ReturnTo) + redirectURL := flow.GetFlowExpiredRedirectURL(ctx, h.d.Config(), RouteInitBrowserFlow, pr.ReturnTo) h.d.Writer().WriteError(w, r, errors.WithStack(x.ErrGone. WithReason("The settings flow has expired. Redirect the user to the settings flow init endpoint to initialize a new settings flow."). WithDetail("redirect_to", redirectURL.String()). WithDetail("return_to", pr.ReturnTo))) - return nil + return } 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.d.Config().SelfPublicURL(r.Context()), RouteInitAPIFlow).String()))) - return nil + WithDetail("api", urlx.AppendPaths(h.d.Config().SelfPublicURL(ctx), RouteInitAPIFlow).String()))) + return } h.d.Writer().Write(w, r, pr) - return nil } // Update Settings Flow Parameters @@ -557,13 +558,14 @@ type updateSettingsFlowBody struct{} // 422: errorBrowserLocationChangeRequired // default: errorGeneric func (h *Handler) updateSettingsFlow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + ctx := r.Context() rid, err := GetFlowID(r) if err != nil { h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, nil, nil, err) return } - f, err := h.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), rid) + f, err := h.d.SettingsFlowPersister().GetSettingsFlow(ctx, rid) if errors.Is(err, sqlcon.ErrNoRows) { h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, nil, nil, errors.WithStack(herodot.ErrNotFound.WithReasonf("The settings request could not be found. Please restart the flow."))) return @@ -572,14 +574,14 @@ func (h *Handler) updateSettingsFlow(w http.ResponseWriter, r *http.Request, ps return } - ss, err := h.d.SessionManager().FetchFromRequest(r.Context(), r) + ss, err := h.d.SessionManager().FetchFromRequest(ctx, r) if err != nil { h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, nil, err) return } requestURL := x.RequestURL(r).String() - if err := h.d.SessionManager().DoesSessionSatisfy(r, ss, h.d.Config().SelfServiceSettingsRequiredAAL(r.Context()), session.WithRequestURL(requestURL)); err != nil { + if err := h.d.SessionManager().DoesSessionSatisfy(ctx, ss, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), session.WithRequestURL(requestURL)); err != nil { h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, nil, err) return } diff --git a/selfservice/strategy/oidc/strategy_test.go b/selfservice/strategy/oidc/strategy_test.go index 25faf0e91a55..f019e0256129 100644 --- a/selfservice/strategy/oidc/strategy_test.go +++ b/selfservice/strategy/oidc/strategy_test.go @@ -1589,9 +1589,9 @@ func TestStrategy(t *testing.T) { body, err := io.ReadAll(res.Body) require.NoError(t, res.Body.Close()) require.NoError(t, err) - assert.Equal(t, - strconv.Itoa(int(text.ErrorValidationLoginLinkedCredentialsDoNotMatch)), - gjson.GetBytes(body, "ui.messages.0.id").String(), + assert.EqualValues(t, + text.ErrorValidationLoginLinkedCredentialsDoNotMatch, + gjson.GetBytes(body, "ui.messages.0.id").Int(), prettyJSON(t, body), ) }) diff --git a/session/handler.go b/session/handler.go index 84be9c25e1b8..1699abd766bc 100644 --- a/session/handler.go +++ b/session/handler.go @@ -226,7 +226,7 @@ func (h *Handler) whoami(w http.ResponseWriter, r *http.Request, _ httprouter.Pa } var aalErr *ErrAALNotSatisfied - if err := h.r.SessionManager().DoesSessionSatisfy(r, s, c.SessionWhoAmIAAL(ctx), + if err := h.r.SessionManager().DoesSessionSatisfy(ctx, s, c.SessionWhoAmIAAL(ctx), // For the time being we want to update the AAL in the database if it is unset. UpsertAAL, ); errors.As(err, &aalErr) { diff --git a/session/manager.go b/session/manager.go index 596d0ab7da97..e44d91f9111f 100644 --- a/session/manager.go +++ b/session/manager.go @@ -146,7 +146,7 @@ type Manager interface { // This method is implemented in such a way, that if a second factor is found for the user, it is always assumed // that the user is able to authenticate with it. This means that if a user has a second factor, the user is always // asked to authenticate with it if `highest_available` is set and the session's AAL is `aal1`. - DoesSessionSatisfy(r *http.Request, sess *Session, matcher string, opts ...ManagerOptions) error + DoesSessionSatisfy(ctx context.Context, sess *Session, matcher string, opts ...ManagerOptions) error // SessionAddAuthenticationMethods adds one or more authentication method to the session. SessionAddAuthenticationMethods(ctx context.Context, sid uuid.UUID, methods ...AuthenticationMethod) error diff --git a/session/manager_http.go b/session/manager_http.go index cf68e7289737..b56a16be88ee 100644 --- a/session/manager_http.go +++ b/session/manager_http.go @@ -284,13 +284,14 @@ func (s *ManagerHTTP) PurgeFromRequest(ctx context.Context, w http.ResponseWrite return nil } -func (s *ManagerHTTP) DoesSessionSatisfy(r *http.Request, sess *Session, requestedAAL string, opts ...ManagerOptions) (err error) { - ctx, span := s.r.Tracer(r.Context()).Tracer().Start(r.Context(), "sessions.ManagerHTTP.DoesSessionSatisfy") +func (s *ManagerHTTP) DoesSessionSatisfy(ctx context.Context, sess *Session, requestedAAL string, opts ...ManagerOptions) (err error) { + ctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, "sessions.ManagerHTTP.DoesSessionSatisfy") defer otelx.End(span, &err) - // If we already have AAL2 there is no need to check further because it is the highest AAL. sess.SetAuthenticatorAssuranceLevel() - if sess.AuthenticatorAssuranceLevel > identity.AuthenticatorAssuranceLevel1 { + + // If we already have AAL2 there is no need to check further because it is the highest AAL. + if sess.AuthenticatorAssuranceLevel == identity.AuthenticatorAssuranceLevel2 { return nil } @@ -311,8 +312,9 @@ func (s *ManagerHTTP) DoesSessionSatisfy(r *http.Request, sess *Session, request if sess.AuthenticatorAssuranceLevel >= identity.AuthenticatorAssuranceLevel1 { return nil } + return NewErrAALNotSatisfied(loginURL.String()) case config.HighestAvailableAAL: - if sess.AuthenticatorAssuranceLevel >= identity.AuthenticatorAssuranceLevel2 { + if sess.AuthenticatorAssuranceLevel == identity.AuthenticatorAssuranceLevel2 { // The session has AAL2, nothing to check. return nil } diff --git a/session/manager_http_test.go b/session/manager_http_test.go index 2a6eac16b5b3..a2ca87893075 100644 --- a/session/manager_http_test.go +++ b/session/manager_http_test.go @@ -408,7 +408,7 @@ func TestManagerHTTP(t *testing.T) { s.CompletedLoginFor(m, "") } require.NoError(t, reg.SessionManager().ActivateSession(req, s, i, time.Now().UTC())) - err := reg.SessionManager().DoesSessionSatisfy((&http.Request{}).WithContext(context.Background()), s, requested) + err := reg.SessionManager().DoesSessionSatisfy(ctx, s, requested) if expectedError != nil { require.ErrorAs(t, err, &expectedError) } else { @@ -455,7 +455,7 @@ func TestManagerHTTP(t *testing.T) { s := session.NewInactiveSession() s.CompletedLoginFor(identity.CredentialsTypePassword, "") require.NoError(t, reg.SessionManager().ActivateSession(req, s, idAAL1, time.Now().UTC())) - require.Error(t, reg.SessionManager().DoesSessionSatisfy((&http.Request{}).WithContext(context.Background()), s, config.HighestAvailableAAL, session.UpsertAAL)) + require.Error(t, reg.SessionManager().DoesSessionSatisfy(ctx, s, config.HighestAvailableAAL, session.UpsertAAL)) result, err := reg.IdentityPool().GetIdentity(context.Background(), idAAL1.ID, identity.ExpandNothing) require.NoError(t, err) @@ -858,7 +858,7 @@ func TestDoesSessionSatisfy(t *testing.T) { } require.NoError(t, reg.SessionManager().ActivateSession(req, s, id, time.Now().UTC())) - err := reg.SessionManager().DoesSessionSatisfy((&http.Request{}).WithContext(ctx), s, string(tc.matcher), tc.sessionManagerOptions...) + err := reg.SessionManager().DoesSessionSatisfy(ctx, s, string(tc.matcher), tc.sessionManagerOptions...) if tc.errAs != nil || tc.errIs != nil { if tc.expectedFunc != nil { tc.expectedFunc(t, err, tc.errAs) @@ -875,7 +875,7 @@ func TestDoesSessionSatisfy(t *testing.T) { // This should still work even if the session does not have identity data attached yet ... s.Identity = nil - err = reg.SessionManager().DoesSessionSatisfy((&http.Request{}).WithContext(ctx), s, string(tc.matcher), tc.sessionManagerOptions...) + err = reg.SessionManager().DoesSessionSatisfy(ctx, s, string(tc.matcher), tc.sessionManagerOptions...) if tc.errAs != nil { if tc.expectedFunc != nil { tc.expectedFunc(t, err, tc.errAs) @@ -888,7 +888,7 @@ func TestDoesSessionSatisfy(t *testing.T) { // ... or no credentials attached. s.Identity = id s.Identity.Credentials = nil - err = reg.SessionManager().DoesSessionSatisfy((&http.Request{}).WithContext(ctx), s, string(tc.matcher), tc.sessionManagerOptions...) + err = reg.SessionManager().DoesSessionSatisfy(ctx, s, string(tc.matcher), tc.sessionManagerOptions...) if tc.errAs != nil { if tc.expectedFunc != nil { tc.expectedFunc(t, err, tc.errAs) From eeb13552118504f17b48f2c7e002e777f5ee73f4 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:24:53 +0100 Subject: [PATCH 038/158] feat: fast add credential type lookups (#4177) --- identity/pool.go | 6 + identity/test/pool.go | 21 ++++ .../sql/identity/persister_identity.go | 103 +++++++++++------- x/sync_map.go | 66 +++++++++++ x/sync_map_test.go | 98 +++++++++++++++++ 5 files changed, 255 insertions(+), 39 deletions(-) create mode 100644 x/sync_map.go create mode 100644 x/sync_map_test.go diff --git a/identity/pool.go b/identity/pool.go index f57cd87ca475..340b73a9b98d 100644 --- a/identity/pool.go +++ b/identity/pool.go @@ -115,6 +115,12 @@ type ( // FindIdentityByWebauthnUserHandle returns an identity matching a webauthn user handle. FindIdentityByWebauthnUserHandle(ctx context.Context, userHandle []byte) (*Identity, error) + + // FindIdentityCredentialsTypeByID returns the credentials type by its id. + FindIdentityCredentialsTypeByID(ctx context.Context, id uuid.UUID) (CredentialsType, error) + + // FindIdentityCredentialsTypeByName returns the credentials type by its name. + FindIdentityCredentialsTypeByName(ctx context.Context, ct CredentialsType) (uuid.UUID, error) } ) diff --git a/identity/test/pool.go b/identity/test/pool.go index 4d9f4c440910..14b41f4bf4d8 100644 --- a/identity/test/pool.go +++ b/identity/test/pool.go @@ -1211,6 +1211,27 @@ func TestPool(ctx context.Context, p persistence.Persister, m *identity.Manager, }) }) + t.Run("suite=credential-types", func(t *testing.T) { + for _, ct := range identity.AllCredentialTypes { + t.Run("type="+ct.String(), func(t *testing.T) { + id, err := p.FindIdentityCredentialsTypeByName(ctx, ct) + require.NoError(t, err) + + require.NotEqual(t, uuid.Nil, id) + name, err := p.FindIdentityCredentialsTypeByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, ct, name) + }) + } + + _, err := p.FindIdentityCredentialsTypeByName(ctx, "unknown") + require.Error(t, err) + + _, err = p.FindIdentityCredentialsTypeByID(ctx, x.NewUUID()) + require.Error(t, err) + }) + t.Run("suite=recovery-address", func(t *testing.T) { createIdentityWithAddresses := func(t *testing.T, email string) *identity.Identity { var i identity.Identity diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index ed46af2924ea..026c40687946 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -10,7 +10,6 @@ import ( "fmt" "sort" "strings" - "sync" "time" "github.com/ory/kratos/x/events" @@ -61,12 +60,17 @@ type IdentityPersister struct { r dependencies c *pop.Connection nid uuid.UUID + + credentialTypesID *x.SyncMap[uuid.UUID, identity.CredentialsType] + credentialTypesName *x.SyncMap[identity.CredentialsType, uuid.UUID] } func NewPersister(r dependencies, c *pop.Connection) *IdentityPersister { return &IdentityPersister{ - c: c, - r: r, + c: c, + r: r, + credentialTypesID: x.NewSyncMap[uuid.UUID, identity.CredentialsType](), + credentialTypesName: x.NewSyncMap[identity.CredentialsType, uuid.UUID](), } } @@ -282,36 +286,6 @@ LIMIT 1`, jsonPath, jsonPath), return &id, nil } -var credentialsTypes = struct { - sync.RWMutex - m map[identity.CredentialsType]*identity.CredentialsTypeTable -}{ - m: map[identity.CredentialsType]*identity.CredentialsTypeTable{}, -} - -func (p *IdentityPersister) findIdentityCredentialsType(ctx context.Context, ct identity.CredentialsType) (_ *identity.CredentialsTypeTable, err error) { - credentialsTypes.RLock() - v, ok := credentialsTypes.m[ct] - credentialsTypes.RUnlock() - - if ok && v != nil { - return v, nil - } - - ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.findIdentityCredentialsType") - defer otelx.End(span, &err) - - var m identity.CredentialsTypeTable - if err := p.GetConnection(ctx).Where("name = ?", ct).First(&m); err != nil { - return nil, sqlcon.HandleError(err) - } - credentialsTypes.Lock() - credentialsTypes.m[ct] = &m - credentialsTypes.Unlock() - - return &m, nil -} - func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn *pop.Connection, identities ...*identity.Identity) (err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.createIdentityCredentials", trace.WithAttributes( @@ -339,7 +313,7 @@ func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn cred.Config = sqlxx.JSONRawMessage("{}") } - ct, err := p.findIdentityCredentialsType(ctx, cred.Type) + ct, err := p.FindIdentityCredentialsTypeByName(ctx, cred.Type) if err != nil { return err } @@ -350,7 +324,7 @@ func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn } cred.IdentityID = ident.ID cred.NID = nid - cred.IdentityCredentialTypeID = ct.ID + cred.IdentityCredentialTypeID = ct credentials = append(credentials, &cred) ident.Credentials[k] = cred @@ -370,7 +344,7 @@ func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn "Unable to create identity credentials with missing or empty identifier.")) } - ct, err := p.findIdentityCredentialsType(ctx, cred.Type) + ct, err := p.FindIdentityCredentialsTypeByName(ctx, cred.Type) if err != nil { return err } @@ -378,7 +352,7 @@ func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn identifiers = append(identifiers, &identity.CredentialIdentifier{ Identifier: identifier, IdentityCredentialsID: cred.ID, - IdentityCredentialsTypeID: ct.ID, + IdentityCredentialsTypeID: ct, NID: p.NetworkID(ctx), }) } @@ -883,11 +857,11 @@ func (p *IdentityPersister) getCredentialTypeIDs(ctx context.Context, credential result := map[identity.CredentialsType]uuid.UUID{} for _, ct := range credentialTypes { - typeID, err := p.findIdentityCredentialsType(ctx, ct) + typeID, err := p.FindIdentityCredentialsTypeByName(ctx, ct) if err != nil { return nil, err } - result[ct] = typeID.ID + result[ct] = typeID } return result, nil @@ -1340,3 +1314,54 @@ func (p *IdentityPersister) InjectTraitsSchemaURL(ctx context.Context, i *identi i.SchemaURL = s.SchemaURL(p.r.Config().SelfPublicURL(ctx)).String() return nil } + +func (p *IdentityPersister) FindIdentityCredentialsTypeByID(ctx context.Context, id uuid.UUID) (identity.CredentialsType, error) { + result, found := p.credentialTypesID.Load(id) + if !found { + if err := p.loadCredentialTypes(ctx); err != nil { + return "", err + } + + result, found = p.credentialTypesID.Load(id) + } + + if !found { + return "", errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The SQL adapter failed to return the appropriate credentials_type for id %q. This is a bug in the code.", id)) + } + + return result, nil +} + +func (p *IdentityPersister) FindIdentityCredentialsTypeByName(ctx context.Context, ct identity.CredentialsType) (uuid.UUID, error) { + result, found := p.credentialTypesName.Load(ct) + if !found { + if err := p.loadCredentialTypes(ctx); err != nil { + return uuid.Nil, err + } + + result, found = p.credentialTypesName.Load(ct) + } + + if !found { + return uuid.Nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The SQL adapter failed to return the appropriate credentials_type for nane %s. This is a bug in the code.", ct)) + } + + return result, nil +} + +func (p *IdentityPersister) loadCredentialTypes(ctx context.Context) (err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.identity.loadCredentialTypes") + defer otelx.End(span, &err) + + var tt []identity.CredentialsTypeTable + if err := p.GetConnection(ctx).All(&tt); err != nil { + return sqlcon.HandleError(err) + } + + for _, t := range tt { + p.credentialTypesID.Store(t.ID, t.Name) + p.credentialTypesName.Store(t.Name, t.ID) + } + + return nil +} diff --git a/x/sync_map.go b/x/sync_map.go new file mode 100644 index 000000000000..4a343e932eb6 --- /dev/null +++ b/x/sync_map.go @@ -0,0 +1,66 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package x + +import ( + "sync" +) + +// SyncMap provides a thread-safe map with generic keys and values +type SyncMap[K comparable, V any] struct { + mu sync.RWMutex + data map[K]V +} + +// NewSyncMap initializes a new SyncMap instance +func NewSyncMap[K comparable, V any]() *SyncMap[K, V] { + return &SyncMap[K, V]{ + data: make(map[K]V), + } +} + +// Load retrieves a value for a key. It returns the value and a boolean indicating if the key exists. +func (m *SyncMap[K, V]) Load(key K) (V, bool) { + m.mu.RLock() + defer m.mu.RUnlock() + val, ok := m.data[key] + return val, ok +} + +// Store sets a value for a key, replacing any existing value. +func (m *SyncMap[K, V]) Store(key K, value V) { + m.mu.Lock() + defer m.mu.Unlock() + m.data[key] = value +} + +// LoadOrStore retrieves the existing value for a key if it exists, or stores and returns a given value if it doesn't. +func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (V, bool) { + m.mu.Lock() + defer m.mu.Unlock() + if existing, ok := m.data[key]; ok { + return existing, true + } + m.data[key] = value + return value, false +} + +// Delete removes a key-value pair from the map. +func (m *SyncMap[K, V]) Delete(key K) { + m.mu.Lock() + defer m.mu.Unlock() + delete(m.data, key) +} + +// Range iterates over all entries in the map, calling the provided function for each key-value pair. +// If the function returns false, the iteration stops. +func (m *SyncMap[K, V]) Range(f func(key K, value V) bool) { + m.mu.RLock() + defer m.mu.RUnlock() + for k, v := range m.data { + if !f(k, v) { + break + } + } +} diff --git a/x/sync_map_test.go b/x/sync_map_test.go new file mode 100644 index 000000000000..6da97c63c801 --- /dev/null +++ b/x/sync_map_test.go @@ -0,0 +1,98 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package x + +import ( + "sync" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSyncMapStoreAndLoad(t *testing.T) { + m := NewSyncMap[int, string]() + + m.Store(1, "one") + + // Test Load for an existing key + val, ok := m.Load(1) + require.True(t, ok, "Expected key 1 to exist") + assert.Equal(t, "one", val, "Expected value 'one' for key 1") + + // Test Load for a non-existing key + _, ok = m.Load(2) + assert.False(t, ok, "Expected key 2 to be absent") +} + +func TestSyncMapLoadOrStore(t *testing.T) { + m := NewSyncMap[int, string]() + + // Store a new key-value pair + val, loaded := m.LoadOrStore(1, "one") + require.False(t, loaded, "Expected key 1 to be newly stored") + assert.Equal(t, "one", val, "Expected value 'one' for key 1 after LoadOrStore") + + // Attempt to store a new value for an existing key + val, loaded = m.LoadOrStore(1, "uno") + require.True(t, loaded, "Expected key 1 to already exist") + assert.Equal(t, "one", val, "Expected existing value 'one' for key 1") +} + +func TestSyncMapDelete(t *testing.T) { + m := NewSyncMap[int, string]() + + m.Store(1, "one") + m.Delete(1) + + _, ok := m.Load(1) + assert.False(t, ok, "Expected key 1 to be deleted") +} + +func TestSyncMapRange(t *testing.T) { + m := NewSyncMap[int, string]() + + m.Store(1, "one") + m.Store(2, "two") + m.Store(3, "three") + + expected := map[int]string{ + 1: "one", + 2: "two", + 3: "three", + } + + m.Range(func(key int, value string) bool { + expectedVal, exists := expected[key] + require.True(t, exists, "Unexpected key found in map") + assert.Equal(t, expectedVal, value, "Unexpected value for key %d", key) + delete(expected, key) + return true + }) + + assert.Empty(t, expected, "Not all entries were iterated over") +} + +func TestSyncMapConcurrentAccess(t *testing.T) { + m := NewSyncMap[int, int]() + var wg sync.WaitGroup + + // Run multiple goroutines to test concurrent access + for i := 0; i < 100; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + m.Store(i, i) + }(i) + } + + wg.Wait() + + // Verify all stored values + for i := 0; i < 100; i++ { + val, ok := m.Load(i) + require.True(t, ok, "Expected key %d to exist", i) + assert.Equal(t, i, val, "Expected value %d for key %d", i, i) + } +} From 825aec208d966b54df9eeac6643e6d8129cf2253 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:02:10 +0100 Subject: [PATCH 039/158] feat: improve secondary indices for self service tables (#4179) --- internal/client-go/go.sum | 1 + ...20241029102200000001_self_service.down.sql | 26 +++++++++++++++++++ ...29102200000001_self_service.mysql.down.sql | 26 +++++++++++++++++++ ...1029102200000001_self_service.mysql.up.sql | 26 +++++++++++++++++++ .../20241029102200000001_self_service.up.sql | 26 +++++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 persistence/sql/migrations/sql/20241029102200000001_self_service.down.sql create mode 100644 persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.down.sql create mode 100644 persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.up.sql create mode 100644 persistence/sql/migrations/sql/20241029102200000001_self_service.up.sql diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/persistence/sql/migrations/sql/20241029102200000001_self_service.down.sql b/persistence/sql/migrations/sql/20241029102200000001_self_service.down.sql new file mode 100644 index 000000000000..9ca6f56a49c0 --- /dev/null +++ b/persistence/sql/migrations/sql/20241029102200000001_self_service.down.sql @@ -0,0 +1,26 @@ +CREATE INDEX IF NOT EXISTS selfservice_login_flows_nid_id_idx ON selfservice_login_flows (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS selfservice_login_flows_id_nid_idx ON selfservice_login_flows (id ASC, nid ASC); +DROP INDEX IF EXISTS selfservice_login_flows_nid_idx; + +CREATE INDEX IF NOT EXISTS selfservice_errors_errors_nid_id_idx ON selfservice_errors (nid ASC, id ASC); +DROP INDEX IF EXISTS selfservice_errors_nid_idx; + +CREATE INDEX IF NOT EXISTS selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows (recovered_identity_id ASC, nid ASC); +DROP INDEX IF EXISTS selfservice_recovery_flows_nid_idx; +DROP INDEX IF EXISTS selfservice_recovery_flows_recovered_identity_id_idx; + +CREATE INDEX IF NOT EXISTS selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows (id ASC, nid ASC); +DROP INDEX IF EXISTS selfservice_registration_flows_nid_idx; + +CREATE INDEX IF NOT EXISTS selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows (identity_id ASC, nid ASC); +DROP INDEX IF EXISTS selfservice_settings_flows_nid_idx; +DROP INDEX IF EXISTS selfservice_settings_flows_identity_id_idx; + +CREATE INDEX IF NOT EXISTS selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows (id ASC, nid ASC); +DROP INDEX IF EXISTS selfservice_verification_flows_nid_idx; diff --git a/persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.down.sql b/persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.down.sql new file mode 100644 index 000000000000..0d669e21fe52 --- /dev/null +++ b/persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.down.sql @@ -0,0 +1,26 @@ +CREATE INDEX selfservice_login_flows_nid_id_idx ON selfservice_login_flows (nid ASC, id ASC); +CREATE INDEX selfservice_login_flows_id_nid_idx ON selfservice_login_flows (id ASC, nid ASC); +DROP INDEX selfservice_login_flows_nid_idx ON selfservice_login_flows; + +CREATE INDEX selfservice_errors_errors_nid_id_idx ON selfservice_errors (nid ASC, id ASC); +DROP INDEX selfservice_errors_nid_idx ON selfservice_errors; + +CREATE INDEX selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows (nid ASC, id ASC); +CREATE INDEX selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows (id ASC, nid ASC); +CREATE INDEX selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows (recovered_identity_id ASC, nid ASC); +DROP INDEX selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows; +DROP INDEX selfservice_recovery_flows_recovered_identity_id_idx ON selfservice_recovery_flows; + +CREATE INDEX selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows (nid ASC, id ASC); +CREATE INDEX selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows (id ASC, nid ASC); +DROP INDEX selfservice_registration_flows_nid_idx ON selfservice_registration_flows; + +CREATE INDEX selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows (nid ASC, id ASC); +CREATE INDEX selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows (id ASC, nid ASC); +CREATE INDEX selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows (identity_id ASC, nid ASC); +DROP INDEX selfservice_settings_flows_nid_idx ON selfservice_settings_flows; +DROP INDEX selfservice_settings_flows_identity_id_idx ON selfservice_settings_flows; + +CREATE INDEX selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows (nid ASC, id ASC); +CREATE INDEX selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows (id ASC, nid ASC); +DROP INDEX selfservice_verification_flows_nid_idx ON selfservice_verification_flows; diff --git a/persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.up.sql b/persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.up.sql new file mode 100644 index 000000000000..678658c48e05 --- /dev/null +++ b/persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.up.sql @@ -0,0 +1,26 @@ +CREATE INDEX selfservice_login_flows_nid_idx ON selfservice_login_flows (nid ASC); +DROP INDEX selfservice_login_flows_nid_id_idx ON selfservice_login_flows; +DROP INDEX selfservice_login_flows_id_nid_idx ON selfservice_login_flows; + +CREATE INDEX selfservice_errors_nid_idx ON selfservice_errors (nid ASC); +DROP INDEX selfservice_errors_errors_nid_id_idx ON selfservice_errors; + +CREATE INDEX selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows (nid ASC); +CREATE INDEX selfservice_recovery_flows_recovered_identity_id_idx ON selfservice_recovery_flows (recovered_identity_id ASC); +DROP INDEX selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows; +DROP INDEX selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows; +DROP INDEX selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows; + +CREATE INDEX selfservice_registration_flows_nid_idx ON selfservice_registration_flows (nid ASC); +DROP INDEX selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows; +DROP INDEX selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows; + +CREATE INDEX selfservice_settings_flows_nid_idx ON selfservice_settings_flows (nid ASC); +CREATE INDEX selfservice_settings_flows_identity_id_idx ON selfservice_settings_flows (identity_id ASC); +DROP INDEX selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows; +DROP INDEX selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows; +DROP INDEX selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows; + +CREATE INDEX selfservice_verification_flows_nid_idx ON selfservice_verification_flows (nid ASC); +DROP INDEX selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows; +DROP INDEX selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows; diff --git a/persistence/sql/migrations/sql/20241029102200000001_self_service.up.sql b/persistence/sql/migrations/sql/20241029102200000001_self_service.up.sql new file mode 100644 index 000000000000..e40ce8dd8808 --- /dev/null +++ b/persistence/sql/migrations/sql/20241029102200000001_self_service.up.sql @@ -0,0 +1,26 @@ +CREATE INDEX IF NOT EXISTS selfservice_login_flows_nid_idx ON selfservice_login_flows (nid ASC); +DROP INDEX IF EXISTS selfservice_login_flows_nid_id_idx; +DROP INDEX IF EXISTS selfservice_login_flows_id_nid_idx; + +CREATE INDEX IF NOT EXISTS selfservice_errors_nid_idx ON selfservice_errors (nid ASC); +DROP INDEX IF EXISTS selfservice_errors_errors_nid_id_idx; + +CREATE INDEX IF NOT EXISTS selfservice_recovery_flows_recovered_identity_id_idx ON selfservice_recovery_flows (recovered_identity_id ASC); +CREATE INDEX IF NOT EXISTS selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows (nid ASC); +DROP INDEX IF EXISTS selfservice_recovery_flows_nid_id_idx; +DROP INDEX IF EXISTS selfservice_recovery_flows_id_nid_idx; +DROP INDEX IF EXISTS selfservice_recovery_flows_recovered_identity_id_nid_idx; + +CREATE INDEX IF NOT EXISTS selfservice_registration_flows_nid_idx ON selfservice_registration_flows (nid ASC); +DROP INDEX IF EXISTS selfservice_registration_flows_nid_id_idx; +DROP INDEX IF EXISTS selfservice_registration_flows_id_nid_idx; + +CREATE INDEX IF NOT EXISTS selfservice_settings_flows_nid_idx ON selfservice_settings_flows (nid ASC); +CREATE INDEX IF NOT EXISTS selfservice_settings_flows_identity_id_idx ON selfservice_settings_flows (identity_id ASC); +DROP INDEX IF EXISTS selfservice_settings_flows_nid_id_idx; +DROP INDEX IF EXISTS selfservice_settings_flows_id_nid_idx; +DROP INDEX IF EXISTS selfservice_settings_flows_identity_id_nid_idx; + +CREATE INDEX IF NOT EXISTS selfservice_verification_flows_nid_idx ON selfservice_verification_flows (nid ASC); +DROP INDEX IF EXISTS selfservice_verification_flows_nid_id_idx; +DROP INDEX IF EXISTS selfservice_verification_flows_id_nid_idx; From e24f993ea4236bac4e23bd4250c11b5932040fd9 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:02:49 +0100 Subject: [PATCH 040/158] refactor: remove total count from listSessions and improve secondary indices (#4173) This patch changes sorting to improve performance on list session endpoints. It also removes the `x-total-count` header from list responses. BREAKING CHANGE: The total count header `x-total-count` will no longer be sent in response to `GET /admin/sessions` requests. Closes https://github.com/ory-corp/cloud/issues/7177 Closes https://github.com/ory-corp/cloud/issues/7175 Closes https://github.com/ory-corp/cloud/issues/7176 --- .../sql/identity/persister_identity.go | 11 +++++----- ...used_indices_identity_credentials.down.sql | 6 ++++++ ...ndices_identity_credentials.mysql.down.sql | 4 ++++ ..._indices_identity_credentials.mysql.up.sql | 5 +++++ ...unused_indices_identity_credentials.up.sql | 6 ++++++ ...0002_drop_unused_indices_sessions.down.sql | 9 +++++++++ ...rop_unused_indices_sessions.mysql.down.sql | 9 +++++++++ ..._drop_unused_indices_sessions.mysql.up.sql | 9 +++++++++ ...000002_drop_unused_indices_sessions.up.sql | 9 +++++++++ ..._credential_identifiers.cockroach.down.sql | 11 ++++++++++ ...es_credential_identifiers.cockroach.up.sql | 11 ++++++++++ ...ed_indices_credential_identifiers.down.sql | 5 +++++ ...ices_credential_identifiers.mysql.down.sql | 5 +++++ ...ndices_credential_identifiers.mysql.up.sql | 5 +++++ ...used_indices_credential_identifiers.up.sql | 5 +++++ persistence/sql/persister_session.go | 20 ++++++------------- session/handler.go | 3 +-- session/handler_test.go | 20 ++++++------------- session/persistence.go | 2 +- session/test/persistence.go | 12 ++++------- 20 files changed, 123 insertions(+), 44 deletions(-) create mode 100644 persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.down.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.down.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.up.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.up.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.down.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.down.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.up.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.up.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.down.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.up.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.down.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.down.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.up.sql create mode 100644 persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.up.sql diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 026c40687946..56a53ae3f0dd 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -761,11 +761,11 @@ func QueryForCredentials(con *pop.Connection, where ...Where) (map[uuid.UUID](ma ici := "identity_credential_identifiers" switch con.Dialect.Name() { case "cockroach": - ici += "@identity_credential_identifiers_nid_identity_credential_id_idx" + ici += "@identity_credential_identifiers_identity_credential_id_idx" case "sqlite3": - ici += " INDEXED BY identity_credential_identifiers_nid_identity_credential_id_idx" + ici += " INDEXED BY identity_credential_identifiers_identity_credential_id_idx" case "mysql": - ici += " USE INDEX(identity_credential_identifiers_nid_identity_credential_id_idx)" + ici += " USE INDEX(identity_credential_identifiers_identity_credential_id_idx)" default: // good luck 🤷‍♂️ } @@ -930,8 +930,9 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. // important to normalize the identifier before querying the database. joins = params.TransformStatement(` - INNER JOIN identity_credentials ic ON ic.identity_id = identities.id - INNER JOIN identity_credential_identifiers ici ON ici.identity_credential_id = ic.id`) + INNER JOIN identity_credentials ic ON ic.identity_id = identities.id AND ic.nid = identities.nid + INNER JOIN identity_credential_identifiers ici ON ici.identity_credential_id = ic.id AND ici.nid = ic.nid +`) wheres += fmt.Sprintf(` AND ic.nid = ? AND ici.nid = ? diff --git a/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.down.sql b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.down.sql new file mode 100644 index 000000000000..2c71ddb0abac --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.down.sql @@ -0,0 +1,6 @@ +CREATE INDEX IF NOT EXISTS identity_credentials_id_nid_idx ON identity_credentials (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_credentials_nid_id_idx ON identity_credentials (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS identity_credentials_nid_identity_id_idx ON identity_credentials (identity_id ASC, nid ASC); + +DROP INDEX IF EXISTS identity_credentials_identity_id_idx; +DROP INDEX IF EXISTS identity_credentials_nid_idx; diff --git a/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.down.sql b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.down.sql new file mode 100644 index 000000000000..b86304241b16 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.down.sql @@ -0,0 +1,4 @@ +CREATE INDEX identity_credentials_id_nid_idx ON identity_credentials (id ASC, nid ASC); +CREATE INDEX identity_credentials_nid_id_idx ON identity_credentials (nid ASC, id ASC); + +DROP INDEX identity_credentials_nid_idx ON identity_credentials; diff --git a/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.up.sql b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.up.sql new file mode 100644 index 000000000000..d53879b3f058 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.up.sql @@ -0,0 +1,5 @@ +CREATE INDEX identity_credentials_nid_idx ON identity_credentials (nid ASC); + +DROP INDEX identity_credentials_id_nid_idx ON identity_credentials; +DROP INDEX identity_credentials_nid_id_idx ON identity_credentials; + diff --git a/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.up.sql b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.up.sql new file mode 100644 index 000000000000..c360d9aa24a1 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.up.sql @@ -0,0 +1,6 @@ +CREATE INDEX IF NOT EXISTS identity_credentials_identity_id_idx ON identity_credentials (identity_id ASC); +CREATE INDEX IF NOT EXISTS identity_credentials_nid_idx ON identity_credentials (nid ASC); + +DROP INDEX IF EXISTS identity_credentials_id_nid_idx; +DROP INDEX IF EXISTS identity_credentials_nid_id_idx; +DROP INDEX IF EXISTS identity_credentials_nid_identity_id_idx; diff --git a/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.down.sql b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.down.sql new file mode 100644 index 000000000000..3514f4bb5bd1 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.down.sql @@ -0,0 +1,9 @@ +CREATE INDEX IF NOT EXISTS sessions_nid_id_identity_id_idx ON sessions(nid ASC, identity_id ASC, id ASC); +CREATE INDEX IF NOT EXISTS sessions_id_nid_idx ON sessions(id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS sessions_token_nid_idx ON sessions(nid ASC, token ASC); +CREATE INDEX IF NOT EXISTS sessions_identity_id_nid_sorted_idx ON sessions(identity_id ASC, nid ASC, authenticated_at DESC); +CREATE INDEX IF NOT EXISTS sessions_nid_created_at_id_idx ON sessions(nid ASC, created_at DESC, id ASC); + +DROP INDEX IF EXISTS sessions_list_idx; +DROP INDEX IF EXISTS sessions_list_active_idx; +DROP INDEX IF EXISTS sessions_list_identity_idx; diff --git a/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.down.sql b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.down.sql new file mode 100644 index 000000000000..357de26c4957 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.down.sql @@ -0,0 +1,9 @@ +CREATE INDEX sessions_nid_id_identity_id_idx ON sessions(nid ASC, identity_id ASC, id ASC); +CREATE INDEX sessions_id_nid_idx ON sessions(id ASC, nid ASC); +CREATE INDEX sessions_token_nid_idx ON sessions(nid ASC, token ASC); +CREATE INDEX sessions_identity_id_nid_sorted_idx ON sessions(identity_id ASC, nid ASC, authenticated_at DESC); +CREATE INDEX sessions_nid_created_at_id_idx ON sessions(nid ASC, created_at DESC, id ASC); + +DROP INDEX sessions_list_idx ON sessions; +DROP INDEX sessions_list_active_idx ON sessions; +DROP INDEX sessions_list_identity_idx ON sessions; diff --git a/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.up.sql b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.up.sql new file mode 100644 index 000000000000..fecfd833afcb --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.up.sql @@ -0,0 +1,9 @@ +CREATE INDEX sessions_list_idx ON sessions (nid ASC, created_at DESC, id ASC); +CREATE INDEX sessions_list_active_idx ON sessions (nid ASC, expires_at ASC, active ASC, created_at DESC, id ASC); +CREATE INDEX sessions_list_identity_idx ON sessions (identity_id ASC, nid ASC, created_at DESC); + +DROP INDEX sessions_nid_id_identity_id_idx ON sessions; +DROP INDEX sessions_id_nid_idx ON sessions; +DROP INDEX sessions_token_nid_idx ON sessions; +DROP INDEX sessions_identity_id_nid_sorted_idx ON sessions; +DROP INDEX sessions_nid_created_at_id_idx ON sessions; diff --git a/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.up.sql b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.up.sql new file mode 100644 index 000000000000..cd5122959f80 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.up.sql @@ -0,0 +1,9 @@ +CREATE INDEX IF NOT EXISTS sessions_list_idx ON sessions (nid ASC, created_at DESC, id ASC); +CREATE INDEX IF NOT EXISTS sessions_list_active_idx ON sessions (nid ASC, expires_at ASC, active ASC, created_at DESC, id ASC); +CREATE INDEX IF NOT EXISTS sessions_list_identity_idx ON sessions (identity_id ASC, nid ASC, created_at DESC); + +DROP INDEX IF EXISTS sessions_nid_id_identity_id_idx; +DROP INDEX IF EXISTS sessions_id_nid_idx; +DROP INDEX IF EXISTS sessions_token_nid_idx; +DROP INDEX IF EXISTS sessions_identity_id_nid_sorted_idx; +DROP INDEX IF EXISTS sessions_nid_created_at_id_idx; diff --git a/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.down.sql b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.down.sql new file mode 100644 index 000000000000..696d0027da57 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.down.sql @@ -0,0 +1,11 @@ +-- THIS IS COCKROACH ONLY +ALTER INDEX identity_credential_identifiers_identifier_nid_type_uq_idx RENAME TO identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme; +CREATE UNIQUE INDEX IF NOT EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx ON identity_credential_identifiers(nid ASC, identity_credential_type_id ASC, identifier ASC); +DROP INDEX IF EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme; +-- + +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC); + +DROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx; diff --git a/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.up.sql b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.up.sql new file mode 100644 index 000000000000..b03363cbe982 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.up.sql @@ -0,0 +1,11 @@ +-- THIS IS COCKROACH ONLY +ALTER INDEX identity_credential_identifiers_identifier_nid_type_uq_idx RENAME TO identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme; +CREATE UNIQUE INDEX IF NOT EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx ON identity_credential_identifiers (nid ASC, identity_credential_type_id ASC, identifier ASC) STORING (identity_credential_id); +DROP INDEX IF EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme; +-- + +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC); + +DROP INDEX IF EXISTS identity_credential_identifiers_nid_id_idx; +DROP INDEX IF EXISTS identity_credential_identifiers_id_nid_idx; +DROP INDEX IF EXISTS identity_credential_identifiers_nid_identity_credential_id_idx; diff --git a/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.down.sql b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.down.sql new file mode 100644 index 000000000000..dd899bc7cf3f --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.down.sql @@ -0,0 +1,5 @@ +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC); + +DROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx; diff --git a/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.down.sql b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.down.sql new file mode 100644 index 000000000000..53fcfae91bbc --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.down.sql @@ -0,0 +1,5 @@ +CREATE INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid ASC, id ASC); +CREATE INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id ASC, nid ASC); +CREATE INDEX identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC); + +DROP INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers; diff --git a/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.up.sql b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.up.sql new file mode 100644 index 000000000000..9cbfb64b6375 --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.up.sql @@ -0,0 +1,5 @@ +CREATE INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC); + +DROP INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers; +DROP INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers; +DROP INDEX identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers; diff --git a/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.up.sql b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.up.sql new file mode 100644 index 000000000000..9841ea69404e --- /dev/null +++ b/persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.up.sql @@ -0,0 +1,5 @@ +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC); + +DROP INDEX IF EXISTS identity_credential_identifiers_nid_id_idx; +DROP INDEX IF EXISTS identity_credential_identifiers_id_nid_idx; +DROP INDEX IF EXISTS identity_credential_identifiers_nid_identity_credential_id_idx; diff --git a/persistence/sql/persister_session.go b/persistence/sql/persister_session.go index 6279af56d26a..987fc5f76edf 100644 --- a/persistence/sql/persister_session.go +++ b/persistence/sql/persister_session.go @@ -69,12 +69,11 @@ func (p *Persister) GetSession(ctx context.Context, sid uuid.UUID, expandables s return &s, nil } -func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpts []keysetpagination.Option, expandables session.Expandables) (_ []session.Session, _ int64, _ *keysetpagination.Paginator, err error) { +func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpts []keysetpagination.Option, expandables session.Expandables) (_ []session.Session, _ *keysetpagination.Paginator, err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.ListSessions") defer otelx.End(span, &err) s := make([]session.Session, 0) - t := int64(0) nid := p.NetworkID(ctx) paginatorOpts = append(paginatorOpts, keysetpagination.WithDefaultSize(paginationDefaultItemsSize)) @@ -84,7 +83,7 @@ func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpt paginator := keysetpagination.GetPaginator(paginatorOpts...) if _, err := uuid.FromString(paginator.Token().Parse("id")["id"]); err != nil { - return nil, 0, nil, errors.WithStack(x.PageTokenInvalid) + return nil, nil, errors.WithStack(x.PageTokenInvalid) } if err := p.Transaction(ctx, func(ctx context.Context, c *pop.Connection) error { @@ -97,13 +96,6 @@ func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpt } } - // Get the total count of matching items - total, err := q.Count(new(session.Session)) - if err != nil { - return sqlcon.HandleError(err) - } - t = int64(total) - if len(expandables) > 0 { q = q.EagerPreload(expandables.ToEager()...) } @@ -115,7 +107,7 @@ func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpt return nil }); err != nil { - return nil, 0, nil, err + return nil, nil, err } for k := range s { @@ -123,12 +115,12 @@ func (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpt continue } if err := p.InjectTraitsSchemaURL(ctx, s[k].Identity); err != nil { - return nil, 0, nil, err + return nil, nil, err } } s, nextPage := keysetpagination.Result(s, paginator) - return s, t, nextPage, nil + return s, nextPage, nil } // ListSessionsByIdentity retrieves sessions for an identity from the store. @@ -171,7 +163,7 @@ func (p *Persister) ListSessionsByIdentity( } t = int64(total) - q.Order("authenticated_at DESC") + q.Order("created_at DESC") // Get the paginated list of matching items if err := q.Paginate(page, perPage).All(&s); err != nil { diff --git a/session/handler.go b/session/handler.go index 1699abd766bc..8b8cd84e3bd8 100644 --- a/session/handler.go +++ b/session/handler.go @@ -414,13 +414,12 @@ func (h *Handler) adminListSessions(w http.ResponseWriter, r *http.Request, ps h } } - sess, total, nextPage, err := h.r.SessionPersister().ListSessions(r.Context(), active, opts, expandables) + sess, nextPage, err := h.r.SessionPersister().ListSessions(r.Context(), active, opts, expandables) if err != nil { h.r.Writer().WriteError(w, r, err) return } - w.Header().Set("x-total-count", fmt.Sprint(total)) u := *r.URL keysetpagination.Header(w, &u, nextPage) h.r.Writer().Write(w, r, sess) diff --git a/session/handler_test.go b/session/handler_test.go index 8783a3dcc604..62385b8b37c2 100644 --- a/session/handler_test.go +++ b/session/handler_test.go @@ -590,7 +590,6 @@ func TestHandlerAdminSessionManagement(t *testing.T) { res, err := client.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, "1", res.Header.Get("X-Total-Count")) assertPageToken(t, uuid.Nil.String(), res.Header.Get("Link")) @@ -639,7 +638,6 @@ func TestHandlerAdminSessionManagement(t *testing.T) { res, err := client.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, res.StatusCode) - assert.Equal(t, "1", res.Header.Get("X-Total-Count")) assertPageToken(t, uuid.Nil.String(), res.Header.Get("Link")) body := ioutilx.MustReadAll(res.Body) @@ -812,7 +810,7 @@ func TestHandlerAdminSessionManagement(t *testing.T) { t.Run(fmt.Sprintf("active=%#v", tc.activeOnly), func(t *testing.T) { sessions, _, _ := reg.SessionPersister().ListSessionsByIdentity(ctx, i.ID, nil, 1, 10, uuid.Nil, ExpandEverything) require.Equal(t, 5, len(sessions)) - assert.True(t, sort.IsSorted(sort.Reverse(byAuthenticatedAt(sessions)))) + assert.True(t, sort.IsSorted(sort.Reverse(byCreatedAt(sessions)))) reqURL := ts.URL + "/admin/identities/" + i.ID.String() + "/sessions" if tc.activeOnly != "" { @@ -830,9 +828,6 @@ func TestHandlerAdminSessionManagement(t *testing.T) { actualSessionIds = append(actualSessionIds, s.ID) } - totalCount, err := strconv.Atoi(res.Header.Get("X-Total-Count")) - require.NoError(t, err) - assert.Equal(t, len(tc.expectedSessionIds), totalCount) assert.NotEqual(t, "", res.Header.Get("Link")) assert.ElementsMatch(t, tc.expectedSessionIds, actualSessionIds) }) @@ -895,9 +890,6 @@ func TestHandlerSelfServiceSessionManagement(t *testing.T) { require.NoError(t, err) require.Equal(t, http.StatusOK, res.StatusCode) - totalCount, err := strconv.Atoi(res.Header.Get("X-Total-Count")) - require.NoError(t, err) - require.Equal(t, numSessionsActive, totalCount) require.NotEqual(t, "", res.Header.Get("Link")) }) @@ -1088,10 +1080,10 @@ func TestHandlerRefreshSessionBySessionID(t *testing.T) { }) } -type byAuthenticatedAt []Session +type byCreatedAt []Session -func (s byAuthenticatedAt) Len() int { return len(s) } -func (s byAuthenticatedAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s byAuthenticatedAt) Less(i, j int) bool { - return s[i].AuthenticatedAt.Before(s[j].AuthenticatedAt) +func (s byCreatedAt) Len() int { return len(s) } +func (s byCreatedAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byCreatedAt) Less(i, j int) bool { + return s[i].CreatedAt.Before(s[j].CreatedAt) } diff --git a/session/persistence.go b/session/persistence.go index ab5ec0a47735..abe793a50882 100644 --- a/session/persistence.go +++ b/session/persistence.go @@ -27,7 +27,7 @@ type Persister interface { GetSession(ctx context.Context, sid uuid.UUID, expandables Expandables) (*Session, error) // ListSessions retrieves all sessions. - ListSessions(ctx context.Context, active *bool, paginatorOpts []keysetpagination.Option, expandables Expandables) ([]Session, int64, *keysetpagination.Paginator, error) + ListSessions(ctx context.Context, active *bool, paginatorOpts []keysetpagination.Option, expandables Expandables) ([]Session, *keysetpagination.Paginator, error) // ListSessionsByIdentity retrieves sessions for an identity from the store. ListSessionsByIdentity(ctx context.Context, iID uuid.UUID, active *bool, page, perPage int, except uuid.UUID, expandables Expandables) ([]Session, int64, error) diff --git a/session/test/persistence.go b/session/test/persistence.go index d124aa23eb4f..41bcdd6a9fe4 100644 --- a/session/test/persistence.go +++ b/session/test/persistence.go @@ -295,11 +295,10 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { } { t.Run("case=all "+tc.desc, func(t *testing.T) { paginatorOpts := make([]keysetpagination.Option, 0) - actual, total, nextPage, err := l.ListSessions(ctx, tc.active, paginatorOpts, session.ExpandEverything) + actual, nextPage, err := l.ListSessions(ctx, tc.active, paginatorOpts, session.ExpandEverything) require.NoError(t, err, "%+v", err) require.Equal(t, len(tc.expected), len(actual)) - require.Equal(t, int64(len(tc.expected)), total) assert.Equal(t, true, nextPage.IsLast()) mapPageToken := nextPage.Token().Parse("") @@ -322,11 +321,10 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { t.Run("case=all sessions pagination only one page", func(t *testing.T) { paginatorOpts := make([]keysetpagination.Option, 0) - actual, total, page, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) + actual, page, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) require.NoError(t, err) require.Equal(t, 6, len(actual)) - require.Equal(t, int64(6), total) assert.Equal(t, true, page.IsLast()) mapPageToken := page.Token().Parse("") assert.Equal(t, uuid.Nil.String(), mapPageToken["id"]) @@ -336,9 +334,8 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { t.Run("case=all sessions pagination multiple pages", func(t *testing.T) { paginatorOpts := make([]keysetpagination.Option, 0) paginatorOpts = append(paginatorOpts, keysetpagination.WithSize(3)) - firstPageItems, total, page1, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) + firstPageItems, page1, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything) require.NoError(t, err) - require.Equal(t, int64(6), total) assert.Len(t, firstPageItems, 3) assert.Equal(t, false, page1.IsLast()) @@ -347,9 +344,8 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { assert.Equal(t, 3, page1.Size()) // Validate secondPageItems page - secondPageItems, total, page2, err := l.ListSessions(ctx, nil, page1.ToOptions(), session.ExpandEverything) + secondPageItems, page2, err := l.ListSessions(ctx, nil, page1.ToOptions(), session.ExpandEverything) require.NoError(t, err) - require.Equal(t, int64(6), total) assert.Len(t, secondPageItems, 3) acutalIDs := make([]uuid.UUID, 0) From 5bf46cbe56acac3cf7475d882450121d2702abd0 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:04:46 +0000 Subject: [PATCH 041/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From cb71e38147d21f73e9bd1e081dc3443abb63353e Mon Sep 17 00:00:00 2001 From: Sebastian Schneider Date: Tue, 29 Oct 2024 13:06:15 +0100 Subject: [PATCH 042/158] docs: usage of `organization` parameter in native self-service flows (#4176) --- selfservice/flow/login/handler.go | 7 +++++++ selfservice/flow/registration/handler.go | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/selfservice/flow/login/handler.go b/selfservice/flow/login/handler.go index fe76240b5221..d86641470ef0 100644 --- a/selfservice/flow/login/handler.go +++ b/selfservice/flow/login/handler.go @@ -337,6 +337,13 @@ type createNativeLoginFlow struct { // in: query ReturnTo string `json:"return_to"` + // An optional organization ID that should be used for logging this user in. + // This parameter is only effective in the Ory Network. + // + // required: false + // in: query + Organization string `json:"organization"` + // Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows. // // DEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice diff --git a/selfservice/flow/registration/handler.go b/selfservice/flow/registration/handler.go index ee3e23ba144f..de52abefd5a7 100644 --- a/selfservice/flow/registration/handler.go +++ b/selfservice/flow/registration/handler.go @@ -237,6 +237,13 @@ type createNativeRegistrationFlow struct { // // in: query ReturnTo string `json:"return_to"` + + // An optional organization ID that should be used to register this user. + // This parameter is only effective in the Ory Network. + // + // required: false + // in: query + Organization string `json:"organization"` } // Create Browser Registration Flow Parameters @@ -274,6 +281,9 @@ type createBrowserRegistrationFlow struct { // in: query AfterVerificationReturnTo string `json:"after_verification_return_to"` + // An optional organization ID that should be used to register this user. + // This parameter is only effective in the Ory Network. + // // required: false // in: query Organization string `json:"organization"` From eb0080d45a8725ded4e54fdc7bb71698d0d0f706 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:07:56 +0000 Subject: [PATCH 043/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/api_frontend.go | 16 ++++++++++++++++ internal/httpclient/api_frontend.go | 16 ++++++++++++++++ spec/api.json | 17 +++++++++++++++++ spec/swagger.json | 13 +++++++++++++ 4 files changed, 62 insertions(+) diff --git a/internal/client-go/api_frontend.go b/internal/client-go/api_frontend.go index 18138c402c18..97266e9c4c94 100644 --- a/internal/client-go/api_frontend.go +++ b/internal/client-go/api_frontend.go @@ -1898,6 +1898,7 @@ type FrontendAPIApiCreateNativeLoginFlowRequest struct { xSessionToken *string returnSessionTokenExchangeCode *bool returnTo *string + organization *string via *string } @@ -1921,6 +1922,10 @@ func (r FrontendAPIApiCreateNativeLoginFlowRequest) ReturnTo(returnTo string) Fr r.returnTo = &returnTo return r } +func (r FrontendAPIApiCreateNativeLoginFlowRequest) Organization(organization string) FrontendAPIApiCreateNativeLoginFlowRequest { + r.organization = &organization + return r +} func (r FrontendAPIApiCreateNativeLoginFlowRequest) Via(via string) FrontendAPIApiCreateNativeLoginFlowRequest { r.via = &via return r @@ -1999,6 +2004,9 @@ func (a *FrontendAPIService) CreateNativeLoginFlowExecute(r FrontendAPIApiCreate if r.returnTo != nil { localVarQueryParams.Add("return_to", parameterToString(*r.returnTo, "")) } + if r.organization != nil { + localVarQueryParams.Add("organization", parameterToString(*r.organization, "")) + } if r.via != nil { localVarQueryParams.Add("via", parameterToString(*r.via, "")) } @@ -2211,6 +2219,7 @@ type FrontendAPIApiCreateNativeRegistrationFlowRequest struct { ApiService FrontendAPI returnSessionTokenExchangeCode *bool returnTo *string + organization *string } func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) ReturnSessionTokenExchangeCode(returnSessionTokenExchangeCode bool) FrontendAPIApiCreateNativeRegistrationFlowRequest { @@ -2221,6 +2230,10 @@ func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) ReturnTo(returnTo str r.returnTo = &returnTo return r } +func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) Organization(organization string) FrontendAPIApiCreateNativeRegistrationFlowRequest { + r.organization = &organization + return r +} func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) { return r.ApiService.CreateNativeRegistrationFlowExecute(r) @@ -2288,6 +2301,9 @@ func (a *FrontendAPIService) CreateNativeRegistrationFlowExecute(r FrontendAPIAp if r.returnTo != nil { localVarQueryParams.Add("return_to", parameterToString(*r.returnTo, "")) } + if r.organization != nil { + localVarQueryParams.Add("organization", parameterToString(*r.organization, "")) + } // to determine the Content-Type header localVarHTTPContentTypes := []string{} diff --git a/internal/httpclient/api_frontend.go b/internal/httpclient/api_frontend.go index 18138c402c18..97266e9c4c94 100644 --- a/internal/httpclient/api_frontend.go +++ b/internal/httpclient/api_frontend.go @@ -1898,6 +1898,7 @@ type FrontendAPIApiCreateNativeLoginFlowRequest struct { xSessionToken *string returnSessionTokenExchangeCode *bool returnTo *string + organization *string via *string } @@ -1921,6 +1922,10 @@ func (r FrontendAPIApiCreateNativeLoginFlowRequest) ReturnTo(returnTo string) Fr r.returnTo = &returnTo return r } +func (r FrontendAPIApiCreateNativeLoginFlowRequest) Organization(organization string) FrontendAPIApiCreateNativeLoginFlowRequest { + r.organization = &organization + return r +} func (r FrontendAPIApiCreateNativeLoginFlowRequest) Via(via string) FrontendAPIApiCreateNativeLoginFlowRequest { r.via = &via return r @@ -1999,6 +2004,9 @@ func (a *FrontendAPIService) CreateNativeLoginFlowExecute(r FrontendAPIApiCreate if r.returnTo != nil { localVarQueryParams.Add("return_to", parameterToString(*r.returnTo, "")) } + if r.organization != nil { + localVarQueryParams.Add("organization", parameterToString(*r.organization, "")) + } if r.via != nil { localVarQueryParams.Add("via", parameterToString(*r.via, "")) } @@ -2211,6 +2219,7 @@ type FrontendAPIApiCreateNativeRegistrationFlowRequest struct { ApiService FrontendAPI returnSessionTokenExchangeCode *bool returnTo *string + organization *string } func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) ReturnSessionTokenExchangeCode(returnSessionTokenExchangeCode bool) FrontendAPIApiCreateNativeRegistrationFlowRequest { @@ -2221,6 +2230,10 @@ func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) ReturnTo(returnTo str r.returnTo = &returnTo return r } +func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) Organization(organization string) FrontendAPIApiCreateNativeRegistrationFlowRequest { + r.organization = &organization + return r +} func (r FrontendAPIApiCreateNativeRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) { return r.ApiService.CreateNativeRegistrationFlowExecute(r) @@ -2288,6 +2301,9 @@ func (a *FrontendAPIService) CreateNativeRegistrationFlowExecute(r FrontendAPIAp if r.returnTo != nil { localVarQueryParams.Add("return_to", parameterToString(*r.returnTo, "")) } + if r.organization != nil { + localVarQueryParams.Add("organization", parameterToString(*r.organization, "")) + } // to determine the Content-Type header localVarHTTPContentTypes := []string{} diff --git a/spec/api.json b/spec/api.json index 6fd8dd5cdfc7..954aecaab228 100644 --- a/spec/api.json +++ b/spec/api.json @@ -5633,6 +5633,14 @@ "type": "string" } }, + { + "description": "An optional organization ID that should be used for logging this user in.\nThis parameter is only effective in the Ory Network.", + "in": "query", + "name": "organization", + "schema": { + "type": "string" + } + }, { "description": "Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\n\nDEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\nof MFA credentials to choose from to perform the second factor instead.", "in": "query", @@ -6430,6 +6438,14 @@ "schema": { "type": "string" } + }, + { + "description": "An optional organization ID that should be used to register this user.\nThis parameter is only effective in the Ory Network.", + "in": "query", + "name": "organization", + "schema": { + "type": "string" + } } ], "responses": { @@ -6500,6 +6516,7 @@ } }, { + "description": "An optional organization ID that should be used to register this user.\nThis parameter is only effective in the Ory Network.", "in": "query", "name": "organization", "schema": { diff --git a/spec/swagger.json b/spec/swagger.json index 6b57d6a50873..8b3d47305a96 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -1608,6 +1608,12 @@ "name": "return_to", "in": "query" }, + { + "type": "string", + "description": "An optional organization ID that should be used for logging this user in.\nThis parameter is only effective in the Ory Network.", + "name": "organization", + "in": "query" + }, { "type": "string", "description": "Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\n\nDEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\nof MFA credentials to choose from to perform the second factor instead.", @@ -2265,6 +2271,12 @@ "description": "The URL to return the browser to after the flow was completed.", "name": "return_to", "in": "query" + }, + { + "type": "string", + "description": "An optional organization ID that should be used to register this user.\nThis parameter is only effective in the Ory Network.", + "name": "organization", + "in": "query" } ], "responses": { @@ -2325,6 +2337,7 @@ }, { "type": "string", + "description": "An optional organization ID that should be used to register this user.\nThis parameter is only effective in the Ory Network.", "name": "organization", "in": "query" } From 77beb4de5209cee0bea4b63dfec21d656cf64473 Mon Sep 17 00:00:00 2001 From: teawithfruit Date: Tue, 29 Oct 2024 13:08:25 +0100 Subject: [PATCH 044/158] feat: add oid as subject source for microsoft (#4171) In the case of Microsoft, using `sub` as an identifier can lead to problems. Because the use of OIDC at Microsoft is based on an app registration, the content of `sub` changes with every new app registration. `Sub` is therefore not uniquely related to the user. It is therefore not possible to transfer users from one app registration to another without further problems. https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#payload-claims With the use of `oid` it is possible to identify a user by a unique id. Closes #4170 --- .schemastore/config.schema.json | 2 +- embedx/config.schema.json | 2 +- selfservice/strategy/oidc/provider.go | 1 + selfservice/strategy/oidc/provider_microsoft.go | 4 ++++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.schemastore/config.schema.json b/.schemastore/config.schema.json index 9d853c1e64f3..3a8297d9d1c7 100644 --- a/.schemastore/config.schema.json +++ b/.schemastore/config.schema.json @@ -519,7 +519,7 @@ "title": "Microsoft subject source", "description": "Controls which source the subject identifier is taken from by microsoft provider. If set to `userinfo` (the default) then the identifier is taken from the `sub` field of OIDC ID token or data received from `/userinfo` standard OIDC endpoint. If set to `me` then the `id` field of data structure received from `https://graph.microsoft.com/v1.0/me` is taken as an identifier.", "type": "string", - "enum": ["userinfo", "me"], + "enum": ["userinfo", "me", "oid"], "default": "userinfo", "examples": ["userinfo"] }, diff --git a/embedx/config.schema.json b/embedx/config.schema.json index abe5a4e7ac5b..4c98a58138f0 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -519,7 +519,7 @@ "title": "Microsoft subject source", "description": "Controls which source the subject identifier is taken from by microsoft provider. If set to `userinfo` (the default) then the identifier is taken from the `sub` field of OIDC ID token or data received from `/userinfo` standard OIDC endpoint. If set to `me` then the `id` field of data structure received from `https://graph.microsoft.com/v1.0/me` is taken as an identifier.", "type": "string", - "enum": ["userinfo", "me"], + "enum": ["userinfo", "me", "oid"], "default": "userinfo", "examples": ["userinfo"] }, diff --git a/selfservice/strategy/oidc/provider.go b/selfservice/strategy/oidc/provider.go index 30ea305a22ed..2241cb93d193 100644 --- a/selfservice/strategy/oidc/provider.go +++ b/selfservice/strategy/oidc/provider.go @@ -55,6 +55,7 @@ type NonceValidationSkipper interface { type Claims struct { Issuer string `json:"iss,omitempty"` Subject string `json:"sub,omitempty"` + Object string `json:"oid,omitempty"` Name string `json:"name,omitempty"` GivenName string `json:"given_name,omitempty"` FamilyName string `json:"family_name,omitempty"` diff --git a/selfservice/strategy/oidc/provider_microsoft.go b/selfservice/strategy/oidc/provider_microsoft.go index d69206ec4d87..ec634ce75e3e 100644 --- a/selfservice/strategy/oidc/provider_microsoft.go +++ b/selfservice/strategy/oidc/provider_microsoft.go @@ -117,6 +117,10 @@ func (m *ProviderMicrosoft) updateSubject(ctx context.Context, claims *Claims, e claims.Subject = user.ID } + if m.config.SubjectSource == "oid" { + claims.Subject = claims.Object + } + return claims, nil } From 793e1ebd2a6009a2533e498584f70eeed6229e8a Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:59:49 +0000 Subject: [PATCH 045/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 131 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index feebcd64461d..0dc56c1c6160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,15 @@ **Table of Contents** -- [ (2024-10-23)](#2024-10-23) - - [Bug Fixes](#bug-fixes) - - [Documentation](#documentation) - - [Features](#features) - - [Tests](#tests) -- [1.3.0 (2024-09-26)](#130-2024-09-26) +- [ (2024-10-29)](#2024-10-29) - [Breaking Changes](#breaking-changes) + - [Bug Fixes](#bug-fixes) + - [Code Refactoring](#code-refactoring) + - [Documentation](#documentation) + - [Features](#features) + - [Tests](#tests) +- [1.3.0 (2024-09-26)](#130-2024-09-26) + - [Breaking Changes](#breaking-changes-1) - [Bug Fixes](#bug-fixes-1) - [Code Generation](#code-generation) - [Documentation](#documentation-1) @@ -19,7 +21,7 @@ - [Tests](#tests-1) - [Unclassified](#unclassified) - [1.2.0 (2024-06-05)](#120-2024-06-05) - - [Breaking Changes](#breaking-changes-1) + - [Breaking Changes](#breaking-changes-2) - [Bug Fixes](#bug-fixes-2) - [Code Generation](#code-generation-1) - [Documentation](#documentation-2) @@ -27,7 +29,7 @@ - [Tests](#tests-2) - [Unclassified](#unclassified-1) - [1.1.0 (2024-02-20)](#110-2024-02-20) - - [Breaking Changes](#breaking-changes-2) + - [Breaking Changes](#breaking-changes-3) - [Bug Fixes](#bug-fixes-3) - [Code Generation](#code-generation-2) - [Documentation](#documentation-3) @@ -43,16 +45,16 @@ - [Tests](#tests-4) - [Unclassified](#unclassified-3) - [0.13.0 (2023-04-18)](#0130-2023-04-18) - - [Breaking Changes](#breaking-changes-3) + - [Breaking Changes](#breaking-changes-4) - [Bug Fixes](#bug-fixes-5) - [Code Generation](#code-generation-4) - - [Code Refactoring](#code-refactoring) + - [Code Refactoring](#code-refactoring-1) - [Documentation](#documentation-5) - [Features](#features-5) - [Tests](#tests-5) - [Unclassified](#unclassified-4) - [0.11.1 (2023-01-14)](#0111-2023-01-14) - - [Breaking Changes](#breaking-changes-4) + - [Breaking Changes](#breaking-changes-5) - [Bug Fixes](#bug-fixes-6) - [Code Generation](#code-generation-5) - [Documentation](#documentation-6) @@ -62,10 +64,10 @@ - [Code Generation](#code-generation-6) - [Features](#features-7) - [0.11.0-alpha.0.pre.2 (2022-11-28)](#0110-alpha0pre2-2022-11-28) - - [Breaking Changes](#breaking-changes-5) + - [Breaking Changes](#breaking-changes-6) - [Bug Fixes](#bug-fixes-7) - [Code Generation](#code-generation-7) - - [Code Refactoring](#code-refactoring-1) + - [Code Refactoring](#code-refactoring-2) - [Documentation](#documentation-7) - [Features](#features-8) - [Reverts](#reverts-1) @@ -75,16 +77,16 @@ - [Bug Fixes](#bug-fixes-8) - [Code Generation](#code-generation-8) - [0.10.0 (2022-05-30)](#0100-2022-05-30) - - [Breaking Changes](#breaking-changes-6) + - [Breaking Changes](#breaking-changes-7) - [Bug Fixes](#bug-fixes-9) - [Code Generation](#code-generation-9) - - [Code Refactoring](#code-refactoring-2) + - [Code Refactoring](#code-refactoring-3) - [Documentation](#documentation-8) - [Features](#features-9) - [Tests](#tests-8) - [Unclassified](#unclassified-6) - [0.9.0-alpha.3 (2022-03-25)](#090-alpha3-2022-03-25) - - [Breaking Changes](#breaking-changes-7) + - [Breaking Changes](#breaking-changes-8) - [Bug Fixes](#bug-fixes-10) - [Code Generation](#code-generation-10) - [Documentation](#documentation-9) @@ -92,19 +94,19 @@ - [Bug Fixes](#bug-fixes-11) - [Code Generation](#code-generation-11) - [0.9.0-alpha.1 (2022-03-21)](#090-alpha1-2022-03-21) - - [Breaking Changes](#breaking-changes-8) + - [Breaking Changes](#breaking-changes-9) - [Bug Fixes](#bug-fixes-12) - [Code Generation](#code-generation-12) - - [Code Refactoring](#code-refactoring-3) + - [Code Refactoring](#code-refactoring-4) - [Documentation](#documentation-10) - [Features](#features-10) - [Tests](#tests-9) - [Unclassified](#unclassified-7) - [0.8.3-alpha.1.pre.0 (2022-01-21)](#083-alpha1pre0-2022-01-21) - - [Breaking Changes](#breaking-changes-9) + - [Breaking Changes](#breaking-changes-10) - [Bug Fixes](#bug-fixes-13) - [Code Generation](#code-generation-13) - - [Code Refactoring](#code-refactoring-4) + - [Code Refactoring](#code-refactoring-5) - [Documentation](#documentation-11) - [Features](#features-11) - [Tests](#tests-10) @@ -119,7 +121,7 @@ - [Features](#features-12) - [Tests](#tests-11) - [0.8.0-alpha.4.pre.0 (2021-11-09)](#080-alpha4pre0-2021-11-09) - - [Breaking Changes](#breaking-changes-10) + - [Breaking Changes](#breaking-changes-11) - [Bug Fixes](#bug-fixes-16) - [Code Generation](#code-generation-16) - [Documentation](#documentation-14) @@ -131,10 +133,10 @@ - [0.8.0-alpha.2 (2021-10-28)](#080-alpha2-2021-10-28) - [Code Generation](#code-generation-18) - [0.8.0-alpha.1 (2021-10-27)](#080-alpha1-2021-10-27) - - [Breaking Changes](#breaking-changes-11) + - [Breaking Changes](#breaking-changes-12) - [Bug Fixes](#bug-fixes-18) - [Code Generation](#code-generation-19) - - [Code Refactoring](#code-refactoring-5) + - [Code Refactoring](#code-refactoring-6) - [Documentation](#documentation-15) - [Features](#features-14) - [Reverts](#reverts-2) @@ -161,19 +163,19 @@ - [Documentation](#documentation-18) - [Tests](#tests-15) - [0.7.0-alpha.1 (2021-07-13)](#070-alpha1-2021-07-13) - - [Breaking Changes](#breaking-changes-12) + - [Breaking Changes](#breaking-changes-13) - [Bug Fixes](#bug-fixes-22) - [Code Generation](#code-generation-25) - - [Code Refactoring](#code-refactoring-6) + - [Code Refactoring](#code-refactoring-7) - [Documentation](#documentation-19) - [Features](#features-17) - [Tests](#tests-16) - [Unclassified](#unclassified-9) - [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17) - - [Breaking Changes](#breaking-changes-13) + - [Breaking Changes](#breaking-changes-14) - [Bug Fixes](#bug-fixes-23) - [Code Generation](#code-generation-26) - - [Code Refactoring](#code-refactoring-7) + - [Code Refactoring](#code-refactoring-8) - [0.6.2-alpha.1 (2021-05-14)](#062-alpha1-2021-05-14) - [Code Generation](#code-generation-27) - [Documentation](#documentation-20) @@ -185,10 +187,10 @@ - [Code Generation](#code-generation-29) - [Features](#features-19) - [0.6.0-alpha.1 (2021-05-05)](#060-alpha1-2021-05-05) - - [Breaking Changes](#breaking-changes-14) + - [Breaking Changes](#breaking-changes-15) - [Bug Fixes](#bug-fixes-25) - [Code Generation](#code-generation-30) - - [Code Refactoring](#code-refactoring-8) + - [Code Refactoring](#code-refactoring-9) - [Documentation](#documentation-21) - [Features](#features-20) - [Tests](#tests-17) @@ -203,7 +205,7 @@ - [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11) - [Bug Fixes](#bug-fixes-27) - [Code Generation](#code-generation-32) - - [Code Refactoring](#code-refactoring-9) + - [Code Refactoring](#code-refactoring-10) - [Documentation](#documentation-23) - [Features](#features-22) - [0.5.3-alpha.1 (2020-10-27)](#053-alpha1-2020-10-27) @@ -225,10 +227,10 @@ - [Tests](#tests-21) - [Unclassified](#unclassified-12) - [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15) - - [Breaking Changes](#breaking-changes-15) + - [Breaking Changes](#breaking-changes-16) - [Bug Fixes](#bug-fixes-31) - [Code Generation](#code-generation-36) - - [Code Refactoring](#code-refactoring-10) + - [Code Refactoring](#code-refactoring-11) - [Documentation](#documentation-27) - [Features](#features-25) - [Tests](#tests-22) @@ -250,18 +252,18 @@ - [Bug Fixes](#bug-fixes-36) - [Code Generation](#code-generation-41) - [0.4.0-alpha.1 (2020-07-08)](#040-alpha1-2020-07-08) - - [Breaking Changes](#breaking-changes-16) + - [Breaking Changes](#breaking-changes-17) - [Bug Fixes](#bug-fixes-37) - [Code Generation](#code-generation-42) - - [Code Refactoring](#code-refactoring-11) + - [Code Refactoring](#code-refactoring-12) - [Documentation](#documentation-29) - [Features](#features-26) - [Unclassified](#unclassified-14) - [0.3.0-alpha.1 (2020-05-15)](#030-alpha1-2020-05-15) - - [Breaking Changes](#breaking-changes-17) + - [Breaking Changes](#breaking-changes-18) - [Bug Fixes](#bug-fixes-38) - [Chores](#chores) - - [Code Refactoring](#code-refactoring-12) + - [Code Refactoring](#code-refactoring-13) - [Documentation](#documentation-30) - [Features](#features-27) - [Unclassified](#unclassified-15) @@ -269,20 +271,20 @@ - [Chores](#chores-1) - [Documentation](#documentation-31) - [0.2.0-alpha.2 (2020-05-04)](#020-alpha2-2020-05-04) - - [Breaking Changes](#breaking-changes-18) + - [Breaking Changes](#breaking-changes-19) - [Bug Fixes](#bug-fixes-39) - [Chores](#chores-2) - - [Code Refactoring](#code-refactoring-13) + - [Code Refactoring](#code-refactoring-14) - [Documentation](#documentation-32) - [Features](#features-28) - [Unclassified](#unclassified-16) - [0.1.1-alpha.1 (2020-02-18)](#011-alpha1-2020-02-18) - [Bug Fixes](#bug-fixes-40) - - [Code Refactoring](#code-refactoring-14) + - [Code Refactoring](#code-refactoring-15) - [Documentation](#documentation-33) - [0.1.0-alpha.6 (2020-02-16)](#010-alpha6-2020-02-16) - [Bug Fixes](#bug-fixes-41) - - [Code Refactoring](#code-refactoring-15) + - [Code Refactoring](#code-refactoring-16) - [Documentation](#documentation-34) - [Features](#features-29) - [0.1.0-alpha.5 (2020-02-06)](#010-alpha5-2020-02-06) @@ -337,10 +339,22 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-23) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-29) + +## Breaking Changes + +The total count header `x-total-count` will no longer be sent in response to +`GET /admin/sessions` requests. + +Closes https://github.com/ory-corp/cloud/issues/7177 Closes +https://github.com/ory-corp/cloud/issues/7175 Closes +https://github.com/ory-corp/cloud/issues/7176 ### Bug Fixes +- Account linking should only happen after 2fa when required + ([#4174](https://github.com/ory/kratos/issues/4174)) + ([8e29b68](https://github.com/ory/kratos/commit/8e29b68a595d2ef18e48c2a01072335cefa36d86)) - Duplicate autocomplete trigger ([6bbf915](https://github.com/ory/kratos/commit/6bbf91593a37e4973a86f610290ebab44df8dc81)) - Explicity set updated_at field when updating identity @@ -358,14 +372,49 @@ - Truncate updated at ([#4149](https://github.com/ory/kratos/issues/4149)) ([2f8aaee](https://github.com/ory/kratos/commit/2f8aaee0716835caaba0dff9b6cc457c2cdff5d4)) +### Code Refactoring + +- Remove total count from listSessions and improve secondary indices + ([#4173](https://github.com/ory/kratos/issues/4173)) + ([e24f993](https://github.com/ory/kratos/commit/e24f993ea4236bac4e23bd4250c11b5932040fd9)): + + This patch changes sorting to improve performance on list session endpoints. + It also removes the `x-total-count` header from list responses. + ### Documentation - Add return_to query parameter to OAS Verification Flow for Native Apps ([#4086](https://github.com/ory/kratos/issues/4086)) ([b22135f](https://github.com/ory/kratos/commit/b22135fa05d7fb47dfeaccd7cdc183d16921a7ac)) +- Usage of `organization` parameter in native self-service flows + ([#4176](https://github.com/ory/kratos/issues/4176)) + ([cb71e38](https://github.com/ory/kratos/commit/cb71e38147d21f73e9bd1e081dc3443abb63353e)) ### Features +- Add oid as subject source for microsoft + ([#4171](https://github.com/ory/kratos/issues/4171)) + ([77beb4d](https://github.com/ory/kratos/commit/77beb4de5209cee0bea4b63dfec21d656cf64473)), + closes [#4170](https://github.com/ory/kratos/issues/4170): + + In the case of Microsoft, using `sub` as an identifier can lead to problems. + Because the use of OIDC at Microsoft is based on an app registration, the + content of `sub` changes with every new app registration. `Sub` is therefore + not uniquely related to the user. It is therefore not possible to transfer + users from one app registration to another without further problems. + https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#payload-claims + + With the use of `oid` it is possible to identify a user by a unique id. + +- Allow listing identities by organization ID + ([#4115](https://github.com/ory/kratos/issues/4115)) + ([b4c453b](https://github.com/ory/kratos/commit/b4c453b0472f67d0a52b345691f66aa48777a897)) +- Fast add credential type lookups + ([#4177](https://github.com/ory/kratos/issues/4177)) + ([eeb1355](https://github.com/ory/kratos/commit/eeb13552118504f17b48f2c7e002e777f5ee73f4)) +- Improve secondary indices for self service tables + ([#4179](https://github.com/ory/kratos/issues/4179)) + ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253)) - Passwordless SMS and expiry notice in code / link templates ([#4104](https://github.com/ory/kratos/issues/4104)) ([462cea9](https://github.com/ory/kratos/commit/462cea91448a00a0db21e20c2c347bf74957dc8f)): From 0c48ad12b978bf58b6bc68b0684a7879f93ebf06 Mon Sep 17 00:00:00 2001 From: Henning Perl Date: Tue, 29 Oct 2024 14:18:12 +0100 Subject: [PATCH 046/158] fix: enable b2b_sso hook in more places (#4168) fix: allow b2b_sso hook in more places --------- Co-authored-by: zepatrik --- embedx/config.schema.json | 6 ++++++ selfservice/flow/duplicate_credentials.go | 2 ++ selfservice/flow/login/flow_test.go | 2 ++ selfservice/flow/registration/hook.go | 3 +++ selfservice/strategy/oidc/strategy_registration.go | 1 + selfservice/strategy/oidc/strategy_test.go | 5 +++++ 6 files changed, 19 insertions(+) diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 4c98a58138f0..78a92213d1cd 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -709,6 +709,9 @@ "anyOf": [ { "$ref": "#/definitions/selfServiceWebHook" + }, + { + "$ref": "#/definitions/b2bSSOHook" } ] }, @@ -766,6 +769,9 @@ }, { "$ref": "#/definitions/selfServiceShowVerificationUIHook" + }, + { + "$ref": "#/definitions/b2bSSOHook" } ] }, diff --git a/selfservice/flow/duplicate_credentials.go b/selfservice/flow/duplicate_credentials.go index ddba57251ed2..8e263240cedb 100644 --- a/selfservice/flow/duplicate_credentials.go +++ b/selfservice/flow/duplicate_credentials.go @@ -6,6 +6,7 @@ package flow import ( "encoding/json" + "github.com/gofrs/uuid" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -19,6 +20,7 @@ type DuplicateCredentialsData struct { CredentialsType identity.CredentialsType CredentialsConfig sqlxx.JSONRawMessage DuplicateIdentifier string + OrganizationID uuid.UUID } type InternalContexter interface { diff --git a/selfservice/flow/login/flow_test.go b/selfservice/flow/login/flow_test.go index 24c9d03dcb52..1ac990200479 100644 --- a/selfservice/flow/login/flow_test.go +++ b/selfservice/flow/login/flow_test.go @@ -18,6 +18,7 @@ import ( "github.com/ory/x/jsonx" "github.com/ory/x/sqlxx" + "github.com/ory/x/uuidx" "github.com/ory/kratos/driver/config" "github.com/ory/kratos/identity" @@ -224,6 +225,7 @@ func TestDuplicateCredentials(t *testing.T) { CredentialsType: "foo", CredentialsConfig: sqlxx.JSONRawMessage(`{"bar":"baz"}`), DuplicateIdentifier: "bar", + OrganizationID: uuidx.NewV4(), } require.NoError(t, flow.SetDuplicateCredentials(f, dc)) diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index a002da524c31..f2356d2c8277 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -171,6 +171,9 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque CredentialsConfig: i.Credentials[ct].Config, DuplicateIdentifier: duplicateIdentifier, } + if registrationFlow.OrganizationID.Valid { + registrationDuplicateCredentials.OrganizationID = registrationFlow.OrganizationID.UUID + } if err := flow.SetDuplicateCredentials(registrationFlow, registrationDuplicateCredentials); err != nil { return err diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index 4edab83a351e..3cac2b931d5c 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -278,6 +278,7 @@ func (s *Strategy) registrationToLogin(ctx context.Context, w http.ResponseWrite } lf.TransientPayload = rf.TransientPayload lf.Active = s.ID() + lf.OrganizationID = rf.OrganizationID return lf, nil } diff --git a/selfservice/strategy/oidc/strategy_test.go b/selfservice/strategy/oidc/strategy_test.go index f019e0256129..4bb7bec9bf2f 100644 --- a/selfservice/strategy/oidc/strategy_test.go +++ b/selfservice/strategy/oidc/strategy_test.go @@ -26,6 +26,7 @@ import ( "github.com/ory/kratos/selfservice/hook/hooktest" "github.com/ory/x/sqlxx" + "github.com/ory/x/uuidx" "github.com/ory/kratos/hydra" "github.com/ory/kratos/selfservice/sessiontokenexchange" @@ -1531,6 +1532,7 @@ func TestStrategy(t *testing.T) { subject2 := "new-login-subject2@ory.sh" scope = []string{"openid"} password := "lwkj52sdkjf" + orgID := uuidx.NewV4() var i *identity.Identity t.Run("step=create password identity", func(t *testing.T) { @@ -1555,6 +1557,8 @@ func TestStrategy(t *testing.T) { client := testhelpers.NewClientWithCookieJar(t, nil, nil) loginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser) + loginFlow.OrganizationID = uuid.NullUUID{orgID, true} + require.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), loginFlow)) var linkingLoginFlow struct { ID string @@ -1572,6 +1576,7 @@ func TestStrategy(t *testing.T) { assert.True(t, gjson.GetBytes(body, "ui.nodes.#(attributes.name==identifier)").Exists(), "%s", body) assert.True(t, gjson.GetBytes(body, "ui.nodes.#(attributes.name==password)").Exists(), "%s", body) assert.Equal(t, "new-login-if-email-exist-with-password-strategy@ory.sh", gjson.GetBytes(body, "ui.messages.#(id==1010016).context.duplicateIdentifier").String()) + assert.Equal(t, gjson.GetBytes(body, "organization_id").String(), orgID.String()) linkingLoginFlow.ID = gjson.GetBytes(body, "id").String() linkingLoginFlow.UIAction = gjson.GetBytes(body, "ui.action").String() linkingLoginFlow.CSRFToken = gjson.GetBytes(body, `ui.nodes.#(attributes.name=="csrf_token").attributes.value`).String() From a3fa7604ff6b1f918d61b0ef5afc54f920436334 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:11:00 +0000 Subject: [PATCH 047/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc56c1c6160..ff5add03c1c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -357,6 +357,12 @@ https://github.com/ory-corp/cloud/issues/7176 ([8e29b68](https://github.com/ory/kratos/commit/8e29b68a595d2ef18e48c2a01072335cefa36d86)) - Duplicate autocomplete trigger ([6bbf915](https://github.com/ory/kratos/commit/6bbf91593a37e4973a86f610290ebab44df8dc81)) +- Enable b2b_sso hook in more places + ([#4168](https://github.com/ory/kratos/issues/4168)) + ([0c48ad1](https://github.com/ory/kratos/commit/0c48ad12b978bf58b6bc68b0684a7879f93ebf06)): + + fix: allow b2b_sso hook in more places + - Explicity set updated_at field when updating identity ([#4131](https://github.com/ory/kratos/issues/4131)) ([66afac1](https://github.com/ory/kratos/commit/66afac173dc08b1d6666b107cf7050a2b0b27774)) From 2aabe12e5329acc807c495445999e5591bdf982b Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:21:55 +0100 Subject: [PATCH 048/158] feat: add explicit config flag for secure cookies (#4180) Adds a new config flag for session and all other cookies. Falls back to the previous behavior of using the dev mode to decide if the cookie should be secure or not. --- driver/config/config.go | 16 ++++++++++++++++ driver/registry_default.go | 4 ++-- embedx/config.schema.json | 10 ++++++++++ x/nosurf.go | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/driver/config/config.go b/driver/config/config.go index dd7a6a42f00d..a7e5a09580d0 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -110,6 +110,7 @@ const ( ViperKeyAdminTLSKeyPath = "serve.admin.tls.key.path" ViperKeySessionLifespan = "session.lifespan" ViperKeySessionSameSite = "session.cookie.same_site" + ViperKeySessionSecure = "session.cookie.secure" ViperKeySessionDomain = "session.cookie.domain" ViperKeySessionName = "session.cookie.name" ViperKeySessionPath = "session.cookie.path" @@ -124,6 +125,7 @@ const ( ViperKeyCookieSameSite = "cookies.same_site" ViperKeyCookieDomain = "cookies.domain" ViperKeyCookiePath = "cookies.path" + ViperKeyCookieSecure = "cookies.secure" ViperKeySelfServiceStrategyConfig = "selfservice.methods" ViperKeySelfServiceBrowserDefaultReturnTo = "selfservice." + DefaultBrowserReturnURL ViperKeyURLsAllowedReturnToDomains = "selfservice.allowed_return_urls" @@ -1384,6 +1386,13 @@ func (p *Config) SessionDomain(ctx context.Context) string { return p.GetProvider(ctx).String(ViperKeySessionDomain) } +func (p *Config) SessionCookieSecure(ctx context.Context) bool { + if !p.GetProvider(ctx).Exists(ViperKeySessionSecure) { + return !p.IsInsecureDevMode(ctx) + } + return p.GetProvider(ctx).Bool(ViperKeySessionSecure) +} + func (p *Config) CookieDomain(ctx context.Context) string { return p.GetProvider(ctx).String(ViperKeyCookieDomain) } @@ -1439,6 +1448,13 @@ func (p *Config) CookiePath(ctx context.Context) string { return p.GetProvider(ctx).String(ViperKeyCookiePath) } +func (p *Config) CookieSecure(ctx context.Context) bool { + if !p.GetProvider(ctx).Exists(ViperKeyCookieSecure) { + return !p.IsInsecureDevMode(ctx) + } + return p.GetProvider(ctx).Bool(ViperKeyCookieSecure) +} + func (p *Config) SelfServiceFlowLoginReturnTo(ctx context.Context, strategy string) *url.URL { return p.selfServiceReturnTo(ctx, ViperKeySelfServiceLoginAfter, strategy) } diff --git a/driver/registry_default.go b/driver/registry_default.go index 9f88c7375dd0..70e2994db5c9 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -527,7 +527,7 @@ func (m *RegistryDefault) CookieManager(ctx context.Context) sessions.StoreExact } cs := sessions.NewCookieStore(keys...) - cs.Options.Secure = !m.Config().IsInsecureDevMode(ctx) + cs.Options.Secure = m.Config().SessionCookieSecure(ctx) cs.Options.HttpOnly = true if domain := m.Config().SessionDomain(ctx); domain != "" { @@ -553,7 +553,7 @@ func (m *RegistryDefault) CookieManager(ctx context.Context) sessions.StoreExact func (m *RegistryDefault) ContinuityCookieManager(ctx context.Context) sessions.StoreExact { // To support hot reloading, this can not be instantiated only once. cs := sessions.NewCookieStore(m.Config().SecretsSession(ctx)...) - cs.Options.Secure = !m.Config().IsInsecureDevMode(ctx) + cs.Options.Secure = m.Config().CookieSecure(ctx) cs.Options.HttpOnly = true cs.Options.SameSite = http.SameSiteLaxMode return cs diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 78a92213d1cd..6a1cc1c90ede 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -2785,6 +2785,11 @@ "type": "string", "default": "/" }, + "secure": { + "title": "Session Cookie Secure Flag", + "description": "Sets the session secure flag. If unset, defaults to !dev mode.", + "type": "string" + }, "same_site": { "title": "HTTP Cookie Same Site Configuration", "description": "Sets the session and CSRF cookie SameSite.", @@ -2879,6 +2884,11 @@ "description": "Sets the session cookie path. Use with care! Overrides `cookies.path`.", "type": "string" }, + "secure": { + "title": "Session Cookie Secure Flag", + "description": "Sets the session secure flag. If unset, defaults to !dev mode.", + "type": "string" + }, "same_site": { "title": "Session Cookie SameSite Configuration", "description": "Sets the session cookie SameSite. Overrides `cookies.same_site`.", diff --git a/x/nosurf.go b/x/nosurf.go index 8169717fcda8..ce49f01d3cdd 100644 --- a/x/nosurf.go +++ b/x/nosurf.go @@ -138,7 +138,7 @@ func NosurfBaseCookieHandler(reg interface { config.Provider }) func(w http.ResponseWriter, r *http.Request) http.Cookie { return func(w http.ResponseWriter, r *http.Request) http.Cookie { - secure := !reg.Config().IsInsecureDevMode(r.Context()) + secure := reg.Config().CookieSecure(r.Context()) sameSite := reg.Config().CookieSameSiteMode(r.Context()) if !secure { From 53874c1753940e08e0bf50753a1d3126add77af1 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:50:34 +0100 Subject: [PATCH 049/158] feat: optimize identity-related secondary indices (#4182) --- .../20241029153900000001_identities.down.sql | 13 +++++++++++++ ...241029153900000001_identities.mysql.down.sql | 17 +++++++++++++++++ ...20241029153900000001_identities.mysql.up.sql | 13 +++++++++++++ .../sql/20241029153900000001_identities.up.sql | 13 +++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 persistence/sql/migrations/sql/20241029153900000001_identities.down.sql create mode 100644 persistence/sql/migrations/sql/20241029153900000001_identities.mysql.down.sql create mode 100644 persistence/sql/migrations/sql/20241029153900000001_identities.mysql.up.sql create mode 100644 persistence/sql/migrations/sql/20241029153900000001_identities.up.sql diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.down.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.down.sql new file mode 100644 index 000000000000..eea887f40899 --- /dev/null +++ b/persistence/sql/migrations/sql/20241029153900000001_identities.down.sql @@ -0,0 +1,13 @@ +CREATE INDEX IF NOT EXISTS identities_id_nid_idx ON identities (id ASC, nid ASC); + +CREATE INDEX IF NOT EXISTS identity_recovery_addresses_status_via_idx ON identity_recovery_addresses (nid ASC, via ASC, value ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_addresses_nid_identity_id_idx ON identity_recovery_addresses (identity_id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses (id ASC, nid ASC); +DROP INDEX IF EXISTS identity_recovery_addresses_identity_id_idx; + +CREATE INDEX IF NOT EXISTS identity_verifiable_addresses_status_via_idx ON identity_verifiable_addresses (nid ASC, via ASC, value ASC); +CREATE INDEX IF NOT EXISTS identity_verifiable_addresses_nid_identity_id_idx ON identity_verifiable_addresses (identity_id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses (id ASC, nid ASC); +DROP INDEX IF EXISTS identity_verifiable_addresses_identity_id_idx; diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.mysql.down.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.mysql.down.sql new file mode 100644 index 000000000000..83a531c1e0ec --- /dev/null +++ b/persistence/sql/migrations/sql/20241029153900000001_identities.mysql.down.sql @@ -0,0 +1,17 @@ +CREATE INDEX identities_id_nid_idx ON identities (id ASC, nid ASC); + +CREATE INDEX identity_recovery_addresses_status_via_idx ON identity_recovery_addresses (nid ASC, via ASC, value ASC); +-- While this index did not exist in the past, it is needed in MySQL for foreign key relations. We accept +-- that this index is "unaccounted" for if we execute down and then up migrations on MySQL. +CREATE INDEX identity_recovery_addresses_identity_id_fk_idx ON identity_recovery_addresses (identity_id ASC); +CREATE INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid ASC, id ASC); +CREATE INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses (id ASC, nid ASC); +DROP INDEX identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses; + +CREATE INDEX identity_verifiable_addresses_status_via_idx ON identity_verifiable_addresses (nid ASC, via ASC, value ASC); +-- While this index did not exist in the past, it is needed in MySQL for foreign key relations. We accept +-- that this index is "unaccounted" for if we execute down and then up migrations on MySQL. +CREATE INDEX identity_verifiable_addresses_identity_id_fk_idx ON identity_verifiable_addresses (identity_id ASC); +CREATE INDEX identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses (nid ASC, id ASC); +CREATE INDEX identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses (id ASC, nid ASC); +DROP INDEX identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses; diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.mysql.up.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.mysql.up.sql new file mode 100644 index 000000000000..8d2ce70d220a --- /dev/null +++ b/persistence/sql/migrations/sql/20241029153900000001_identities.mysql.up.sql @@ -0,0 +1,13 @@ +DROP INDEX identities_id_nid_idx ON identities; + +CREATE INDEX identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses (identity_id ASC); +DROP INDEX identity_recovery_addresses_status_via_idx ON identity_recovery_addresses; +-- DROP INDEX identity_recovery_addresses_nid_identity_id_idx ON identity_recovery_addresses; +DROP INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses; +DROP INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses; + +CREATE INDEX identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC); +DROP INDEX identity_verifiable_addresses_status_via_idx ON identity_verifiable_addresses; +-- DROP INDEX identity_verifiable_addresses_nid_identity_id_idx ON identity_verifiable_addresses; +DROP INDEX identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses; +DROP INDEX identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses; diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.up.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.up.sql new file mode 100644 index 000000000000..5b117cf05091 --- /dev/null +++ b/persistence/sql/migrations/sql/20241029153900000001_identities.up.sql @@ -0,0 +1,13 @@ +DROP INDEX IF EXISTS identities_id_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses(identity_id ASC); +DROP INDEX IF EXISTS identity_recovery_addresses_status_via_idx; +DROP INDEX IF EXISTS identity_recovery_addresses_nid_identity_id_idx; +DROP INDEX IF EXISTS identity_recovery_addresses_nid_id_idx; +DROP INDEX IF EXISTS identity_recovery_addresses_id_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC); +DROP INDEX IF EXISTS identity_verifiable_addresses_status_via_idx; +DROP INDEX IF EXISTS identity_verifiable_addresses_nid_identity_id_idx; +DROP INDEX IF EXISTS identity_verifiable_addresses_nid_id_idx; +DROP INDEX IF EXISTS identity_verifiable_addresses_id_nid_idx; From a1d3eafa6a61ae7c84cabadd2f227c172faa056c Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:41:26 +0000 Subject: [PATCH 050/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff5add03c1c8..4954f5a4940e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-10-29)](#2024-10-29) +- [ (2024-10-30)](#2024-10-30) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-29) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-30) ## Breaking Changes @@ -398,6 +398,14 @@ https://github.com/ory-corp/cloud/issues/7176 ### Features +- Add explicit config flag for secure cookies + ([#4180](https://github.com/ory/kratos/issues/4180)) + ([2aabe12](https://github.com/ory/kratos/commit/2aabe12e5329acc807c495445999e5591bdf982b)): + + Adds a new config flag for session and all other cookies. Falls back to the + previous behavior of using the dev mode to decide if the cookie should be + secure or not. + - Add oid as subject source for microsoft ([#4171](https://github.com/ory/kratos/issues/4171)) ([77beb4d](https://github.com/ory/kratos/commit/77beb4de5209cee0bea4b63dfec21d656cf64473)), @@ -421,6 +429,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Improve secondary indices for self service tables ([#4179](https://github.com/ory/kratos/issues/4179)) ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253)) +- Optimize identity-related secondary indices + ([#4182](https://github.com/ory/kratos/issues/4182)) + ([53874c1](https://github.com/ory/kratos/commit/53874c1753940e08e0bf50753a1d3126add77af1)) - Passwordless SMS and expiry notice in code / link templates ([#4104](https://github.com/ory/kratos/issues/4104)) ([462cea9](https://github.com/ory/kratos/commit/462cea91448a00a0db21e20c2c347bf74957dc8f)): From 7bec935c33b9adb6033aaecfa9a6dbe6c9c3daa1 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Wed, 30 Oct 2024 14:09:56 +0100 Subject: [PATCH 051/158] feat: allow extra go migrations in persister (#4183) --- driver/registry.go | 53 +++++++++++++++++++----------------- driver/registry_default.go | 5 +++- go.mod | 2 +- go.sum | 1 - persistence/sql/persister.go | 23 ++++++++-------- 5 files changed, 45 insertions(+), 39 deletions(-) diff --git a/driver/registry.go b/driver/registry.go index dc31f7305633..e284d6f6a6dd 100644 --- a/driver/registry.go +++ b/driver/registry.go @@ -7,45 +7,39 @@ import ( "context" "io/fs" - "github.com/ory/kratos/selfservice/sessiontokenexchange" - "github.com/ory/x/contextx" - "github.com/ory/x/jsonnetsecure" - "github.com/ory/x/otelx" - prometheus "github.com/ory/x/prometheusx" - "github.com/gorilla/sessions" "github.com/pkg/errors" - "github.com/ory/nosurf" - - "github.com/ory/x/logrusx" - + "github.com/ory/kratos/cipher" "github.com/ory/kratos/continuity" "github.com/ory/kratos/courier" + "github.com/ory/kratos/driver/config" "github.com/ory/kratos/hash" + "github.com/ory/kratos/identity" + "github.com/ory/kratos/persistence" "github.com/ory/kratos/schema" + "github.com/ory/kratos/selfservice/errorx" + "github.com/ory/kratos/selfservice/flow/login" + "github.com/ory/kratos/selfservice/flow/logout" "github.com/ory/kratos/selfservice/flow/recovery" + "github.com/ory/kratos/selfservice/flow/registration" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/selfservice/flow/verification" + "github.com/ory/kratos/selfservice/sessiontokenexchange" "github.com/ory/kratos/selfservice/strategy/code" "github.com/ory/kratos/selfservice/strategy/link" - - "github.com/ory/x/healthx" - - "github.com/ory/kratos/persistence" - "github.com/ory/kratos/selfservice/flow/login" - "github.com/ory/kratos/selfservice/flow/logout" - "github.com/ory/kratos/selfservice/flow/registration" - - "github.com/ory/kratos/x" - - "github.com/ory/x/dbal" - - "github.com/ory/kratos/driver/config" - "github.com/ory/kratos/identity" - "github.com/ory/kratos/selfservice/errorx" password2 "github.com/ory/kratos/selfservice/strategy/password" "github.com/ory/kratos/session" + "github.com/ory/kratos/x" + "github.com/ory/nosurf" + "github.com/ory/x/contextx" + "github.com/ory/x/dbal" + "github.com/ory/x/healthx" + "github.com/ory/x/jsonnetsecure" + "github.com/ory/x/logrusx" + "github.com/ory/x/otelx" + "github.com/ory/x/popx" + prometheus "github.com/ory/x/prometheusx" ) type Registry interface { @@ -85,6 +79,8 @@ type Registry interface { continuity.ManagementProvider continuity.PersistenceProvider + cipher.Provider + courier.Provider persistence.Provider @@ -186,6 +182,7 @@ type options struct { replaceIdentitySchemaProvider func(Registry) schema.IdentitySchemaProvider inspect func(Registry) error extraMigrations []fs.FS + extraGoMigrations popx.Migrations replacementStrategies []NewStrategy extraHooks map[string]func(config.SelfServiceHook) any disableMigrationLogging bool @@ -251,6 +248,12 @@ func WithExtraMigrations(m ...fs.FS) RegistryOption { } } +func WithExtraGoMigrations(m ...popx.Migration) RegistryOption { + return func(o *options) { + o.extraGoMigrations = append(o.extraGoMigrations, m...) + } +} + func WithDisabledMigrationLogging() RegistryOption { return func(o *options) { o.disableMigrationLogging = true diff --git a/driver/registry_default.go b/driver/registry_default.go index 70e2994db5c9..6cdfb5bfe41f 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -672,7 +672,10 @@ func (m *RegistryDefault) Init(ctx context.Context, ctxer contextx.Contextualize m.Logger().WithError(err).Warnf("Unable to open database, retrying.") return errors.WithStack(err) } - p, err := sql.NewPersister(ctx, m, c, sql.WithExtraMigrations(o.extraMigrations...), sql.WithDisabledLogging(o.disableMigrationLogging)) + p, err := sql.NewPersister(ctx, m, c, + sql.WithExtraMigrations(o.extraMigrations...), + sql.WithExtraGoMigrations(o.extraGoMigrations...), + sql.WithDisabledLogging(o.disableMigrationLogging)) if err != nil { m.Logger().WithError(err).Warnf("Unable to initialize persister, retrying.") return err diff --git a/go.mod b/go.mod index ac36ab25892a..61009dc01832 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/jmoiron/sqlx v1.4.0 github.com/julienschmidt/httprouter v1.3.0 github.com/knadh/koanf/parsers/json v0.1.0 - github.com/laher/mergefs v0.1.2-0.20230223191438-d16611b2f4e7 + github.com/laher/mergefs v0.1.2-0.20230223191438-d16611b2f4e7 // indirect github.com/lestrrat-go/jwx/v2 v2.1.1 github.com/luna-duclos/instrumentedsql v1.1.3 github.com/mailhog/MailHog v1.0.1 diff --git a/go.sum b/go.sum index e33a12499ae0..8036e25d5bec 100644 --- a/go.sum +++ b/go.sum @@ -551,7 +551,6 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno= github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= -github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= diff --git a/persistence/sql/persister.go b/persistence/sql/persister.go index 85bcdf7466c8..6939857c372b 100644 --- a/persistence/sql/persister.go +++ b/persistence/sql/persister.go @@ -11,7 +11,6 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" - "github.com/laher/mergefs" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -24,6 +23,7 @@ import ( "github.com/ory/kratos/session" "github.com/ory/kratos/x" "github.com/ory/x/contextx" + "github.com/ory/x/fsx" "github.com/ory/x/networkx" "github.com/ory/x/otelx" "github.com/ory/x/popx" @@ -57,8 +57,9 @@ type ( ) type persisterOptions struct { - extraMigrations []fs.FS - disableLogging bool + extraMigrations []fs.FS + extraGoMigrations popx.Migrations + disableLogging bool } type persisterOption func(o *persisterOptions) @@ -69,6 +70,12 @@ func WithExtraMigrations(fss ...fs.FS) persisterOption { } } +func WithExtraGoMigrations(ms ...popx.Migration) persisterOption { + return func(o *persisterOptions) { + o.extraGoMigrations = ms + } +} + func WithDisabledLogging(v bool) persisterOption { return func(o *persisterOptions) { o.disableLogging = v @@ -85,15 +92,9 @@ func NewPersister(ctx context.Context, r persisterDependencies, c *pop.Connectio logger.Logrus().SetLevel(logrus.WarnLevel) } m, err := popx.NewMigrationBox( - mergefs.Merge( - append( - []fs.FS{ - migrations, networkx.Migrations, - }, - o.extraMigrations..., - )..., - ), + fsx.Merge(append([]fs.FS{migrations, networkx.Migrations}, o.extraMigrations...)...), popx.NewMigrator(c, logger, r.Tracer(ctx), 0), + popx.WithGoMigrations(o.extraGoMigrations), ) if err != nil { return nil, err From 2fdcd97ca1f8e7a48cf36d57ed239660edb6bf76 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:02:47 +0000 Subject: [PATCH 052/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4954f5a4940e..dca6dd2e7f2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -420,6 +420,9 @@ https://github.com/ory-corp/cloud/issues/7176 With the use of `oid` it is possible to identify a user by a unique id. +- Allow extra go migrations in persister + ([#4183](https://github.com/ory/kratos/issues/4183)) + ([7bec935](https://github.com/ory/kratos/commit/7bec935c33b9adb6033aaecfa9a6dbe6c9c3daa1)) - Allow listing identities by organization ID ([#4115](https://github.com/ory/kratos/issues/4115)) ([b4c453b](https://github.com/ory/kratos/commit/b4c453b0472f67d0a52b345691f66aa48777a897)) From ca0d6a7ea717495429b8bac7fd843ac69c1ebf16 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Thu, 31 Oct 2024 17:05:16 +0100 Subject: [PATCH 053/158] feat: improve QueryForCredentials (#4181) --- identity/pool.go | 6 - identity/test/pool.go | 15 +- .../sql/identity/persister_identity.go | 163 +++++++++--------- persistence/sql/migratest/migration_test.go | 5 +- 4 files changed, 89 insertions(+), 100 deletions(-) diff --git a/identity/pool.go b/identity/pool.go index 340b73a9b98d..f57cd87ca475 100644 --- a/identity/pool.go +++ b/identity/pool.go @@ -115,12 +115,6 @@ type ( // FindIdentityByWebauthnUserHandle returns an identity matching a webauthn user handle. FindIdentityByWebauthnUserHandle(ctx context.Context, userHandle []byte) (*Identity, error) - - // FindIdentityCredentialsTypeByID returns the credentials type by its id. - FindIdentityCredentialsTypeByID(ctx context.Context, id uuid.UUID) (CredentialsType, error) - - // FindIdentityCredentialsTypeByName returns the credentials type by its name. - FindIdentityCredentialsTypeByName(ctx context.Context, ct CredentialsType) (uuid.UUID, error) } ) diff --git a/identity/test/pool.go b/identity/test/pool.go index 14b41f4bf4d8..4f898917449f 100644 --- a/identity/test/pool.go +++ b/identity/test/pool.go @@ -13,10 +13,6 @@ import ( "testing" "time" - confighelpers "github.com/ory/kratos/driver/config/testhelpers" - - "github.com/ory/x/crdbx" - "github.com/go-faker/faker/v4" "github.com/gofrs/uuid" "github.com/stretchr/testify/assert" @@ -24,12 +20,15 @@ import ( "github.com/tidwall/gjson" "github.com/ory/kratos/driver/config" + confighelpers "github.com/ory/kratos/driver/config/testhelpers" "github.com/ory/kratos/identity" "github.com/ory/kratos/internal/testhelpers" "github.com/ory/kratos/persistence" + idpersistence "github.com/ory/kratos/persistence/sql/identity" "github.com/ory/kratos/schema" "github.com/ory/kratos/x" "github.com/ory/x/assertx" + "github.com/ory/x/crdbx" "github.com/ory/x/errorsx" "github.com/ory/x/pagination/keysetpagination" "github.com/ory/x/randx" @@ -1214,21 +1213,21 @@ func TestPool(ctx context.Context, p persistence.Persister, m *identity.Manager, t.Run("suite=credential-types", func(t *testing.T) { for _, ct := range identity.AllCredentialTypes { t.Run("type="+ct.String(), func(t *testing.T) { - id, err := p.FindIdentityCredentialsTypeByName(ctx, ct) + id, err := idpersistence.FindIdentityCredentialsTypeByName(p.GetConnection(ctx), ct) require.NoError(t, err) require.NotEqual(t, uuid.Nil, id) - name, err := p.FindIdentityCredentialsTypeByID(ctx, id) + name, err := idpersistence.FindIdentityCredentialsTypeByID(p.GetConnection(ctx), id) require.NoError(t, err) assert.Equal(t, ct, name) }) } - _, err := p.FindIdentityCredentialsTypeByName(ctx, "unknown") + _, err := idpersistence.FindIdentityCredentialsTypeByName(p.GetConnection(ctx), "unknown") require.Error(t, err) - _, err = p.FindIdentityCredentialsTypeByID(ctx, x.NewUUID()) + _, err = idpersistence.FindIdentityCredentialsTypeByID(p.GetConnection(ctx), x.NewUUID()) require.Error(t, err) }) diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 56a53ae3f0dd..00ac440a16a6 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -10,6 +10,7 @@ import ( "fmt" "sort" "strings" + "sync" "time" "github.com/ory/kratos/x/events" @@ -60,17 +61,12 @@ type IdentityPersister struct { r dependencies c *pop.Connection nid uuid.UUID - - credentialTypesID *x.SyncMap[uuid.UUID, identity.CredentialsType] - credentialTypesName *x.SyncMap[identity.CredentialsType, uuid.UUID] } func NewPersister(r dependencies, c *pop.Connection) *IdentityPersister { return &IdentityPersister{ - c: c, - r: r, - credentialTypesID: x.NewSyncMap[uuid.UUID, identity.CredentialsType](), - credentialTypesName: x.NewSyncMap[identity.CredentialsType, uuid.UUID](), + c: c, + r: r, } } @@ -313,7 +309,7 @@ func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn cred.Config = sqlxx.JSONRawMessage("{}") } - ct, err := p.FindIdentityCredentialsTypeByName(ctx, cred.Type) + ct, err := FindIdentityCredentialsTypeByName(conn, cred.Type) if err != nil { return err } @@ -344,7 +340,7 @@ func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn "Unable to create identity credentials with missing or empty identifier.")) } - ct, err := p.FindIdentityCredentialsTypeByName(ctx, cred.Type) + ct, err := FindIdentityCredentialsTypeByName(conn, cred.Type) if err != nil { return err } @@ -607,7 +603,6 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... for _, k := range paritalErr.Failed { failedIdentityIDs[k.IdentityID] = struct{}{} } - } else if paritalErr := new(batch.PartialConflictError[identity.CredentialIdentifier]); errors.As(err, &paritalErr) { for _, k := range paritalErr.Failed { credID := k.IdentityCredentialsID @@ -663,10 +658,7 @@ func (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i * attribute.Stringer("network.id", p.NetworkID(ctx)))) defer otelx.End(span, &err) - var ( - con = p.GetConnection(ctx) - nid = p.NetworkID(ctx) - ) + nid := p.NetworkID(ctx) eg, ctx := errgroup.WithContext(ctx) if expand.Has(identity.ExpandFieldRecoveryAddresses) { @@ -675,7 +667,7 @@ func (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i * // from complaining incorrectly. // // https://github.com/gobuffalo/pop/issues/723 - if err := con.WithContext(ctx). + if err := p.GetConnection(ctx).WithContext(ctx). Where("identity_id = ? AND nid = ?", i.ID, nid). Order("id ASC"). All(&i.RecoveryAddresses); err != nil { @@ -691,7 +683,7 @@ func (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i * // from complaining incorrectly. // // https://github.com/gobuffalo/pop/issues/723 - if err := con.WithContext(ctx). + if err := p.GetConnection(ctx).WithContext(ctx). Order("id ASC"). Where("identity_id = ? AND nid = ?", i.ID, nid). All(&i.VerifiableAddresses); err != nil { @@ -707,9 +699,9 @@ func (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i * // from complaining incorrectly. // // https://github.com/gobuffalo/pop/issues/723 - con := con.WithContext(ctx) - creds, err := QueryForCredentials(con, - Where{"(identity_credentials.identity_id = ? AND identity_credentials.nid = ?)", []interface{}{i.ID, nid}}) + creds, err := QueryForCredentials(p.GetConnection(ctx).WithContext(ctx), + Where{"identity_credentials.identity_id = ?", []interface{}{i.ID}}, + Where{"identity_credentials.nid = ?", []interface{}{nid}}) if err != nil { return err } @@ -734,16 +726,8 @@ func (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i * } type queryCredentials struct { - ID uuid.UUID `db:"cred_id"` - IdentityID uuid.UUID `db:"identity_id"` - NID uuid.UUID `db:"nid"` - Type identity.CredentialsType `db:"cred_type"` - TypeID uuid.UUID `db:"cred_type_id"` - Identifier string `db:"cred_identifier"` - Config sqlxx.JSONRawMessage `db:"cred_config"` - Version int `db:"cred_version"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` + Identifier string `db:"cred_identifier"` + identity.Credentials } func (queryCredentials) TableName() string { @@ -757,35 +741,23 @@ type Where struct { // QueryForCredentials queries for identity credentials with custom WHERE // clauses, returning the results resolved by the owning identity's UUID. -func QueryForCredentials(con *pop.Connection, where ...Where) (map[uuid.UUID](map[identity.CredentialsType]identity.Credentials), error) { - ici := "identity_credential_identifiers" - switch con.Dialect.Name() { - case "cockroach": - ici += "@identity_credential_identifiers_identity_credential_id_idx" - case "sqlite3": - ici += " INDEXED BY identity_credential_identifiers_identity_credential_id_idx" - case "mysql": - ici += " USE INDEX(identity_credential_identifiers_identity_credential_id_idx)" - default: - // good luck 🤷‍♂️ - } +func QueryForCredentials(con *pop.Connection, where ...Where) (credentialsPerIdentity map[uuid.UUID](map[identity.CredentialsType]identity.Credentials), err error) { + // This query has been meticulously crafted to be as fast as possible. + // If you touch it, you will likely introduce a performance regression. q := con.Select( - "identity_credentials.id cred_id", - "identity_credentials.identity_id identity_id", - "identity_credentials.nid nid", - "ict.name cred_type", - "ict.id cred_type_id", "COALESCE(identity_credential_identifiers.identifier, '') cred_identifier", - "identity_credentials.config cred_config", - "identity_credentials.version cred_version", - "identity_credentials.created_at created_at", - "identity_credentials.updated_at updated_at", - ).InnerJoin( - "identity_credential_types ict", - "(identity_credentials.identity_credential_type_id = ict.id)", - ).LeftJoin( - ici, + "identity_credentials.id", + "identity_credentials.identity_credential_type_id", + "identity_credentials.identity_id", + "identity_credentials.nid", + "identity_credentials.config", + "identity_credentials.version", + "identity_credentials.created_at", + "identity_credentials.updated_at", + ).LeftJoin(identifiersTableNameWithIndexHint(con), "identity_credential_identifiers.identity_credential_id = identity_credentials.id AND identity_credential_identifiers.nid = identity_credentials.nid", + ).Order( + "identity_credentials.id ASC", ) for _, w := range where { q = q.Where("("+w.Condition+")", w.Args...) @@ -794,8 +766,16 @@ func QueryForCredentials(con *pop.Connection, where ...Where) (map[uuid.UUID](ma if err := q.All(&results); err != nil { return nil, sqlcon.HandleError(err) } - credentialsPerIdentity := map[uuid.UUID](map[identity.CredentialsType]identity.Credentials){} + + // assemble + credentialsPerIdentity = map[uuid.UUID](map[identity.CredentialsType]identity.Credentials){} for _, res := range results { + + res.Type, err = FindIdentityCredentialsTypeByID(con, res.IdentityCredentialTypeID) + if err != nil { + return nil, err + } + credentials, ok := credentialsPerIdentity[res.IdentityID] if !ok { credentialsPerIdentity[res.IdentityID] = make(map[identity.CredentialsType]identity.Credentials) @@ -808,20 +788,10 @@ func QueryForCredentials(con *pop.Connection, where ...Where) (map[uuid.UUID](ma if identifiers == nil { identifiers = make([]string, 0) } - c := identity.Credentials{ - ID: res.ID, - IdentityID: res.IdentityID, - NID: res.NID, - Type: res.Type, - IdentityCredentialTypeID: res.TypeID, - Identifiers: identifiers, - Config: res.Config, - Version: res.Version, - CreatedAt: res.CreatedAt, - UpdatedAt: res.UpdatedAt, - } - credentials[res.Type] = c + res.Identifiers = identifiers + credentials[res.Type] = res.Credentials } + // We need deterministic ordering for testing, but sorting in the // database can be expensive under certain circumstances. for _, creds := range credentialsPerIdentity { @@ -832,6 +802,21 @@ func QueryForCredentials(con *pop.Connection, where ...Where) (map[uuid.UUID](ma return credentialsPerIdentity, nil } +func identifiersTableNameWithIndexHint(con *pop.Connection) string { + ici := "identity_credential_identifiers" + switch con.Dialect.Name() { + case "cockroach": + ici += "@identity_credential_identifiers_nid_i_ici_idx" + case "sqlite3": + ici += " INDEXED BY identity_credential_identifiers_nid_i_ici_idx" + case "mysql": + ici += " USE INDEX(identity_credential_identifiers_nid_i_ici_idx)" + default: + // good luck 🤷‍♂️ + } + return ici +} + func paginationAttributes(params *identity.ListIdentityParameters, paginator *keysetpagination.Paginator) []attribute.KeyValue { attrs := []attribute.KeyValue{ attribute.StringSlice("expand", params.Expand.ToEager()), @@ -857,7 +842,7 @@ func (p *IdentityPersister) getCredentialTypeIDs(ctx context.Context, credential result := map[identity.CredentialsType]uuid.UUID{} for _, ct := range credentialTypes { - typeID, err := p.FindIdentityCredentialsTypeByName(ctx, ct) + typeID, err := FindIdentityCredentialsTypeByName(p.GetConnection(ctx), ct) if err != nil { return nil, err } @@ -992,7 +977,7 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. switch e { case identity.ExpandFieldCredentials: creds, err := QueryForCredentials(con, - Where{"identity_credentials.nid = ?", []any{nid}}, + Where{"identity_credentials.nid = ?", []interface{}{nid}}, Where{"identity_credentials.identity_id IN (?)", identityIDs}) if err != nil { return err @@ -1316,14 +1301,19 @@ func (p *IdentityPersister) InjectTraitsSchemaURL(ctx context.Context, i *identi return nil } -func (p *IdentityPersister) FindIdentityCredentialsTypeByID(ctx context.Context, id uuid.UUID) (identity.CredentialsType, error) { - result, found := p.credentialTypesID.Load(id) +var ( + credentialTypesID = x.NewSyncMap[uuid.UUID, identity.CredentialsType]() + credentialTypesName = x.NewSyncMap[identity.CredentialsType, uuid.UUID]() +) + +func FindIdentityCredentialsTypeByID(con *pop.Connection, id uuid.UUID) (identity.CredentialsType, error) { + result, found := credentialTypesID.Load(id) if !found { - if err := p.loadCredentialTypes(ctx); err != nil { + if err := loadCredentialTypes(con); err != nil { return "", err } - result, found = p.credentialTypesID.Load(id) + result, found = credentialTypesID.Load(id) } if !found { @@ -1333,14 +1323,14 @@ func (p *IdentityPersister) FindIdentityCredentialsTypeByID(ctx context.Context, return result, nil } -func (p *IdentityPersister) FindIdentityCredentialsTypeByName(ctx context.Context, ct identity.CredentialsType) (uuid.UUID, error) { - result, found := p.credentialTypesName.Load(ct) +func FindIdentityCredentialsTypeByName(con *pop.Connection, ct identity.CredentialsType) (uuid.UUID, error) { + result, found := credentialTypesName.Load(ct) if !found { - if err := p.loadCredentialTypes(ctx); err != nil { + if err := loadCredentialTypes(con); err != nil { return uuid.Nil, err } - result, found = p.credentialTypesName.Load(ct) + result, found = credentialTypesName.Load(ct) } if !found { @@ -1350,18 +1340,23 @@ func (p *IdentityPersister) FindIdentityCredentialsTypeByName(ctx context.Contex return result, nil } -func (p *IdentityPersister) loadCredentialTypes(ctx context.Context) (err error) { - ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.identity.loadCredentialTypes") +var mux sync.Mutex + +func loadCredentialTypes(con *pop.Connection) (err error) { + ctx, span := trace.SpanFromContext(con.Context()).TracerProvider().Tracer("").Start(con.Context(), "persistence.sql.identity.loadCredentialTypes") defer otelx.End(span, &err) + _ = ctx + mux.Lock() + defer mux.Unlock() var tt []identity.CredentialsTypeTable - if err := p.GetConnection(ctx).All(&tt); err != nil { + if err := con.WithContext(ctx).All(&tt); err != nil { return sqlcon.HandleError(err) } for _, t := range tt { - p.credentialTypesID.Store(t.ID, t.Name) - p.credentialTypesName.Store(t.Name, t.ID) + credentialTypesID.Store(t.ID, t.Name) + credentialTypesName.Store(t.Name, t.ID) } return nil diff --git a/persistence/sql/migratest/migration_test.go b/persistence/sql/migratest/migration_test.go index bf727683248b..bafe38c040d6 100644 --- a/persistence/sql/migratest/migration_test.go +++ b/persistence/sql/migratest/migration_test.go @@ -422,6 +422,7 @@ func testDatabase(t *testing.T, db string, c *pop.Connection) { }) }) - tm.DumpMigrations = false - require.NoError(t, tm.Down(ctx, -1)) + tm.DumpMigrations = false // true for debug + err = tm.Down(ctx, -1) // for easy breakpointing + require.NoError(t, err) } From 7784a606b8283c80ee73968e454430c5a958819b Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:56:19 +0000 Subject: [PATCH 054/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dca6dd2e7f2b..e291b8971f25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-10-30)](#2024-10-30) +- [ (2024-10-31)](#2024-10-31) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-30) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-31) ## Breaking Changes @@ -429,6 +429,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Fast add credential type lookups ([#4177](https://github.com/ory/kratos/issues/4177)) ([eeb1355](https://github.com/ory/kratos/commit/eeb13552118504f17b48f2c7e002e777f5ee73f4)) +- Improve QueryForCredentials + ([#4181](https://github.com/ory/kratos/issues/4181)) + ([ca0d6a7](https://github.com/ory/kratos/commit/ca0d6a7ea717495429b8bac7fd843ac69c1ebf16)) - Improve secondary indices for self service tables ([#4179](https://github.com/ory/kratos/issues/4179)) ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253)) From b2948044de4eee1841110162fe874055182bd2d2 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:09:18 +0100 Subject: [PATCH 055/158] feat: remove more unused indices (#4186) --- driver/registry_default.go | 6 +- go.mod | 11 ++-- go.sum | 23 ++++---- internal/client-go/go.sum | 1 + ...3900000001_identities.autocommit.down.sql} | 0 ...9153900000001_identities.autocommit.up.sql | 13 +++++ .../20241029153900000001_identities.up.sql | 13 ----- ...maining_unused_indices.autocommit.down.sql | 56 +++++++++++++++++++ ...remaining_unused_indices.autocommit.up.sql | 56 +++++++++++++++++++ ...g_unused_indices.mysql.autocommit.down.sql | 56 +++++++++++++++++++ ...ing_unused_indices.mysql.autocommit.up.sql | 56 +++++++++++++++++++ ...4100000002_foreign_key.autocommit.down.sql | 1 + ...094100000002_foreign_key.autocommit.up.sql | 1 + ...002_foreign_key.sqlite.autocommit.down.sql | 27 +++++++++ ...00002_foreign_key.sqlite.autocommit.up.sql | 37 ++++++++++++ request/builder.go | 4 +- selfservice/hook/web_hook.go | 2 +- .../strategy/oidc/strategy_registration.go | 2 +- selfservice/strategy/password/validator.go | 9 ++- session/tokenizer.go | 4 +- 20 files changed, 333 insertions(+), 45 deletions(-) rename persistence/sql/migrations/sql/{20241029153900000001_identities.down.sql => 20241029153900000001_identities.autocommit.down.sql} (100%) create mode 100644 persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql delete mode 100644 persistence/sql/migrations/sql/20241029153900000001_identities.up.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.up.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.up.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.up.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.up.sql diff --git a/driver/registry_default.go b/driver/registry_default.go index 6cdfb5bfe41f..fdf78f41f44e 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -12,6 +12,8 @@ import ( "testing" "time" + "github.com/lestrrat-go/jwx/jwk" + "github.com/ory/kratos/selfservice/strategy/idfirst" "github.com/cenkalti/backoff" @@ -874,13 +876,13 @@ func (m *RegistryDefault) Contextualizer() contextx.Contextualizer { func (m *RegistryDefault) JWKSFetcher() *jwksx.FetcherNext { if m.jwkFetcher == nil { maxItems := int64(10000000) - cache, _ := ristretto.NewCache(&ristretto.Config{ + cache, _ := ristretto.NewCache(&ristretto.Config[[]byte, jwk.Set]{ NumCounters: maxItems * 10, MaxCost: maxItems, BufferItems: 64, Metrics: true, IgnoreInternalCost: true, - Cost: func(value interface{}) int64 { + Cost: func(value jwk.Set) int64 { return 1 }, }) diff --git a/go.mod b/go.mod index 61009dc01832..2a2b2e00ed7b 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/cortesi/modd v0.8.1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/dghubble/oauth1 v0.7.3 - github.com/dgraph-io/ristretto v0.1.1 + github.com/dgraph-io/ristretto v1.0.0 github.com/fatih/color v1.17.0 github.com/ghodss/yaml v1.0.0 github.com/go-crypt/crypt v0.2.25 @@ -70,7 +70,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.660 + github.com/ory/x v0.0.665-0.20241031130226-ae5097122246 github.com/peterhellberg/link v1.2.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 @@ -138,7 +138,7 @@ require ( github.com/docker/docker v27.1.1+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect @@ -179,7 +179,6 @@ require ( github.com/goccy/go-yaml v1.11.3 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-tpm v0.9.1 // indirect @@ -229,7 +228,7 @@ require ( github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/jwx v1.2.29 // indirect + github.com/lestrrat-go/jwx v1.2.29 github.com/lestrrat-go/option v1.0.1 // indirect github.com/lib/pq v1.10.9 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -307,7 +306,7 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sys v0.22.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/tools v0.23.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect diff --git a/go.sum b/go.sum index 8036e25d5bec..97de7e85e1e2 100644 --- a/go.sum +++ b/go.sum @@ -128,10 +128,10 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnN github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE= github.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY= -github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= -github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgraph-io/ristretto v1.0.0 h1:SYG07bONKMlFDUYu5pEu3DGAh8c2OFNzKm6G9J4Si84= +github.com/dgraph-io/ristretto v1.0.0/go.mod h1:jTi2FiYEhQ1NsMmA7DeBykizjOuY88NhKBkepyu1jPc= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v26.1.4+incompatible h1:I8PHdc0MtxEADqYJZvhBrW9bo8gawKwwenxRM7/rLu8= @@ -142,8 +142,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -287,8 +287,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 h1:xisWqjiKEff2B0KfFYGpCqc3M3zdTz+OHQHRc09FeYk= github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -644,8 +642,8 @@ github.com/ory/pop/v6 v6.2.0 h1:hRFOGAOEHw91kUHQ32k5NHqCkcHrRou/romvrJP1w0E= github.com/ory/pop/v6 v6.2.0/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/ory/x v0.0.660 h1:mEZjmVtPY5grN3bmuSPkJBTK7xSNepzy0bCmVOCLZxU= -github.com/ory/x v0.0.660/go.mod h1:tS0FyZXpVeKd1lCcFgV/Rb1GlccI/Xq8DraFS+lmIt8= +github.com/ory/x v0.0.665-0.20241031130226-ae5097122246 h1:h6Jt8glJkehgQxr7MP3q5gmR4Ub0RWqlVgPXTnJU5rs= +github.com/ory/x v0.0.665-0.20241031130226-ae5097122246/go.mod h1:7SCTki3N0De3ZpqlxhxU/94ZrOCfNEnXwVtd0xVt+L8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -1043,7 +1041,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1052,8 +1049,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.down.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.down.sql similarity index 100% rename from persistence/sql/migrations/sql/20241029153900000001_identities.down.sql rename to persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.down.sql diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql new file mode 100644 index 000000000000..c8340636bf40 --- /dev/null +++ b/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql @@ -0,0 +1,13 @@ +DROP INDEX identities_id_nid_idx; + +CREATE INDEX identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses(identity_id ASC); +DROP INDEX identity_recovery_addresses_status_via_idx; +DROP INDEX identity_recovery_addresses_nid_identity_id_idx; +DROP INDEX identity_recovery_addresses_nid_id_idx; +DROP INDEX identity_recovery_addresses_id_nid_idx; + +CREATE INDEX identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC); +DROP INDEX identity_verifiable_addresses_status_via_idx; +DROP INDEX identity_verifiable_addresses_nid_identity_id_idx; +DROP INDEX identity_verifiable_addresses_nid_id_idx; +DROP INDEX identity_verifiable_addresses_id_nid_idx; diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.up.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.up.sql deleted file mode 100644 index 5b117cf05091..000000000000 --- a/persistence/sql/migrations/sql/20241029153900000001_identities.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -DROP INDEX IF EXISTS identities_id_nid_idx; - -CREATE INDEX IF NOT EXISTS identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses(identity_id ASC); -DROP INDEX IF EXISTS identity_recovery_addresses_status_via_idx; -DROP INDEX IF EXISTS identity_recovery_addresses_nid_identity_id_idx; -DROP INDEX IF EXISTS identity_recovery_addresses_nid_id_idx; -DROP INDEX IF EXISTS identity_recovery_addresses_id_nid_idx; - -CREATE INDEX IF NOT EXISTS identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC); -DROP INDEX IF EXISTS identity_verifiable_addresses_status_via_idx; -DROP INDEX IF EXISTS identity_verifiable_addresses_nid_identity_id_idx; -DROP INDEX IF EXISTS identity_verifiable_addresses_nid_id_idx; -DROP INDEX IF EXISTS identity_verifiable_addresses_id_nid_idx; diff --git a/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.down.sql b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.down.sql new file mode 100644 index 000000000000..487f44fb21a1 --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.down.sql @@ -0,0 +1,56 @@ +CREATE INDEX IF NOT EXISTS session_devices_id_nid_idx ON session_devices (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS session_devices_session_id_nid_idx ON session_devices (session_id ASC, nid ASC); +DROP INDEX IF EXISTS session_devices_nid_idx; +DROP INDEX IF EXISTS session_devices_session_id_idx; + +CREATE INDEX IF NOT EXISTS session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code ASC, nid ASC); +CREATE INDEX IF NOT EXISTS session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id ASC, nid ASC); +DROP INDEX IF EXISTS session_token_exchanges_flow_id_nid_init_code_idx; +DROP INDEX IF EXISTS session_token_exchanges_nid_init_code_idx; + +CREATE INDEX IF NOT EXISTS courier_messages_status_idx ON courier_messages (status ASC); +CREATE INDEX IF NOT EXISTS courier_messages_nid_id_idx ON courier_messages (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS courier_messages_id_nid_idx ON courier_messages (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS courier_messages_nid_created_at_id_idx ON courier_messages (nid ASC, created_at DESC); +DROP INDEX IF EXISTS courier_messages_status_id_idx; +DROP INDEX IF EXISTS courier_messages_nid_id_created_at_idx; + +CREATE INDEX IF NOT EXISTS continuity_containers_nid_id_idx ON continuity_containers (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS continuity_containers_id_nid_idx ON continuity_containers (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS continuity_containers_identity_id_nid_idx ON continuity_containers (identity_id ASC, nid ASC); +DROP INDEX IF EXISTS continuity_containers_identity_id_idx; +DROP INDEX IF EXISTS continuity_containers_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_verification_codes_nid_flow_id_idx ON identity_verification_codes (nid ASC, selfservice_verification_flow_id ASC); +CREATE INDEX IF NOT EXISTS identity_verification_codes_id_nid_idx ON identity_verification_codes (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes (identity_verifiable_address_id ASC, nid ASC); +DROP INDEX IF EXISTS identity_verification_codes_identity_verifiable_address_id_idx; +DROP INDEX IF EXISTS identity_verification_codes_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid ASC, token ASC, used ASC, selfservice_verification_flow_id ASC); +DROP INDEX IF EXISTS identity_verification_tokens_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_registration_codes_nid_flow_id_idx ON identity_registration_codes (nid ASC, selfservice_registration_flow_id ASC); +CREATE INDEX IF NOT EXISTS identity_registration_codes_id_nid_idx ON identity_registration_codes (id ASC, nid ASC); +DROP INDEX IF EXISTS identity_registration_codes_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens (nid ASC, id ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_tokens_token_nid_used_idx ON identity_recovery_tokens (nid ASC, token ASC, used ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_tokens_identity_id_nid_idx ON identity_recovery_tokens (identity_id ASC, nid ASC); +DROP INDEX IF EXISTS identity_recovery_tokens_identity_id_idx; +DROP INDEX IF EXISTS identity_recovery_tokens_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes (nid ASC, selfservice_recovery_flow_id ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_codes_id_nid_idx ON identity_recovery_codes (id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes (identity_id ASC, nid ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes (identity_recovery_address_id ASC, nid ASC); +DROP INDEX IF EXISTS identity_recovery_codes_identity_recovery_address_id_idx; +DROP INDEX IF EXISTS identity_recovery_codes_identity_id_idx; +DROP INDEX IF EXISTS identity_recovery_codes_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_login_codes_nid_flow_id_idx ON identity_login_codes (nid ASC, selfservice_login_flow_id ASC); +CREATE INDEX IF NOT EXISTS identity_login_codes_id_nid_idx ON identity_login_codes (id ASC, nid ASC); +DROP INDEX IF EXISTS identity_login_codes_nid_idx; diff --git a/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.up.sql b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.up.sql new file mode 100644 index 000000000000..1e9ffae8c664 --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.up.sql @@ -0,0 +1,56 @@ +CREATE INDEX IF NOT EXISTS session_devices_nid_idx ON session_devices (nid ASC); +CREATE INDEX IF NOT EXISTS session_devices_session_id_idx ON session_devices (session_id ASC); +DROP INDEX IF EXISTS session_devices_id_nid_idx; +DROP INDEX IF EXISTS session_devices_session_id_nid_idx; + +CREATE INDEX IF NOT EXISTS session_token_exchanges_flow_id_nid_init_code_idx ON session_token_exchanges (flow_id ASC, nid ASC, init_code ASC); +CREATE INDEX IF NOT EXISTS session_token_exchanges_nid_init_code_idx ON session_token_exchanges (nid ASC, init_code ASC); +DROP INDEX IF EXISTS session_token_exchanges_nid_code_idx; +DROP INDEX IF EXISTS session_token_exchanges_nid_flow_id_idx; + +CREATE INDEX IF NOT EXISTS courier_messages_status_id_idx ON courier_messages (status ASC, id ASC); +CREATE INDEX IF NOT EXISTS courier_messages_nid_id_created_at_idx ON courier_messages (nid ASC, id ASC, created_at DESC); +DROP INDEX IF EXISTS courier_messages_status_idx; +DROP INDEX IF EXISTS courier_messages_nid_id_idx; +DROP INDEX IF EXISTS courier_messages_id_nid_idx; +DROP INDEX IF EXISTS courier_messages_nid_created_at_id_idx; + +CREATE INDEX IF NOT EXISTS continuity_containers_identity_id_idx ON continuity_containers (identity_id ASC); +CREATE INDEX IF NOT EXISTS continuity_containers_nid_idx ON continuity_containers (nid ASC); +DROP INDEX IF EXISTS continuity_containers_nid_id_idx; +DROP INDEX IF EXISTS continuity_containers_id_nid_idx; +DROP INDEX IF EXISTS continuity_containers_identity_id_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_verification_codes_identity_verifiable_address_id_idx ON identity_verification_codes (identity_verifiable_address_id ASC); +CREATE INDEX IF NOT EXISTS identity_verification_codes_nid_idx ON identity_verification_codes (nid ASC); +DROP INDEX IF EXISTS identity_verification_codes_nid_flow_id_idx; +DROP INDEX IF EXISTS identity_verification_codes_id_nid_idx; +DROP INDEX IF EXISTS identity_verification_codes_verifiable_address_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_verification_tokens_nid_idx ON identity_verification_tokens (nid ASC); +DROP INDEX IF EXISTS identity_verification_tokens_nid_id_idx; +DROP INDEX IF EXISTS identity_verification_tokens_id_nid_idx; +DROP INDEX IF EXISTS identity_verification_tokens_token_nid_used_flow_id_idx; + +CREATE INDEX IF NOT EXISTS identity_registration_codes_nid_idx ON identity_registration_codes (nid ASC); +DROP INDEX IF EXISTS identity_registration_codes_nid_flow_id_idx; +DROP INDEX IF EXISTS identity_registration_codes_id_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_recovery_tokens_identity_id_idx ON identity_recovery_tokens (identity_id ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_tokens_nid_idx ON identity_recovery_tokens (nid ASC); +DROP INDEX IF EXISTS identity_recovery_tokens_nid_id_idx; +DROP INDEX IF EXISTS identity_recovery_tokens_id_nid_idx; +DROP INDEX IF EXISTS identity_recovery_tokens_token_nid_used_idx; +DROP INDEX IF EXISTS identity_recovery_tokens_identity_id_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_recovery_address_id_idx ON identity_recovery_codes (identity_recovery_address_id ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_id_idx ON identity_recovery_codes (identity_id ASC); +CREATE INDEX IF NOT EXISTS identity_recovery_codes_nid_idx ON identity_recovery_codes (nid ASC); +DROP INDEX IF EXISTS identity_recovery_codes_nid_flow_id_idx; +DROP INDEX IF EXISTS identity_recovery_codes_id_nid_idx; +DROP INDEX IF EXISTS identity_recovery_codes_identity_id_nid_idx; +DROP INDEX IF EXISTS identity_recovery_codes_identity_recovery_address_id_nid_idx; + +CREATE INDEX IF NOT EXISTS identity_login_codes_nid_idx ON identity_login_codes (nid ASC); +DROP INDEX IF EXISTS identity_login_codes_nid_flow_id_idx; +DROP INDEX IF EXISTS identity_login_codes_id_nid_idx; diff --git a/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.down.sql b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.down.sql new file mode 100644 index 000000000000..61d763673656 --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.down.sql @@ -0,0 +1,56 @@ +CREATE INDEX session_devices_id_nid_idx ON session_devices (nid ASC, id ASC); -- the original index is id, nid - but then we can't drop session_devices_nid_idx +CREATE INDEX session_devices_session_id_nid_idx ON session_devices (session_id ASC, nid ASC); +DROP INDEX session_devices_nid_idx ON session_devices; +DROP INDEX session_devices_session_id_idx ON session_devices; + +CREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code ASC, nid ASC); +CREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id ASC, nid ASC); +DROP INDEX session_token_exchanges_flow_id_nid_init_code_idx ON session_token_exchanges; +DROP INDEX session_token_exchanges_nid_init_code_idx ON session_token_exchanges; + +CREATE INDEX courier_messages_status_idx ON courier_messages (status ASC); +CREATE INDEX courier_messages_nid_id_idx ON courier_messages (nid ASC, id ASC); +CREATE INDEX courier_messages_id_nid_idx ON courier_messages (id ASC, nid ASC); +CREATE INDEX courier_messages_nid_created_at_id_idx ON courier_messages (nid ASC, created_at DESC); +DROP INDEX courier_messages_status_id_idx ON courier_messages; +DROP INDEX courier_messages_nid_id_created_at_idx ON courier_messages; + +CREATE INDEX continuity_containers_nid_id_idx ON continuity_containers (nid ASC, id ASC); +CREATE INDEX continuity_containers_id_nid_idx ON continuity_containers (id ASC, nid ASC); +CREATE INDEX continuity_containers_identity_id_nid_idx ON continuity_containers (identity_id ASC, nid ASC); +DROP INDEX continuity_containers_identity_id_idx ON continuity_containers; +DROP INDEX continuity_containers_nid_idx ON continuity_containers; + +CREATE INDEX identity_verification_codes_nid_flow_id_idx ON identity_verification_codes (nid ASC, selfservice_verification_flow_id ASC); +CREATE INDEX identity_verification_codes_id_nid_idx ON identity_verification_codes (id ASC, nid ASC); +CREATE INDEX identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes (identity_verifiable_address_id ASC, nid ASC); +DROP INDEX identity_verification_codes_verifiable_address_idx ON identity_verification_codes; +DROP INDEX identity_verification_codes_nid_idx ON identity_verification_codes; + +CREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid ASC, id ASC); +CREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id ASC, nid ASC); +CREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid ASC, token ASC, used ASC, selfservice_verification_flow_id ASC); +DROP INDEX identity_verification_tokens_nid_idx ON identity_verification_tokens; + +CREATE INDEX identity_registration_codes_nid_flow_id_idx ON identity_registration_codes (nid ASC, selfservice_registration_flow_id ASC); +CREATE INDEX identity_registration_codes_id_nid_idx ON identity_registration_codes (id ASC, nid ASC); +DROP INDEX identity_registration_codes_nid_idx ON identity_registration_codes; + +CREATE INDEX identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens (nid ASC, id ASC); +CREATE INDEX identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens (id ASC, nid ASC); +CREATE INDEX identity_recovery_tokens_token_nid_used_idx ON identity_recovery_tokens (nid ASC, token ASC, used ASC); +CREATE INDEX identity_recovery_tokens_identity_id_nid_idx ON identity_recovery_tokens (identity_id ASC, nid ASC); +DROP INDEX identity_recovery_tokens_identity_id_idx ON identity_recovery_tokens; +DROP INDEX identity_recovery_tokens_nid_idx ON identity_recovery_tokens; + +CREATE INDEX identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes (nid ASC, selfservice_recovery_flow_id ASC); +CREATE INDEX identity_recovery_codes_id_nid_idx ON identity_recovery_codes (id ASC, nid ASC); +CREATE INDEX identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes (identity_id ASC, nid ASC); +CREATE INDEX identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes (identity_recovery_address_id ASC, nid ASC); +DROP INDEX identity_recovery_codes_address_id_idx ON identity_recovery_codes; +DROP INDEX identity_recovery_codes_identity_id_idx ON identity_recovery_codes; +DROP INDEX identity_recovery_codes_nid_idx ON identity_recovery_codes; + +CREATE INDEX identity_login_codes_nid_flow_id_idx ON identity_login_codes (nid ASC, selfservice_login_flow_id ASC); +CREATE INDEX identity_login_codes_id_nid_idx ON identity_login_codes (id ASC, nid ASC); +DROP INDEX identity_login_codes_nid_idx ON identity_login_codes; diff --git a/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.up.sql b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.up.sql new file mode 100644 index 000000000000..1f6a636b74bf --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.autocommit.up.sql @@ -0,0 +1,56 @@ +CREATE INDEX session_devices_nid_idx ON session_devices (nid ASC); +CREATE INDEX session_devices_session_id_idx ON session_devices (session_id ASC); +DROP INDEX session_devices_id_nid_idx ON session_devices; +DROP INDEX session_devices_session_id_nid_idx ON session_devices; + +CREATE INDEX session_token_exchanges_flow_id_nid_init_code_idx ON session_token_exchanges (flow_id ASC, nid ASC, init_code ASC); +CREATE INDEX session_token_exchanges_nid_init_code_idx ON session_token_exchanges (nid ASC, init_code ASC); +DROP INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges; +DROP INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges; + +CREATE INDEX courier_messages_status_id_idx ON courier_messages (status ASC, id ASC); +CREATE INDEX courier_messages_nid_id_created_at_idx ON courier_messages (nid ASC, id ASC, created_at DESC); +DROP INDEX courier_messages_status_idx ON courier_messages; +DROP INDEX courier_messages_nid_id_idx ON courier_messages; +DROP INDEX courier_messages_id_nid_idx ON courier_messages; +DROP INDEX courier_messages_nid_created_at_id_idx ON courier_messages; + +CREATE INDEX continuity_containers_identity_id_idx ON continuity_containers (identity_id ASC); +CREATE INDEX continuity_containers_nid_idx ON continuity_containers (nid ASC); +DROP INDEX continuity_containers_nid_id_idx ON continuity_containers; +DROP INDEX continuity_containers_id_nid_idx ON continuity_containers; +DROP INDEX continuity_containers_identity_id_nid_idx ON continuity_containers; + +CREATE INDEX identity_verification_codes_verifiable_address_idx ON identity_verification_codes (identity_verifiable_address_id ASC); +CREATE INDEX identity_verification_codes_nid_idx ON identity_verification_codes (nid ASC); +DROP INDEX identity_verification_codes_nid_flow_id_idx ON identity_verification_codes; +DROP INDEX identity_verification_codes_id_nid_idx ON identity_verification_codes; +DROP INDEX identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes; + +CREATE INDEX identity_verification_tokens_nid_idx ON identity_verification_tokens (nid ASC); +DROP INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens; +DROP INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens; +DROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens; + +CREATE INDEX identity_registration_codes_nid_idx ON identity_registration_codes (nid ASC); +DROP INDEX identity_registration_codes_nid_flow_id_idx ON identity_registration_codes; +DROP INDEX identity_registration_codes_id_nid_idx ON identity_registration_codes; + +CREATE INDEX identity_recovery_tokens_identity_id_idx ON identity_recovery_tokens (identity_id ASC); +CREATE INDEX identity_recovery_tokens_nid_idx ON identity_recovery_tokens (nid ASC); +DROP INDEX identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens; +DROP INDEX identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens; +DROP INDEX identity_recovery_tokens_token_nid_used_idx ON identity_recovery_tokens; +DROP INDEX identity_recovery_tokens_identity_id_nid_idx ON identity_recovery_tokens; + +CREATE INDEX identity_recovery_codes_address_id_idx ON identity_recovery_codes (identity_recovery_address_id ASC); +CREATE INDEX identity_recovery_codes_identity_id_idx ON identity_recovery_codes (identity_id ASC); +CREATE INDEX identity_recovery_codes_nid_idx ON identity_recovery_codes (nid ASC); +DROP INDEX identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes; +DROP INDEX identity_recovery_codes_id_nid_idx ON identity_recovery_codes; +DROP INDEX identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes; +DROP INDEX identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes; + +CREATE INDEX identity_login_codes_nid_idx ON identity_login_codes (nid ASC); +DROP INDEX identity_login_codes_nid_flow_id_idx ON identity_login_codes; +DROP INDEX identity_login_codes_id_nid_idx ON identity_login_codes; diff --git a/persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.down.sql b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.down.sql new file mode 100644 index 000000000000..b31030e3c951 --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.down.sql @@ -0,0 +1 @@ +ALTER TABLE session_token_exchanges DROP CONSTRAINT session_token_exchanges_nid_fk; diff --git a/persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.up.sql b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.up.sql new file mode 100644 index 000000000000..1180d4337076 --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.up.sql @@ -0,0 +1 @@ +ALTER TABLE session_token_exchanges ADD CONSTRAINT session_token_exchanges_nid_fk FOREIGN KEY (nid) REFERENCES networks (id) ON DELETE CASCADE; diff --git a/persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.down.sql b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.down.sql new file mode 100644 index 000000000000..f0d12fdfea4e --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.down.sql @@ -0,0 +1,27 @@ +-- Step 1: Create a temporary table without the nid column and foreign key constraint +CREATE TABLE session_token_exchanges_temp +( + id TEXT NOT NULL, + flow_id TEXT NOT NULL, + session_id TEXT, + init_code VARCHAR(64) NOT NULL, + return_to_code VARCHAR(64) NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, + PRIMARY KEY (id) +); + +-- Step 2: Copy data from the original table to the temporary table (excluding the nid column) +INSERT INTO session_token_exchanges_temp (id, flow_id, session_id, init_code, return_to_code, created_at, updated_at) +SELECT id, flow_id, session_id, init_code, return_to_code, created_at, updated_at +FROM session_token_exchanges; + +-- Step 3: Drop the original table +DROP TABLE session_token_exchanges; + +-- Step 4: Rename the temporary table to the original table name +ALTER TABLE session_token_exchanges_temp RENAME TO session_token_exchanges; + +-- Step 5: Recreate indexes as needed (excluding nid) +CREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code); +CREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id); diff --git a/persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.up.sql b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.up.sql new file mode 100644 index 000000000000..210f7f9034b4 --- /dev/null +++ b/persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.autocommit.up.sql @@ -0,0 +1,37 @@ +-- Step 1: Create a temporary table with the new column and foreign key constraint +CREATE TABLE session_token_exchanges_temp +( + id TEXT NOT NULL, + nid TEXT NOT NULL, + flow_id TEXT NOT NULL, + session_id TEXT, + init_code VARCHAR(64) NOT NULL, + return_to_code VARCHAR(64) NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (nid) REFERENCES networks (id) ON DELETE CASCADE +); + +-- Step 2: Copy data from the original table to the temporary table +INSERT INTO session_token_exchanges_temp (id, nid, flow_id, session_id, init_code, return_to_code, created_at, + updated_at) +SELECT id, + nid, + flow_id, + session_id, + init_code, + return_to_code, + created_at, + updated_at +FROM session_token_exchanges; + +-- Step 3: Drop the original table +DROP TABLE session_token_exchanges; + +-- Step 4: Rename the temporary table to the original table name +ALTER TABLE session_token_exchanges_temp RENAME TO session_token_exchanges; + +-- Step 5: Recreate indexes as needed +CREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code, nid); +CREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id, nid); diff --git a/request/builder.go b/request/builder.go index 4651f64d43e2..5d219a37f336 100644 --- a/request/builder.go +++ b/request/builder.go @@ -44,11 +44,11 @@ type ( r *retryablehttp.Request Config *Config deps Dependencies - cache *ristretto.Cache + cache *ristretto.Cache[[]byte, []byte] } ) -func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies, jsonnetCache *ristretto.Cache) (_ *Builder, err error) { +func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies, jsonnetCache *ristretto.Cache[[]byte, []byte]) (_ *Builder, err error) { _, span := deps.Tracer(ctx).Tracer().Start(ctx, "request.NewBuilder") defer otelx.End(span, &err) diff --git a/selfservice/hook/web_hook.go b/selfservice/hook/web_hook.go index 6773e9ec4b89..255d50605d99 100644 --- a/selfservice/hook/web_hook.go +++ b/selfservice/hook/web_hook.go @@ -62,7 +62,7 @@ var _ interface { settings.PostHookPostPersistExecutor } = (*WebHook)(nil) -var jsonnetCache, _ = ristretto.NewCache(&ristretto.Config{ +var jsonnetCache, _ = ristretto.NewCache(&ristretto.Config[[]byte, []byte]{ MaxCost: 100 << 20, // 100MB, NumCounters: 1_000_000, // 1kB per snippet -> 100k snippets -> 1M counters BufferItems: 64, diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index 3cac2b931d5c..82737df36a9d 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -35,7 +35,7 @@ import ( var _ registration.Strategy = new(Strategy) -var jsonnetCache, _ = ristretto.NewCache(&ristretto.Config{ +var jsonnetCache, _ = ristretto.NewCache(&ristretto.Config[[]byte, []byte]{ MaxCost: 100 << 20, // 100MB, NumCounters: 1_000_000, // 1kB per snippet -> 100k snippets -> 1M counters BufferItems: 64, diff --git a/selfservice/strategy/password/validator.go b/selfservice/strategy/password/validator.go index be1aac3c3497..688cd53f896b 100644 --- a/selfservice/strategy/password/validator.go +++ b/selfservice/strategy/password/validator.go @@ -62,7 +62,7 @@ var ( type DefaultPasswordValidator struct { reg validatorDependencies Client *retryablehttp.Client - hashes *ristretto.Cache + hashes *ristretto.Cache[string, int64] minIdentifierPasswordDist int maxIdentifierPasswordSubstrThreshold float32 @@ -73,7 +73,7 @@ type validatorDependencies interface { } func NewDefaultPasswordValidatorStrategy(reg validatorDependencies) (*DefaultPasswordValidator, error) { - cache, err := ristretto.NewCache(&ristretto.Config{ + cache, err := ristretto.NewCache(&ristretto.Config[string, int64]{ NumCounters: 10 * 10000, MaxCost: 60 * 10000, // BCrypt hash size is 60 bytes BufferItems: 64, @@ -215,9 +215,8 @@ func (s *DefaultPasswordValidator) validate(ctx context.Context, identifier, pas } } - v, ok := c.(int64) - if ok && v > int64(s.reg.Config().PasswordPolicyConfig(ctx).MaxBreaches) { - return text.NewErrorValidationPasswordTooManyBreaches(v) + if c > int64(s.reg.Config().PasswordPolicyConfig(ctx).MaxBreaches) { + return text.NewErrorValidationPasswordTooManyBreaches(c) } return nil diff --git a/session/tokenizer.go b/session/tokenizer.go index 85533f6e397e..721c94a54985 100644 --- a/session/tokenizer.go +++ b/session/tokenizer.go @@ -36,7 +36,7 @@ type ( Tokenizer struct { r tokenizerDependencies nowFunc func() time.Time - cache *ristretto.Cache + cache *ristretto.Cache[[]byte, []byte] } TokenizerProvider interface { SessionTokenizer() *Tokenizer @@ -44,7 +44,7 @@ type ( ) func NewTokenizer(r tokenizerDependencies) *Tokenizer { - cache, _ := ristretto.NewCache(&ristretto.Config{ + cache, _ := ristretto.NewCache(&ristretto.Config[[]byte, []byte]{ MaxCost: 50 << 20, // 50MB, NumCounters: 500_000, // 1kB per snippet -> 50k snippets -> 500k counters BufferItems: 64, From 25f17e7a9a9c8ae466043fb4642ae58683fa0adb Mon Sep 17 00:00:00 2001 From: Andreas Bucksteeg Date: Thu, 31 Oct 2024 23:09:50 +0100 Subject: [PATCH 056/158] ci: fix rate-limit trivy issue (#4184) --- .github/workflows/cve-scan.yaml | 49 ++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/.github/workflows/cve-scan.yaml b/.github/workflows/cve-scan.yaml index 0ddfe3a915de..28e88e24fd28 100644 --- a/.github/workflows/cve-scan.yaml +++ b/.github/workflows/cve-scan.yaml @@ -1,5 +1,6 @@ name: Docker Image Scanners on: + workflow_dispatch: push: branches: - "master" @@ -9,27 +10,47 @@ on: branches: - "master" +permissions: + contents: read + security-events: write + jobs: scanners: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Env id: vars shell: bash run: | echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> "${GITHUB_ENV}" - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Build images shell: bash run: | IMAGE_TAG="${{ env.SHA_SHORT }}" make docker + + # Add GitHub authentication for Trivy + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Configure Trivy + - name: Configure Trivy + run: | + mkdir -p $HOME/.cache/trivy + echo "TRIVY_USERNAME=${{ github.actor }}" >> $GITHUB_ENV + echo "TRIVY_PASSWORD=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV + - name: Anchore Scanner - uses: anchore/scan-action@v3 + uses: anchore/scan-action@v5 id: grype-scan with: image: oryd/kratos:${{ env.SHA_SHORT }} @@ -45,18 +66,10 @@ jobs: echo "::endgroup::" - name: Anchore upload scan SARIF report if: always() - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.grype-scan.outputs.sarif }} - # - name: Kubescape scanner - # uses: kubescape/github-action@main - # id: kubescape - # with: - # verbose: true - # format: pretty-printer - # # can't whitelist CVE yet: https://github.com/kubescape/kubescape/pull/1568 - # image: oryd/kratos:${{ env.SHA_SHORT }} - # severityThreshold: critical + - name: Trivy Scanner uses: aquasecurity/trivy-action@master if: ${{ always() }} @@ -67,9 +80,13 @@ jobs: ignore-unfixed: true vuln-type: "os,library" severity: "CRITICAL,HIGH" - scanners: "vuln,secret,config" + scanners: "vuln,secret,misconfig" + env: + TRIVY_SKIP_JAVA_DB_UPDATE: "true" + TRIVY_DISABLE_VEX_NOTICE: "true" + - name: Dockle Linter - uses: erzz/dockle-action@v1.3.2 + uses: erzz/dockle-action@v1 if: ${{ always() }} with: image: oryd/kratos:${{ env.SHA_SHORT }} From 49104602fd9171af8da3e2e1fb867fc56a1306c4 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:11:45 +0000 Subject: [PATCH 057/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 5665f204329eef07064d919d552ce497b0d97cf8 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:03:42 +0000 Subject: [PATCH 058/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e291b8971f25..48957ec6d907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -452,6 +452,10 @@ https://github.com/ory-corp/cloud/issues/7176 Closes https://github.com/ory/kratos/issues/1570 Closes https://github.com/ory/kratos/issues/3779 +- Remove more unused indices + ([#4186](https://github.com/ory/kratos/issues/4186)) + ([b294804](https://github.com/ory/kratos/commit/b2948044de4eee1841110162fe874055182bd2d2)) + ### Tests - Update snapshots ([#4167](https://github.com/ory/kratos/issues/4167)) From 234b6f2f6435c62b7e161c032b888c4e2b3328d4 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:09:42 +0100 Subject: [PATCH 059/158] feat: load session only once when middleware is used (#4187) --- selfservice/flow/settings/handler.go | 8 ++++---- session/handler.go | 13 +++++++++++-- session/manager.go | 3 +++ session/manager_http.go | 11 +++++++++++ session/manager_http_test.go | 14 ++++++++++++++ 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go index 6232f47b7a9b..bbf3a9010ee6 100644 --- a/selfservice/flow/settings/handler.go +++ b/selfservice/flow/settings/handler.go @@ -214,7 +214,7 @@ type createNativeSettingsFlow struct { // default: errorGeneric func (h *Handler) createNativeSettingsFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { ctx := r.Context() - s, err := h.d.SessionManager().FetchFromRequest(ctx, r) + s, err := h.d.SessionManager().FetchFromRequestContext(ctx, r) if err != nil { h.d.Writer().WriteError(w, r, err) return @@ -298,7 +298,7 @@ type createBrowserSettingsFlow struct { // default: errorGeneric func (h *Handler) createBrowserSettingsFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { ctx := r.Context() - s, err := h.d.SessionManager().FetchFromRequest(ctx, r) + s, err := h.d.SessionManager().FetchFromRequestContext(ctx, r) if err != nil { h.d.SelfServiceErrorManager().Forward(ctx, w, r, err) return @@ -404,7 +404,7 @@ func (h *Handler) getSettingsFlow(w http.ResponseWriter, r *http.Request, _ http return } - sess, err := h.d.SessionManager().FetchFromRequest(ctx, r) + sess, err := h.d.SessionManager().FetchFromRequestContext(ctx, r) if err != nil { h.d.Writer().WriteError(w, r, err) return @@ -574,7 +574,7 @@ func (h *Handler) updateSettingsFlow(w http.ResponseWriter, r *http.Request, ps return } - ss, err := h.d.SessionManager().FetchFromRequest(ctx, r) + ss, err := h.d.SessionManager().FetchFromRequestContext(ctx, r) if err != nil { h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, nil, err) return diff --git a/session/handler.go b/session/handler.go index 8b8cd84e3bd8..ad10e389af99 100644 --- a/session/handler.go +++ b/session/handler.go @@ -4,6 +4,7 @@ package session import ( + "context" "fmt" "net/http" "strconv" @@ -837,9 +838,17 @@ func (h *Handler) listMySessions(w http.ResponseWriter, r *http.Request, _ httpr h.r.Writer().Write(w, r, sess) } +type sessionInContext int + +const ( + sessionInContextKey sessionInContext = iota +) + func (h *Handler) IsAuthenticated(wrap httprouter.Handle, onUnauthenticated httprouter.Handle) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if _, err := h.r.SessionManager().FetchFromRequest(r.Context(), r); err != nil { + ctx := r.Context() + sess, err := h.r.SessionManager().FetchFromRequest(ctx, r) + if err != nil { if onUnauthenticated != nil { onUnauthenticated(w, r, ps) return @@ -849,7 +858,7 @@ func (h *Handler) IsAuthenticated(wrap httprouter.Handle, onUnauthenticated http return } - wrap(w, r, ps) + wrap(w, r.WithContext(context.WithValue(ctx, sessionInContextKey, sess)), ps) } } diff --git a/session/manager.go b/session/manager.go index e44d91f9111f..e6409c21dfd9 100644 --- a/session/manager.go +++ b/session/manager.go @@ -133,6 +133,9 @@ type Manager interface { // FetchFromRequest creates an HTTP session using cookies. FetchFromRequest(context.Context, *http.Request) (*Session, error) + // FetchFromRequestContext returns the session from the context or if that is unset, falls back to FetchFromRequest. + FetchFromRequestContext(context.Context, *http.Request) (*Session, error) + // PurgeFromRequest removes an HTTP session. PurgeFromRequest(context.Context, http.ResponseWriter, *http.Request) error diff --git a/session/manager_http.go b/session/manager_http.go index b56a16be88ee..2b3ffc6aa1e4 100644 --- a/session/manager_http.go +++ b/session/manager_http.go @@ -227,6 +227,17 @@ func (s *ManagerHTTP) extractToken(r *http.Request) string { return token } +func (s *ManagerHTTP) FetchFromRequestContext(ctx context.Context, r *http.Request) (_ *Session, err error) { + ctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, "sessions.ManagerHTTP.FetchFromRequestContext") + otelx.End(span, &err) + + if sess, ok := ctx.Value(sessionInContextKey).(*Session); ok { + return sess, nil + } + + return s.FetchFromRequest(ctx, r) +} + func (s *ManagerHTTP) FetchFromRequest(ctx context.Context, r *http.Request) (_ *Session, err error) { ctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, "sessions.ManagerHTTP.FetchFromRequest") defer func() { diff --git a/session/manager_http_test.go b/session/manager_http_test.go index a2ca87893075..0bbfdd625461 100644 --- a/session/manager_http_test.go +++ b/session/manager_http_test.go @@ -244,6 +244,16 @@ func TestManagerHTTP(t *testing.T) { reg.Writer().Write(w, r, sess) }) + rp.GET("/session/get-middleware", reg.SessionHandler().IsAuthenticated(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + sess, err := reg.SessionManager().FetchFromRequestContext(r.Context(), r) + if err != nil { + t.Logf("Got error on lookup: %s %T", err, errors.Unwrap(err)) + reg.Writer().WriteError(w, r, err) + return + } + reg.Writer().Write(w, r, sess) + }, session.RedirectOnUnauthenticated("https://failed.com"))) + pts := httptest.NewServer(x.NewTestCSRFHandler(rp, reg)) t.Cleanup(pts.Close) conf.MustSet(ctx, config.ViperKeyPublicBaseURL, pts.URL) @@ -263,6 +273,10 @@ func TestManagerHTTP(t *testing.T) { res, err := c.Get(pts.URL + "/session/get") require.NoError(t, err) assert.EqualValues(t, http.StatusOK, res.StatusCode) + + res, err = c.Get(pts.URL + "/session/get-middleware") + require.NoError(t, err) + assert.EqualValues(t, http.StatusOK, res.StatusCode) }) t.Run("case=key rotation", func(t *testing.T) { From a313dd6ba6d823deb40f14c738e3b609dbaad56c Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:08:05 +0100 Subject: [PATCH 060/158] fix: add exists clause (#4191) --- ...9153900000001_identities.autocommit.up.sql | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql b/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql index c8340636bf40..faea35148340 100644 --- a/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql +++ b/persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql @@ -1,13 +1,13 @@ -DROP INDEX identities_id_nid_idx; +DROP INDEX IF EXISTS identities_id_nid_idx; -CREATE INDEX identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses(identity_id ASC); -DROP INDEX identity_recovery_addresses_status_via_idx; -DROP INDEX identity_recovery_addresses_nid_identity_id_idx; -DROP INDEX identity_recovery_addresses_nid_id_idx; -DROP INDEX identity_recovery_addresses_id_nid_idx; +CREATE INDEX IF NOT EXISTS identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses(identity_id ASC); +DROP INDEX IF EXISTS identity_recovery_addresses_status_via_idx; +DROP INDEX IF EXISTS identity_recovery_addresses_nid_identity_id_idx; +DROP INDEX IF EXISTS identity_recovery_addresses_nid_id_idx; +DROP INDEX IF EXISTS identity_recovery_addresses_id_nid_idx; -CREATE INDEX identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC); -DROP INDEX identity_verifiable_addresses_status_via_idx; -DROP INDEX identity_verifiable_addresses_nid_identity_id_idx; -DROP INDEX identity_verifiable_addresses_nid_id_idx; -DROP INDEX identity_verifiable_addresses_id_nid_idx; +CREATE INDEX IF NOT EXISTS identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC); +DROP INDEX IF EXISTS identity_verifiable_addresses_status_via_idx; +DROP INDEX IF EXISTS identity_verifiable_addresses_nid_identity_id_idx; +DROP INDEX IF EXISTS identity_verifiable_addresses_nid_id_idx; +DROP INDEX IF EXISTS identity_verifiable_addresses_id_nid_idx; From 215af5719f6ce6a35f8d37a82c6e6f502f2d0400 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:57:30 +0000 Subject: [PATCH 061/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48957ec6d907..4e6fa9c0e888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-10-31)](#2024-10-31) +- [ (2024-11-05)](#2024-11-05) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-10-31) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-05) ## Breaking Changes @@ -355,6 +355,8 @@ https://github.com/ory-corp/cloud/issues/7176 - Account linking should only happen after 2fa when required ([#4174](https://github.com/ory/kratos/issues/4174)) ([8e29b68](https://github.com/ory/kratos/commit/8e29b68a595d2ef18e48c2a01072335cefa36d86)) +- Add exists clause ([#4191](https://github.com/ory/kratos/issues/4191)) + ([a313dd6](https://github.com/ory/kratos/commit/a313dd6ba6d823deb40f14c738e3b609dbaad56c)) - Duplicate autocomplete trigger ([6bbf915](https://github.com/ory/kratos/commit/6bbf91593a37e4973a86f610290ebab44df8dc81)) - Enable b2b_sso hook in more places @@ -435,6 +437,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Improve secondary indices for self service tables ([#4179](https://github.com/ory/kratos/issues/4179)) ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253)) +- Load session only once when middleware is used + ([#4187](https://github.com/ory/kratos/issues/4187)) + ([234b6f2](https://github.com/ory/kratos/commit/234b6f2f6435c62b7e161c032b888c4e2b3328d4)) - Optimize identity-related secondary indices ([#4182](https://github.com/ory/kratos/issues/4182)) ([53874c1](https://github.com/ory/kratos/commit/53874c1753940e08e0bf50753a1d3126add77af1)) From 4a870a678dd3676abda7afc9803399dec4411b05 Mon Sep 17 00:00:00 2001 From: Henning Perl Date: Thu, 7 Nov 2024 09:59:46 +0100 Subject: [PATCH 062/158] fix: account linking with 2FA (#4188) This fixes some edge cases with OIDC account linking for accounts with 2FA enabled. --- internal/client-go/go.sum | 1 + selfservice/flow/duplicate_credentials.go | 2 - selfservice/flow/login/flow_test.go | 7 +-- selfservice/flow/login/handler.go | 20 ++++++- selfservice/flow/login/hook.go | 55 +++++++++++++++++-- selfservice/flow/login/hook_test.go | 48 +++++++++++++--- selfservice/flow/login/strategy.go | 3 + selfservice/flow/registration/hook.go | 17 +++--- selfservice/strategy/oidc/strategy_login.go | 1 + .../strategy/oidc/strategy_settings.go | 47 +++++++++++++++- selfservice/strategy/oidc/strategy_test.go | 5 +- 11 files changed, 170 insertions(+), 36 deletions(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/selfservice/flow/duplicate_credentials.go b/selfservice/flow/duplicate_credentials.go index 8e263240cedb..ddba57251ed2 100644 --- a/selfservice/flow/duplicate_credentials.go +++ b/selfservice/flow/duplicate_credentials.go @@ -6,7 +6,6 @@ package flow import ( "encoding/json" - "github.com/gofrs/uuid" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -20,7 +19,6 @@ type DuplicateCredentialsData struct { CredentialsType identity.CredentialsType CredentialsConfig sqlxx.JSONRawMessage DuplicateIdentifier string - OrganizationID uuid.UUID } type InternalContexter interface { diff --git a/selfservice/flow/login/flow_test.go b/selfservice/flow/login/flow_test.go index 1ac990200479..1df47e132974 100644 --- a/selfservice/flow/login/flow_test.go +++ b/selfservice/flow/login/flow_test.go @@ -16,12 +16,10 @@ import ( "github.com/tidwall/gjson" - "github.com/ory/x/jsonx" - "github.com/ory/x/sqlxx" - "github.com/ory/x/uuidx" - "github.com/ory/kratos/driver/config" "github.com/ory/kratos/identity" + "github.com/ory/x/jsonx" + "github.com/ory/x/sqlxx" "github.com/ory/kratos/internal" @@ -225,7 +223,6 @@ func TestDuplicateCredentials(t *testing.T) { CredentialsType: "foo", CredentialsConfig: sqlxx.JSONRawMessage(`{"bar":"baz"}`), DuplicateIdentifier: "bar", - OrganizationID: uuidx.NewV4(), } require.NoError(t, flow.SetDuplicateCredentials(f, dc)) diff --git a/selfservice/flow/login/handler.go b/selfservice/flow/login/handler.go index d86641470ef0..267b33217216 100644 --- a/selfservice/flow/login/handler.go +++ b/selfservice/flow/login/handler.go @@ -104,6 +104,18 @@ func WithFlowReturnTo(returnTo string) FlowOption { } } +func WithOrganizationID(organizationID uuid.NullUUID) FlowOption { + return func(f *Flow) { + f.OrganizationID = organizationID + } +} + +func WithRequestedAAL(aal identity.AuthenticatorAssuranceLevel) FlowOption { + return func(f *Flow) { + f.RequestedAAL = aal + } +} + func WithInternalContext(internalContext []byte) FlowOption { return func(f *Flow) { f.InternalContext = internalContext @@ -217,7 +229,13 @@ preLoginHook: if orgID.Valid { f.OrganizationID = orgID - strategyFilters = []StrategyFilter{func(s Strategy) bool { return s.ID() == identity.CredentialsTypeOIDC }} + if f.RequestedAAL == identity.AuthenticatorAssuranceLevel1 { + // We only apply the filter on AAL1, because the OIDC strategy can only satsify + // AAL1. + strategyFilters = []StrategyFilter{func(s Strategy) bool { + return s.ID() == identity.CredentialsTypeOIDC + }} + } } for _, s := range h.d.LoginStrategies(r.Context(), strategyFilters...) { diff --git a/selfservice/flow/login/hook.go b/selfservice/flow/login/hook.go index 402aacef665b..5d4cb270eb41 100644 --- a/selfservice/flow/login/hook.go +++ b/selfservice/flow/login/hook.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "net/http" + "net/url" "time" "github.com/gofrs/uuid" @@ -55,6 +56,7 @@ type ( x.LoggingProvider x.TracingProvider sessiontokenexchange.PersistenceProvider + HandlerProvider FlowPersistenceProvider HooksProvider @@ -273,8 +275,28 @@ func (e *HookExecutor) PostLoginHook( // If we detect that whoami would require a higher AAL, we redirect! if err := e.checkAAL(ctx, classified, f); err != nil { if aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) { - span.SetAttributes(attribute.String("return_to", aalErr.RedirectTo), attribute.String("redirect_reason", "requires aal2")) - e.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(aalErr.RedirectTo)) + if data, _ := flow.DuplicateCredentials(f); data == nil { + span.SetAttributes(attribute.String("return_to", aalErr.RedirectTo), attribute.String("redirect_reason", "requires aal2")) + e.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(aalErr.RedirectTo)) + return nil + } + + // Special case: If we are in a flow that wants to link credentials, we create a + // new login flow here that asks for the require AAL, but also copies over the + // internal context and the organization ID. + r.URL, err = url.Parse(aalErr.RedirectTo) + if err != nil { + return errors.WithStack(err) + } + newFlow, _, err := e.d.LoginHandler().NewLoginFlow(w, r, flow.TypeBrowser, + WithInternalContext(f.InternalContext), + WithOrganizationID(f.OrganizationID), + ) + if err != nil { + return errors.WithStack(err) + } + + x.AcceptToRedirectOrJSON(w, r, e.d.Writer(), newFlow, newFlow.AppendTo(e.d.Config().SelfServiceFlowLoginUI(ctx)).String()) return nil } return err @@ -309,7 +331,27 @@ func (e *HookExecutor) PostLoginHook( // If we detect that whoami would require a higher AAL, we redirect! if err := e.checkAAL(ctx, classified, f); err != nil { if aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) { - http.Redirect(w, r, aalErr.RedirectTo, http.StatusSeeOther) + if data, _ := flow.DuplicateCredentials(f); data == nil { + http.Redirect(w, r, aalErr.RedirectTo, http.StatusSeeOther) + return nil + } + + // Special case: If we are in a flow that wants to link credentials, we create a + // new login flow here that asks for the require AAL, but also copies over the + // internal context and the organization ID. + r.URL, err = url.Parse(aalErr.RedirectTo) + if err != nil { + return errors.WithStack(err) + } + newFlow, _, err := e.d.LoginHandler().NewLoginFlow(w, r, flow.TypeBrowser, + WithInternalContext(f.InternalContext), + WithOrganizationID(f.OrganizationID), + ) + if err != nil { + return errors.WithStack(err) + } + + x.AcceptToRedirectOrJSON(w, r, e.d.Writer(), newFlow, newFlow.AppendTo(e.d.Config().SelfServiceFlowLoginUI(ctx)).String()) return nil } return errors.WithStack(err) @@ -362,7 +404,7 @@ func (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.S return nil } - if err := e.checkDuplicateCredentialsIdentifierMatch(ctx, ident.ID, lc.DuplicateIdentifier); err != nil { + if err = e.checkDuplicateCredentialsIdentifierMatch(ctx, ident.ID, lc.DuplicateIdentifier); err != nil { return err } strategy, err := e.d.AllLoginStrategies().Strategy(lc.CredentialsType) @@ -380,8 +422,9 @@ func (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.S return err } - method := strategy.CompletedAuthenticationMethod(ctx) - sess.CompletedLoginForMethod(method) + if err = linkableStrategy.CompletedLogin(sess, lc); err != nil { + return err + } return nil } diff --git a/selfservice/flow/login/hook_test.go b/selfservice/flow/login/hook_test.go index 160373a47955..c75118f369e6 100644 --- a/selfservice/flow/login/hook_test.go +++ b/selfservice/flow/login/hook_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -27,6 +28,7 @@ import ( "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/session" + "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" ) @@ -42,6 +44,7 @@ func TestLoginExecutor(t *testing.T) { reg.WithHydra(hydra.NewFake()) testhelpers.SetDefaultIdentitySchema(conf, "file://./stub/login.schema.json") conf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, "https://www.ory.sh/") + _ = testhelpers.NewLoginUIFlowEchoServer(t, reg) newServer := func(t *testing.T, ft flow.Type, useIdentity *identity.Identity, flowCallback ...func(*login.Flow)) *httptest.Server { router := httprouter.New() @@ -222,7 +225,6 @@ func TestLoginExecutor(t *testing.T) { t.Run("case=work normally if AAL is satisfied", func(t *testing.T) { conf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, "aal1") - _ = testhelpers.NewLoginUIFlowEchoServer(t, reg) t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf)) useIdentity := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{ @@ -255,7 +257,6 @@ func TestLoginExecutor(t *testing.T) { t.Run("case=redirect to login if AAL is too low", func(t *testing.T) { conf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, "highest_available") - _ = testhelpers.NewLoginUIFlowEchoServer(t, reg) t.Cleanup(func() { conf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, "aal1") }) @@ -320,6 +321,7 @@ func TestLoginExecutor(t *testing.T) { t.Run("case=maybe links credential", func(t *testing.T) { t.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf)) conf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL) + conf.MustSet(ctx, "selfservice.methods.totp.enabled", true) email1, email2 := testhelpers.RandomEmail(), testhelpers.RandomEmail() passwordOnlyIdentity := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{ @@ -360,15 +362,43 @@ func TestLoginExecutor(t *testing.T) { require.NoError(t, err) t.Run("sub-case=does not link after first factor when second factor is available", func(t *testing.T) { + duplicateCredentialsData := flow.DuplicateCredentialsData{ + CredentialsType: identity.CredentialsTypeOIDC, + CredentialsConfig: credsOIDC2FA.Config, + DuplicateIdentifier: email2, + } ts := newServer(t, flow.TypeBrowser, twoFAIdentitiy, func(l *login.Flow) { - require.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{ - CredentialsType: identity.CredentialsTypeOIDC, - CredentialsConfig: credsOIDC2FA.Config, - DuplicateIdentifier: email2, - })) + require.NoError(t, flow.SetDuplicateCredentials(l, duplicateCredentialsData)) }) - res, body := makeRequestPost(t, ts, false, url.Values{}) - assert.Equal(t, res.Request.URL.String(), ts.URL+login.RouteInitBrowserFlow+"?aal=aal2", "%s", body) + res, _ := makeRequestPost(t, ts, false, url.Values{}) + + assert.Equal(t, reg.Config().SelfServiceFlowLoginUI(ctx).Host, res.Request.URL.Host) + assert.Equal(t, reg.Config().SelfServiceFlowLoginUI(ctx).Path, res.Request.URL.Path) + newFlowID := res.Request.URL.Query().Get("flow") + assert.NotEmpty(t, newFlowID) + + newFlow, err := reg.LoginFlowPersister().GetLoginFlow(ctx, uuid.Must(uuid.FromString(newFlowID))) + require.NoError(t, err) + newFlowDuplicateCredentialsData, err := flow.DuplicateCredentials(newFlow) + require.NoError(t, err) + + // Duplicate credentials data should have been copied over + assert.Equal(t, duplicateCredentialsData.CredentialsType, newFlowDuplicateCredentialsData.CredentialsType) + assert.Equal(t, duplicateCredentialsData.DuplicateIdentifier, newFlowDuplicateCredentialsData.DuplicateIdentifier) + assert.JSONEq(t, string(duplicateCredentialsData.CredentialsConfig), string(newFlowDuplicateCredentialsData.CredentialsConfig)) + + // AAL should be AAL2 + assert.Equal(t, identity.AuthenticatorAssuranceLevel2, newFlow.RequestedAAL) + + // TOTP nodes should be present + found := false + for _, n := range newFlow.UI.Nodes { + if n.Group == node.TOTPGroup { + found = true + break + } + } + assert.True(t, found, "could not find TOTP nodes in %+v", newFlow.UI.Nodes) ident, err := reg.Persister().GetIdentity(ctx, twoFAIdentitiy.ID, identity.ExpandCredentials) require.NoError(t, err) diff --git a/selfservice/flow/login/strategy.go b/selfservice/flow/login/strategy.go index 8ea671343c76..2291b760079b 100644 --- a/selfservice/flow/login/strategy.go +++ b/selfservice/flow/login/strategy.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/ory/kratos/identity" + "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/session" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" @@ -28,6 +29,8 @@ type Strategies []Strategy type LinkableStrategy interface { Link(ctx context.Context, i *identity.Identity, credentials sqlxx.JSONRawMessage) error + CompletedLogin(sess *session.Session, data *flow.DuplicateCredentialsData) error + SetDuplicateCredentials(f flow.InternalContexter, duplicateIdentifier string, credentials identity.Credentials, provider string) error } func (s Strategies) Strategy(id identity.CredentialsType) (Strategy, error) { diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index f2356d2c8277..33379368c265 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -161,21 +161,18 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque return err } - if _, ok := strategy.(login.LinkableStrategy); ok { + if strategy, ok := strategy.(login.LinkableStrategy); ok { duplicateIdentifier, err := e.getDuplicateIdentifier(ctx, i) if err != nil { return err } - registrationDuplicateCredentials := flow.DuplicateCredentialsData{ - CredentialsType: ct, - CredentialsConfig: i.Credentials[ct].Config, - DuplicateIdentifier: duplicateIdentifier, - } - if registrationFlow.OrganizationID.Valid { - registrationDuplicateCredentials.OrganizationID = registrationFlow.OrganizationID.UUID - } - if err := flow.SetDuplicateCredentials(registrationFlow, registrationDuplicateCredentials); err != nil { + if err := strategy.SetDuplicateCredentials( + registrationFlow, + duplicateIdentifier, + i.Credentials[ct], + provider, + ); err != nil { return err } } diff --git a/selfservice/strategy/oidc/strategy_login.go b/selfservice/strategy/oidc/strategy_login.go index 4805919f80d1..07be40194d40 100644 --- a/selfservice/strategy/oidc/strategy_login.go +++ b/selfservice/strategy/oidc/strategy_login.go @@ -169,6 +169,7 @@ func (s *Strategy) processLogin(ctx context.Context, w http.ResponseWriter, r *h sess := session.NewInactiveSession() sess.CompletedLoginForWithProvider(s.ID(), identity.AuthenticatorAssuranceLevel1, provider.Config().ID, provider.Config().OrganizationID) + for _, c := range oidcCredentials.Providers { if c.Subject == claims.Subject && c.Provider == provider.Config().ID { if err = s.d.LoginHookExecutor().PostLoginHook(w, r, node.OpenIDConnectGroup, loginFlow, i, sess, provider.Config().ID); err != nil { diff --git a/selfservice/strategy/oidc/strategy_settings.go b/selfservice/strategy/oidc/strategy_settings.go index 9a97a54e5521..4c721225c134 100644 --- a/selfservice/strategy/oidc/strategy_settings.go +++ b/selfservice/strategy/oidc/strategy_settings.go @@ -8,6 +8,7 @@ import ( "context" _ "embed" "encoding/json" + "fmt" "net/http" "time" @@ -527,7 +528,7 @@ func (s *Strategy) Link(ctx context.Context, i *identity.Identity, credentialsCo return err } if len(credentialsOIDCConfig.Providers) != 1 { - return errors.New("No oidc provider was set") + return errors.New("no oidc provider was set") } credentialsOIDCProvider := credentialsOIDCConfig.Providers[0] @@ -550,3 +551,47 @@ func (s *Strategy) Link(ctx context.Context, i *identity.Identity, credentialsCo return nil } + +func (s *Strategy) CompletedLogin(sess *session.Session, data *flow.DuplicateCredentialsData) error { + var credentialsOIDCConfig identity.CredentialsOIDC + if err := json.Unmarshal(data.CredentialsConfig, &credentialsOIDCConfig); err != nil { + return err + } + if len(credentialsOIDCConfig.Providers) != 1 { + return errors.New("no oidc provider was set") + } + credentialsOIDCProvider := credentialsOIDCConfig.Providers[0] + + sess.CompletedLoginForWithProvider( + s.ID(), + identity.AuthenticatorAssuranceLevel1, + credentialsOIDCProvider.Provider, + credentialsOIDCProvider.Organization, + ) + + return nil +} + +func (s *Strategy) SetDuplicateCredentials(f flow.InternalContexter, duplicateIdentifier string, credentials identity.Credentials, provider string) error { + var credentialsOIDCConfig identity.CredentialsOIDC + if err := json.Unmarshal(credentials.Config, &credentialsOIDCConfig); err != nil { + return err + } + + // We want to only set the provider in the credentials config that was used to authenticate the user. + for _, p := range credentialsOIDCConfig.Providers { + if p.Provider == provider { + credentialsOIDCConfig.Providers = []identity.CredentialsOIDCProvider{p} + config, err := json.Marshal(credentialsOIDCConfig) + if err != nil { + return err + } + return flow.SetDuplicateCredentials(f, flow.DuplicateCredentialsData{ + CredentialsType: s.ID(), + CredentialsConfig: config, + DuplicateIdentifier: duplicateIdentifier, + }) + } + } + return fmt.Errorf("provider %q not found in credentials", provider) +} diff --git a/selfservice/strategy/oidc/strategy_test.go b/selfservice/strategy/oidc/strategy_test.go index 4bb7bec9bf2f..dde3e1bda607 100644 --- a/selfservice/strategy/oidc/strategy_test.go +++ b/selfservice/strategy/oidc/strategy_test.go @@ -82,6 +82,8 @@ func TestStrategy(t *testing.T) { routerA := x.NewRouterAdmin() ts, _ := testhelpers.NewKratosServerWithRouters(t, reg, routerP, routerA) invalid := newOIDCProvider(t, ts, remotePublic, remoteAdmin, "invalid-issuer") + + orgID := uuidx.NewV4() viperSetProviderConfig( t, conf, @@ -1532,7 +1534,6 @@ func TestStrategy(t *testing.T) { subject2 := "new-login-subject2@ory.sh" scope = []string{"openid"} password := "lwkj52sdkjf" - orgID := uuidx.NewV4() var i *identity.Identity t.Run("step=create password identity", func(t *testing.T) { @@ -1557,7 +1558,7 @@ func TestStrategy(t *testing.T) { client := testhelpers.NewClientWithCookieJar(t, nil, nil) loginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser) - loginFlow.OrganizationID = uuid.NullUUID{orgID, true} + loginFlow.OrganizationID = uuid.NullUUID{UUID: orgID, Valid: true} require.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), loginFlow)) var linkingLoginFlow struct { From 9d244f01fd57e0a54cc7b3a0d017192f86fd5890 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:01:27 +0000 Subject: [PATCH 063/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 2fcc786b4c6a8d26880eb7d66cada98dbbb2f200 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:51:06 +0000 Subject: [PATCH 064/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6fa9c0e888..6221231834d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-05)](#2024-11-05) +- [ (2024-11-07)](#2024-11-07) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-05) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-07) ## Breaking Changes @@ -355,6 +355,12 @@ https://github.com/ory-corp/cloud/issues/7176 - Account linking should only happen after 2fa when required ([#4174](https://github.com/ory/kratos/issues/4174)) ([8e29b68](https://github.com/ory/kratos/commit/8e29b68a595d2ef18e48c2a01072335cefa36d86)) +- Account linking with 2FA ([#4188](https://github.com/ory/kratos/issues/4188)) + ([4a870a6](https://github.com/ory/kratos/commit/4a870a678dd3676abda7afc9803399dec4411b05)): + + This fixes some edge cases with OIDC account linking for accounts with 2FA + enabled. + - Add exists clause ([#4191](https://github.com/ory/kratos/issues/4191)) ([a313dd6](https://github.com/ory/kratos/commit/a313dd6ba6d823deb40f14c738e3b609dbaad56c)) - Duplicate autocomplete trigger From c33965e5735ead3acddac87ef84c3a730874f9ab Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:08:05 +0100 Subject: [PATCH 065/158] feat: remove duplicate queries during settings flow and use better index hint for credentials lookup (#4193) This patch reduces duplicate GetIdentity queries as part of submitting the settings flow, and improves an index to significantly reduce credential lookup. For better debugging, more tracing ha been added to the settings module. --- cmd/clidoc/main.go | 2 + cmd/hashers/argon2/calibrate.go | 1 + courier/courier_dispatcher.go | 1 + driver/config/config.go | 12 +- hash/hash_comparator.go | 3 +- hash/hasher_argon2.go | 1 + identity/manager.go | 2 +- internal/client-go/go.sum | 1 + .../sql/identity/persister_identity.go | 18 ++- ...42200000001_identities.autocommit.down.sql | 1 + ...6142200000001_identities.autocommit.up.sql | 2 + ...00001_identities.mysql.autocommit.down.sql | 1 + ...0000001_identities.mysql.autocommit.up.sql | 2 + ...42200000002_identities.autocommit.down.sql | 4 + ...6142200000002_identities.autocommit.up.sql | 4 + ...00002_identities.mysql.autocommit.down.sql | 4 + ...0000002_identities.mysql.autocommit.up.sql | 4 + selfservice/flow/error.go | 2 +- selfservice/flow/settings/error.go | 67 ++++++----- selfservice/flow/settings/error_test.go | 4 +- selfservice/flow/settings/handler.go | 62 ++++++---- selfservice/flow/settings/hook.go | 63 +++++----- selfservice/flow/settings/hook_test.go | 4 +- selfservice/flow/settings/strategy.go | 4 +- .../strategy/code/strategy_recovery.go | 5 +- .../strategy/link/strategy_recovery.go | 19 ++- selfservice/strategy/lookup/settings.go | 23 ++-- selfservice/strategy/oidc/strategy.go | 4 +- .../strategy/oidc/strategy_settings.go | 108 +++++++++--------- .../strategy/oidc/strategy_settings_test.go | 4 +- .../strategy/passkey/passkey_registration.go | 2 +- .../strategy/passkey/passkey_settings.go | 27 ++--- selfservice/strategy/password/settings.go | 21 ++-- selfservice/strategy/password/validator.go | 3 + selfservice/strategy/profile/strategy.go | 25 ++-- selfservice/strategy/totp/settings.go | 42 ++++--- selfservice/strategy/webauthn/settings.go | 37 +++--- session/manager_http.go | 4 +- 38 files changed, 334 insertions(+), 259 deletions(-) create mode 100644 persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.up.sql create mode 100644 persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.up.sql create mode 100644 persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql create mode 100644 persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql diff --git a/cmd/clidoc/main.go b/cmd/clidoc/main.go index a2b1850a87fa..c0bcda5d320e 100644 --- a/cmd/clidoc/main.go +++ b/cmd/clidoc/main.go @@ -309,6 +309,8 @@ func validateAllMessages(path string) error { info := &types.Info{ Defs: make(map[*ast.Ident]types.Object), } + + //nolint:staticcheck var pack *ast.Package for _, p := range packs { if p.Name == "text" { diff --git a/cmd/hashers/argon2/calibrate.go b/cmd/hashers/argon2/calibrate.go index 6f888735bbd5..8b4364a764f3 100644 --- a/cmd/hashers/argon2/calibrate.go +++ b/cmd/hashers/argon2/calibrate.go @@ -246,6 +246,7 @@ Please note that the values depend on the machine you run the hashing on. If you case res.MaxMem > conf.localConfig.DedicatedMemory: _, _ = progressPrinter.Printf("The required memory was %s more than the maximum allowed of %s.\n", res.MaxMem-maxMemory, conf.localConfig.DedicatedMemory) + //nolint:gosec // disable G115 conf.localConfig.Memory -= (res.MaxMem - conf.localConfig.DedicatedMemory) / bytesize.ByteSize(reqPerMin) _, _ = progressPrinter.Printf("Decreasing memory to %s\n", conf.localConfig.Memory) // too slow diff --git a/courier/courier_dispatcher.go b/courier/courier_dispatcher.go index a0f75954d68e..75745ef9a954 100644 --- a/courier/courier_dispatcher.go +++ b/courier/courier_dispatcher.go @@ -79,6 +79,7 @@ func (c *courier) DispatchQueue(ctx context.Context) error { maxRetries := c.deps.CourierConfig().CourierMessageRetries(ctx) pullCount := c.deps.CourierConfig().CourierWorkerPullCount(ctx) + //nolint:gosec // disable G115 messages, err := c.deps.CourierPersister().NextMessages(ctx, uint8(pullCount)) if err != nil { if errors.Is(err, ErrQueueEmpty) { diff --git a/driver/config/config.go b/driver/config/config.go index a7e5a09580d0..4eb0566963d2 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -550,10 +550,14 @@ func (p *Config) HasherArgon2(ctx context.Context) *Argon2 { // warn about usage of default values and point to the docs // warning will require https://github.com/ory/viper/issues/19 return &Argon2{ - Memory: p.GetProvider(ctx).ByteSizeF(ViperKeyHasherArgon2ConfigMemory, Argon2DefaultMemory), - Iterations: uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigIterations, int(Argon2DefaultIterations))), - Parallelism: uint8(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigParallelism, int(Argon2DefaultParallelism))), - SaltLength: uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigSaltLength, int(Argon2DefaultSaltLength))), + Memory: p.GetProvider(ctx).ByteSizeF(ViperKeyHasherArgon2ConfigMemory, Argon2DefaultMemory), + //nolint:gosec // disable G115 + Iterations: uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigIterations, int(Argon2DefaultIterations))), + //nolint:gosec // disable G115 + Parallelism: uint8(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigParallelism, int(Argon2DefaultParallelism))), + //nolint:gosec // disable G115 + SaltLength: uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigSaltLength, int(Argon2DefaultSaltLength))), + //nolint:gosec // disable G115 KeyLength: uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigKeyLength, int(Argon2DefaultKeyLength))), ExpectedDuration: p.GetProvider(ctx).DurationF(ViperKeyHasherArgon2ConfigExpectedDuration, Argon2DefaultDuration), ExpectedDeviation: p.GetProvider(ctx).DurationF(ViperKeyHasherArgon2ConfigExpectedDeviation, Argon2DefaultDeviation), diff --git a/hash/hash_comparator.go b/hash/hash_comparator.go index 524eb9bfba14..f324eb1de32a 100644 --- a/hash/hash_comparator.go +++ b/hash/hash_comparator.go @@ -29,7 +29,7 @@ import ( //nolint:staticcheck //lint:ignore SA1019 compatibility for imported passwords - "golang.org/x/crypto/md4" //#nosec G501 -- compatibility for imported passwords + "golang.org/x/crypto/md4" //nolint:gosec // disable G115 G501 -- compatibility for imported passwords "golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/scrypt" @@ -159,6 +159,7 @@ func CompareArgon2id(_ context.Context, password []byte, hash []byte) error { } // Derive the key from the other password using the same parameters. + //nolint:gosec // disable G115 otherHash := argon2.IDKey(password, salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength) return comparePasswordHashConstantTime(hash, otherHash) diff --git a/hash/hasher_argon2.go b/hash/hasher_argon2.go index 6775641bb36e..0a6dd32debc2 100644 --- a/hash/hasher_argon2.go +++ b/hash/hasher_argon2.go @@ -41,6 +41,7 @@ func NewHasherArgon2(c Argon2Configuration) *Argon2 { } func toKB(mem bytesize.ByteSize) uint32 { + //nolint:gosec // disable G115 return uint32(mem / bytesize.KB) } diff --git a/identity/manager.go b/identity/manager.go index 3bc5b08e0158..89c0259e6658 100644 --- a/identity/manager.go +++ b/identity/manager.go @@ -370,7 +370,7 @@ func (e *CreateIdentitiesError) Find(ident *Identity) *FailedIdentity { return nil } func (e *CreateIdentitiesError) ErrOrNil() error { - if e.failedIdentities == nil || len(e.failedIdentities) == 0 { + if len(e.failedIdentities) == 0 { return nil } return e diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 00ac440a16a6..a2fb51a0d5ab 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -13,10 +13,6 @@ import ( "sync" "time" - "github.com/ory/kratos/x/events" - - "github.com/ory/x/crdbx" - "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" "github.com/pkg/errors" @@ -33,7 +29,9 @@ import ( "github.com/ory/kratos/persistence/sql/update" "github.com/ory/kratos/schema" "github.com/ory/kratos/x" + "github.com/ory/kratos/x/events" "github.com/ory/x/contextx" + "github.com/ory/x/crdbx" "github.com/ory/x/errorsx" "github.com/ory/x/otelx" "github.com/ory/x/pagination/keysetpagination" @@ -806,11 +804,11 @@ func identifiersTableNameWithIndexHint(con *pop.Connection) string { ici := "identity_credential_identifiers" switch con.Dialect.Name() { case "cockroach": - ici += "@identity_credential_identifiers_nid_i_ici_idx" + ici += "@identity_credential_identifiers_ici_nid_i_idx" case "sqlite3": - ici += " INDEXED BY identity_credential_identifiers_nid_i_ici_idx" + ici += " INDEXED BY identity_credential_identifiers_ici_nid_i_idx" case "mysql": - ici += " USE INDEX(identity_credential_identifiers_nid_i_ici_idx)" + ici += " USE INDEX(identity_credential_identifiers_ici_nid_i_idx)" default: // good luck 🤷‍♂️ } @@ -932,7 +930,7 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. ) } - if params.IdsFilter != nil && len(params.IdsFilter) != 0 { + if len(params.IdsFilter) > 0 { wheres += ` AND identities.id in (?) ` @@ -987,7 +985,7 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. } case identity.ExpandFieldVerifiableAddresses: addrs := make([]identity.VerifiableAddress, 0) - if err := con.Where("nid = ?", nid).Where("identity_id IN (?)", identityIDs).Order("id").All(&addrs); err != nil { + if err := con.Where("identity_id IN (?)", identityIDs).Where("nid = ?", nid).Order("id").All(&addrs); err != nil { return sqlcon.HandleError(err) } for _, addr := range addrs { @@ -995,7 +993,7 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. } case identity.ExpandFieldRecoveryAddresses: addrs := make([]identity.RecoveryAddress, 0) - if err := con.Where("nid = ?", nid).Where("identity_id IN (?)", identityIDs).Order("id").All(&addrs); err != nil { + if err := con.Where("identity_id IN (?)", identityIDs).Where("nid = ?", nid).Order("id").All(&addrs); err != nil { return sqlcon.HandleError(err) } for _, addr := range addrs { diff --git a/persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.down.sql b/persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.down.sql new file mode 100644 index 000000000000..a989898db612 --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS identity_credential_identifiers_nid_ici_i_idx; diff --git a/persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.up.sql b/persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.up.sql new file mode 100644 index 000000000000..57c2306bb72d --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.up.sql @@ -0,0 +1,2 @@ +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_ici_i_idx + ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC); diff --git a/persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.down.sql b/persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.down.sql new file mode 100644 index 000000000000..859252365579 --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.down.sql @@ -0,0 +1 @@ +DROP INDEX identity_credential_identifiers_nid_ici_i_idx ON identity_credential_identifiers; diff --git a/persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.up.sql b/persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.up.sql new file mode 100644 index 000000000000..a39eab684412 --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000001_identities.mysql.autocommit.up.sql @@ -0,0 +1,2 @@ +CREATE INDEX identity_credential_identifiers_nid_ici_i_idx + ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC); diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql new file mode 100644 index 000000000000..407d5f476304 --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql @@ -0,0 +1,4 @@ +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx + ON identity_credential_identifiers (identity_credential_id ASC); + +DROP INDEX IF EXISTS identity_credential_identifiers_ici_nid_i_idx; diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql new file mode 100644 index 000000000000..6e0cb618c7b7 --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql @@ -0,0 +1,4 @@ +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_ici_nid_i_idx + ON identity_credential_identifiers (identity_credential_id ASC, nid ASC, identifier ASC); + +DROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx; diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql new file mode 100644 index 000000000000..4f0903c917e5 --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql @@ -0,0 +1,4 @@ +CREATE INDEX identity_credential_identifiers_identity_credential_id_idx + ON identity_credential_identifiers (identity_credential_id ASC); + +DROP INDEX identity_credential_identifiers_ici_nid_i_idx ON identity_credential_identifiers; diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql new file mode 100644 index 000000000000..aee0879d44ea --- /dev/null +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql @@ -0,0 +1,4 @@ +CREATE INDEX identity_credential_identifiers_ici_nid_i_idx + ON identity_credential_identifiers (identity_credential_id ASC, nid ASC, identifier ASC); + +DROP INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers; diff --git a/selfservice/flow/error.go b/selfservice/flow/error.go index d666822fa5c0..8449ec58ad3e 100644 --- a/selfservice/flow/error.go +++ b/selfservice/flow/error.go @@ -73,7 +73,7 @@ func NewFlowReplacedError(message *text.Message) *ReplacedError { return &ReplacedError{ DefaultError: x.ErrGone.WithID(text.ErrIDSelfServiceFlowReplaced). WithError("self-service flow replaced"). - WithReasonf(message.Text), + WithReason(message.Text), } } diff --git a/selfservice/flow/settings/error.go b/selfservice/flow/settings/error.go index aaf6076de0be..52294a464092 100644 --- a/selfservice/flow/settings/error.go +++ b/selfservice/flow/settings/error.go @@ -4,9 +4,12 @@ package settings import ( + "context" "net/http" "net/url" + "github.com/ory/x/otelx" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/x/events" @@ -39,6 +42,7 @@ type ( errorx.ManagementProvider x.WriterProvider x.LoggingProvider + x.TracingProvider HandlerProvider FlowPersistenceProvider @@ -91,18 +95,19 @@ func NewErrorHandler(d errorHandlerDependencies) *ErrorHandler { } func (s *ErrorHandler) reauthenticate( + ctx context.Context, w http.ResponseWriter, r *http.Request, f *Flow, err *FlowNeedsReAuth, ) { - returnTo := urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()), r.URL.Path), r.URL.Query()) + returnTo := urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx), r.URL.Path), r.URL.Query()) params := url.Values{} params.Set("refresh", "true") params.Set("return_to", returnTo.String()) - redirectTo := urlx.AppendPaths(urlx.CopyWithQuery(s.d.Config().SelfPublicURL(r.Context()), params), login.RouteInitBrowserFlow).String() + redirectTo := urlx.AppendPaths(urlx.CopyWithQuery(s.d.Config().SelfPublicURL(ctx), params), login.RouteInitBrowserFlow).String() err.RedirectBrowserTo = redirectTo if f.Type == flow.TypeAPI || x.IsJSONRequest(r) { s.d.Writer().WriteError(w, r, err) @@ -112,20 +117,20 @@ func (s *ErrorHandler) reauthenticate( http.Redirect(w, r, redirectTo, http.StatusSeeOther) } -func (s *ErrorHandler) PrepareReplacementForExpiredFlow(w http.ResponseWriter, r *http.Request, f *Flow, id *identity.Identity, err error) (*flow.ExpiredError, error) { +func (s *ErrorHandler) PrepareReplacementForExpiredFlow(ctx context.Context, w http.ResponseWriter, r *http.Request, f *Flow, id *identity.Identity, err error) (*flow.ExpiredError, error) { e := new(flow.ExpiredError) if !errors.As(err, &e) { return nil, nil } // create new flow because the old one is not valid - a, err := s.d.SettingsHandler().FromOldFlow(w, r, id, *f) + a, err := s.d.SettingsHandler().FromOldFlow(ctx, w, r, id, *f) if err != nil { return nil, err } a.UI.Messages.Add(text.NewErrorValidationSettingsFlowExpired(e.ExpiredAt)) - if err := s.d.SettingsFlowPersister().UpdateSettingsFlow(r.Context(), a); err != nil { + if err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, a); err != nil { return nil, err } @@ -133,6 +138,7 @@ func (s *ErrorHandler) PrepareReplacementForExpiredFlow(w http.ResponseWriter, r } func (s *ErrorHandler) WriteFlowError( + ctx context.Context, w http.ResponseWriter, r *http.Request, group node.UiNodeGroup, @@ -140,6 +146,9 @@ func (s *ErrorHandler) WriteFlowError( id *identity.Identity, err error, ) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.flow.settings.ErrorHandler.WriteFlowError") + defer otelx.End(span, &err) + logger := s.d.Audit(). WithError(err). WithRequest(r). @@ -156,7 +165,7 @@ func (s *ErrorHandler) WriteFlowError( if shouldRespondWithJSON { s.d.Writer().WriteError(w, r, err) } else { - http.Redirect(w, r, urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()), login.RouteInitBrowserFlow).String(), http.StatusSeeOther) + http.Redirect(w, r, urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx), login.RouteInitBrowserFlow).String(), http.StatusSeeOther) } return } @@ -171,25 +180,25 @@ func (s *ErrorHandler) WriteFlowError( } if f == nil { - trace.SpanFromContext(r.Context()).AddEvent(events.NewSettingsFailed(r.Context(), "", "")) - s.forward(w, r, nil, err) + trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, "", "")) + s.forward(ctx, w, r, nil, err) return } - trace.SpanFromContext(r.Context()).AddEvent(events.NewSettingsFailed(r.Context(), string(f.Type), f.Active.String())) + trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, string(f.Type), f.Active.String())) - if expired, inner := s.PrepareReplacementForExpiredFlow(w, r, f, id, err); inner != nil { - s.forward(w, r, f, err) + if expired, inner := s.PrepareReplacementForExpiredFlow(ctx, w, r, f, id, err); inner != nil { + s.forward(ctx, w, r, f, err) return } else if expired != nil { if id == nil { - s.forward(w, r, f, err) + s.forward(ctx, w, r, f, err) return } if f.Type == flow.TypeAPI || x.IsJSONRequest(r) { s.d.Writer().WriteError(w, r, expired) } else { - http.Redirect(w, r, expired.GetFlow().AppendTo(s.d.Config().SelfServiceFlowSettingsUI(r.Context())).String(), http.StatusSeeOther) + http.Redirect(w, r, expired.GetFlow().AppendTo(s.d.Config().SelfServiceFlowSettingsUI(ctx)).String(), http.StatusSeeOther) } return } @@ -198,71 +207,71 @@ func (s *ErrorHandler) WriteFlowError( if shouldRespondWithJSON { s.d.Writer().Write(w, r, f) } else { - http.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowSettingsUI(r.Context())).String(), http.StatusSeeOther) + http.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowSettingsUI(ctx)).String(), http.StatusSeeOther) } return } if e := new(FlowNeedsReAuth); errors.As(err, &e) { - s.reauthenticate(w, r, f, e) + s.reauthenticate(ctx, w, r, f, e) return } if err := f.UI.ParseError(group, err); err != nil { - s.forward(w, r, f, err) + s.forward(ctx, w, r, f, err) return } // Lookup the schema from the loaded configuration. This local schema // URL is needed for sorting the UI nodes, instead of the public URL. - schemas, err := s.d.IdentityTraitsSchemas(r.Context()) + schemas, err := s.d.IdentityTraitsSchemas(ctx) if err != nil { - s.forward(w, r, f, err) + s.forward(ctx, w, r, f, err) return } schema, err := schemas.GetByID(id.SchemaID) if err != nil { - s.forward(w, r, f, err) + s.forward(ctx, w, r, f, err) return } - if err := sortNodes(r.Context(), f.UI.Nodes, schema.RawURL); err != nil { - s.forward(w, r, f, err) + if err := sortNodes(ctx, f.UI.Nodes, schema.RawURL); err != nil { + s.forward(ctx, w, r, f, err) return } - if err := s.d.SettingsFlowPersister().UpdateSettingsFlow(r.Context(), f); err != nil { - s.forward(w, r, f, err) + if err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, f); err != nil { + s.forward(ctx, w, r, f, err) return } if f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) { - http.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowSettingsUI(r.Context())).String(), http.StatusSeeOther) + http.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowSettingsUI(ctx)).String(), http.StatusSeeOther) return } - updatedFlow, innerErr := s.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), f.ID) + updatedFlow, innerErr := s.d.SettingsFlowPersister().GetSettingsFlow(ctx, f.ID) if innerErr != nil { - s.forward(w, r, updatedFlow, innerErr) + s.forward(ctx, 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) { +func (s *ErrorHandler) forward(ctx context.Context, 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) + s.d.SelfServiceErrorManager().Forward(ctx, w, r, err) return } if rr.Type == flow.TypeAPI || x.IsJSONRequest(r) { s.d.Writer().WriteErrorCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), err) } else { - s.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err) + s.d.SelfServiceErrorManager().Forward(ctx, w, r, err) } } diff --git a/selfservice/flow/settings/error_test.go b/selfservice/flow/settings/error_test.go index 41b0c1e50104..f37d3c0cf8e4 100644 --- a/selfservice/flow/settings/error_test.go +++ b/selfservice/flow/settings/error_test.go @@ -71,7 +71,7 @@ func TestHandleError(t *testing.T) { require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &id)) router.GET("/error", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - h.WriteFlowError(w, r, flowMethod, settingsFlow, &id, flowError) + h.WriteFlowError(ctx, w, r, flowMethod, settingsFlow, &id, flowError) }) router.GET("/fake-redirect", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { @@ -90,7 +90,7 @@ func TestHandleError(t *testing.T) { require.NoError(t, err) for _, s := range reg.SettingsStrategies(context.Background()) { - require.NoError(t, s.PopulateSettingsMethod(req, &id, f)) + require.NoError(t, s.PopulateSettingsMethod(ctx, req, &id, f)) } require.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(context.Background(), f)) diff --git a/selfservice/flow/settings/handler.go b/selfservice/flow/settings/handler.go index bbf3a9010ee6..3fae390131a1 100644 --- a/selfservice/flow/settings/handler.go +++ b/selfservice/flow/settings/handler.go @@ -4,10 +4,13 @@ package settings import ( + "context" "net/http" "net/url" "time" + "github.com/ory/x/otelx" + "github.com/julienschmidt/httprouter" "github.com/pkg/errors" @@ -48,6 +51,7 @@ type ( x.CSRFProvider x.WriterProvider x.LoggingProvider + x.TracingProvider config.Provider @@ -120,27 +124,30 @@ func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) { admin.GET(RouteSubmitFlow, x.RedirectToPublicRoute(h.d)) } -func (h *Handler) NewFlow(w http.ResponseWriter, r *http.Request, i *identity.Identity, ft flow.Type) (*Flow, error) { +func (h *Handler) NewFlow(ctx context.Context, w http.ResponseWriter, r *http.Request, i *identity.Identity, ft flow.Type) (_ *Flow, err error) { + ctx, span := h.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.flow.settings.Handler.NewFlow") + defer otelx.End(span, &err) + f, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowSettingsFlowLifespan(r.Context()), r, i, ft) if err != nil { return nil, err } - if err := h.d.SettingsHookExecutor().PreSettingsHook(w, r, f); err != nil { + if err := h.d.SettingsHookExecutor().PreSettingsHook(ctx, w, r, f); err != nil { return nil, err } - for _, strategy := range h.d.SettingsStrategies(r.Context()) { - if err := h.d.ContinuityManager().Abort(r.Context(), w, r, ContinuityKey(strategy.SettingsStrategyID())); err != nil { + for _, strategy := range h.d.SettingsStrategies(ctx) { + if err := h.d.ContinuityManager().Abort(ctx, w, r, ContinuityKey(strategy.SettingsStrategyID())); err != nil { return nil, err } - if err := strategy.PopulateSettingsMethod(r, i, f); err != nil { + if err := strategy.PopulateSettingsMethod(ctx, r, i, f); err != nil { return nil, err } } - ds, err := h.d.Config().DefaultIdentityTraitsSchemaURL(r.Context()) + ds, err := h.d.Config().DefaultIdentityTraitsSchemaURL(ctx) if err != nil { return nil, err } @@ -156,8 +163,8 @@ func (h *Handler) NewFlow(w http.ResponseWriter, r *http.Request, i *identity.Id return f, nil } -func (h *Handler) FromOldFlow(w http.ResponseWriter, r *http.Request, i *identity.Identity, of Flow) (*Flow, error) { - nf, err := h.NewFlow(w, r, i, of.Type) +func (h *Handler) FromOldFlow(ctx context.Context, w http.ResponseWriter, r *http.Request, i *identity.Identity, of Flow) (*Flow, error) { + nf, err := h.NewFlow(ctx, w, r, i, of.Type) if err != nil { return nil, err } @@ -225,7 +232,7 @@ func (h *Handler) createNativeSettingsFlow(w http.ResponseWriter, r *http.Reques return } - f, err := h.NewFlow(w, r, s.Identity, flow.TypeAPI) + f, err := h.NewFlow(ctx, w, r, s.Identity, flow.TypeAPI) if err != nil { h.d.Writer().WriteError(w, r, err) return @@ -311,11 +318,11 @@ func (h *Handler) createBrowserSettingsFlow(w http.ResponseWriter, r *http.Reque } if err := h.d.SessionManager().DoesSessionSatisfy(ctx, s, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), managerOptions...); err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, nil, nil, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, err) return } - f, err := h.NewFlow(w, r, s.Identity, flow.TypeBrowser) + f, err := h.NewFlow(ctx, w, r, s.Identity, flow.TypeBrowser) if err != nil { h.d.SelfServiceErrorManager().Forward(ctx, w, r, err) return @@ -558,49 +565,56 @@ type updateSettingsFlowBody struct{} // 422: errorBrowserLocationChangeRequired // default: errorGeneric func (h *Handler) updateSettingsFlow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - ctx := r.Context() + var ( + err error + ctx = r.Context() + ) + + ctx, span := h.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.flow.settings.Handler.updateSettingsFlow") + defer otelx.End(span, &err) + rid, err := GetFlowID(r) if err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, nil, nil, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, err) return } f, err := h.d.SettingsFlowPersister().GetSettingsFlow(ctx, rid) if errors.Is(err, sqlcon.ErrNoRows) { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, nil, nil, errors.WithStack(herodot.ErrNotFound.WithReasonf("The settings request could not be found. Please restart the flow."))) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, errors.WithStack(herodot.ErrNotFound.WithReasonf("The settings request could not be found. Please restart the flow."))) return } else if err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, nil, nil, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, err) return } ss, err := h.d.SessionManager().FetchFromRequestContext(ctx, r) if err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, nil, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, nil, err) return } requestURL := x.RequestURL(r).String() if err := h.d.SessionManager().DoesSessionSatisfy(ctx, ss, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), session.WithRequestURL(requestURL)); err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, nil, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, nil, err) return } if err := f.Valid(ss); err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, ss.Identity, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, err) return } var s string var updateContext *UpdateContext for _, strat := range h.d.AllSettingsStrategies() { - uc, err := strat.Settings(w, r, f, ss) + uc, err := strat.Settings(ctx, w, r, f, ss) if errors.Is(err, flow.ErrStrategyNotResponsible) { continue } else if errors.Is(err, flow.ErrCompletedByStrategy) { return } else if err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, strat.NodeGroup(), f, ss.Identity, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, strat.NodeGroup(), f, ss.Identity, err) return } @@ -610,19 +624,19 @@ func (h *Handler) updateSettingsFlow(w http.ResponseWriter, r *http.Request, ps } if updateContext == nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, ss.Identity, errors.WithStack(schema.NewNoSettingsStrategyResponsible())) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, errors.WithStack(schema.NewNoSettingsStrategyResponsible())) return } i, err := updateContext.GetIdentityToUpdate() if err != nil { // An identity to update must always be present. - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, ss.Identity, err) + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, err) return } - if err := h.d.SettingsHookExecutor().PostSettingsHook(w, r, s, updateContext, i); err != nil { - h.d.SettingsFlowErrorHandler().WriteFlowError(w, r, node.DefaultGroup, f, ss.Identity, err) + if err := h.d.SettingsHookExecutor().PostSettingsHook(ctx, w, r, s, updateContext, i); err != nil { + h.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, err) return } } diff --git a/selfservice/flow/settings/hook.go b/selfservice/flow/settings/hook.go index 3c3c78abf1e8..2170760f20de 100644 --- a/selfservice/flow/settings/hook.go +++ b/selfservice/flow/settings/hook.go @@ -9,6 +9,8 @@ import ( "net/http" "time" + "github.com/ory/x/otelx" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/x/events" @@ -67,6 +69,7 @@ type ( x.CSRFTokenGeneratorProvider x.LoggingProvider x.WriterProvider + x.TracingProvider } HookExecutor struct { d executorDependencies @@ -120,7 +123,7 @@ func WithCallback(cb func(ctxUpdate *UpdateContext) error) func(o *postSettingsH } } -func (e *HookExecutor) handleSettingsError(_ http.ResponseWriter, r *http.Request, settingsType string, f *Flow, i *identity.Identity, flowError error) error { +func (e *HookExecutor) handleSettingsError(_ context.Context, _ http.ResponseWriter, r *http.Request, settingsType string, f *Flow, i *identity.Identity, flowError error) error { if f != nil { if i != nil { var group node.UiNodeGroup @@ -151,7 +154,10 @@ func (e *HookExecutor) handleSettingsError(_ http.ResponseWriter, r *http.Reques return flowError } -func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, settingsType string, ctxUpdate *UpdateContext, i *identity.Identity, opts ...PostSettingsHookOption) error { +func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWriter, r *http.Request, settingsType string, ctxUpdate *UpdateContext, i *identity.Identity, opts ...PostSettingsHookOption) (err error) { + ctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.flow.settings.HookExecutor.PostSettingsHook") + defer otelx.End(span, &err) + e.d.Logger(). WithRequest(r). WithField("identity_id", i.ID). @@ -160,13 +166,13 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, // Verify the redirect URL before we do any other processing. c := e.d.Config() - returnTo, err := x.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(r.Context()), + returnTo, err := x.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx), x.SecureRedirectUseSourceURL(ctxUpdate.Flow.RequestURL), - x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(r.Context())), - x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(r.Context())), + x.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)), + x.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)), x.SecureRedirectOverrideDefaultReturnTo( - e.d.Config().SelfServiceFlowSettingsReturnTo(r.Context(), settingsType, - ctxUpdate.Flow.AppendTo(e.d.Config().SelfServiceFlowSettingsUI(r.Context())))), + e.d.Config().SelfServiceFlowSettingsReturnTo(ctx, settingsType, + ctxUpdate.Flow.AppendTo(e.d.Config().SelfServiceFlowSettingsUI(ctx)))), ) if err != nil { return err @@ -177,11 +183,11 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, f(hookOptions) } - for k, executor := range e.d.PostSettingsPrePersistHooks(r.Context(), settingsType) { + for k, executor := range e.d.PostSettingsPrePersistHooks(ctx, settingsType) { logFields := logrus.Fields{ "executor": fmt.Sprintf("%T", executor), "executor_position": k, - "executors": PostHookPrePersistExecutorNames(e.d.PostSettingsPrePersistHooks(r.Context(), settingsType)), + "executors": PostHookPrePersistExecutorNames(e.d.PostSettingsPrePersistHooks(ctx, settingsType)), "identity_id": i.ID, "flow_method": settingsType, } @@ -199,23 +205,19 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, case "oidc": group = node.OpenIDConnectGroup } - var traits identity.Traits - if i != nil { - traits = i.Traits - } - return flow.HandleHookError(w, r, ctxUpdate.Flow, traits, group, err, e.d, e.d) + return flow.HandleHookError(w, r, ctxUpdate.Flow, i.Traits, group, err, e.d, e.d) } e.d.Logger().WithRequest(r).WithFields(logFields).Debug("ExecuteSettingsPrePersistHook completed successfully.") } options := []identity.ManagerOption{identity.ManagerExposeValidationErrorsForInternalTypeAssertion} - ttl := e.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(r.Context()) + ttl := e.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx) if ctxUpdate.Session.AuthenticatedAt.Add(ttl).After(time.Now()) { options = append(options, identity.ManagerAllowWriteProtectedTraits) } - if err := e.d.IdentityManager().Update(r.Context(), i, options...); err != nil { + if err := e.d.IdentityManager().Update(ctx, i, options...); err != nil { if errors.Is(err, identity.ErrProtectedFieldModified) { e.d.Logger().WithError(err).Debug("Modifying protected field requires re-authentication.") return errors.WithStack(NewFlowNeedsReAuth()) @@ -238,7 +240,7 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, } } - newFlow, err := e.d.SettingsHandler().NewFlow(w, r, i, ctxUpdate.Flow.Type) + newFlow, err := e.d.SettingsHandler().NewFlow(ctx, w, r, i, ctxUpdate.Flow.Type) if err != nil { return err } @@ -247,30 +249,30 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, ctxUpdate.Flow.UI.ResetMessages() ctxUpdate.Flow.UI.AddMessage(node.DefaultGroup, text.NewInfoSelfServiceSettingsUpdateSuccess()) ctxUpdate.Flow.InternalContext = newFlow.InternalContext - if err := e.d.SettingsFlowPersister().UpdateSettingsFlow(r.Context(), ctxUpdate.Flow); err != nil { + if err := e.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil { return err } - for k, executor := range e.d.PostSettingsPostPersistHooks(r.Context(), settingsType) { + for k, executor := range e.d.PostSettingsPostPersistHooks(ctx, settingsType) { if err := executor.ExecuteSettingsPostPersistHook(w, r, ctxUpdate.Flow, i, ctxUpdate.Session); err != nil { if errors.Is(err, ErrHookAbortFlow) { e.d.Logger(). WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(r.Context(), settingsType))). + WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(ctx, settingsType))). WithField("identity_id", i.ID). WithField("flow_method", settingsType). Debug("A ExecuteSettingsPostPersistHook hook aborted early.") return nil } - return e.handleSettingsError(w, r, settingsType, ctxUpdate.Flow, i, err) + return e.handleSettingsError(ctx, w, r, settingsType, ctxUpdate.Flow, i, err) } e.d.Logger().WithRequest(r). WithField("executor", fmt.Sprintf("%T", executor)). WithField("executor_position", k). - WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(r.Context(), settingsType))). + WithField("executors", PostHookPostPersistExecutorNames(e.d.PostSettingsPostPersistHooks(ctx, settingsType))). WithField("identity_id", i.ID). WithField("flow_method", settingsType). Debug("ExecuteSettingsPostPersistHook completed successfully.") @@ -282,11 +284,11 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, WithField("flow_method", settingsType). Debug("Completed all PostSettingsPrePersistHooks and PostSettingsPostPersistHooks.") - trace.SpanFromContext(r.Context()).AddEvent(events.NewSettingsSucceeded( - r.Context(), i.ID, string(ctxUpdate.Flow.Type), settingsType)) + trace.SpanFromContext(ctx).AddEvent(events.NewSettingsSucceeded( + ctx, i.ID, string(ctxUpdate.Flow.Type), settingsType)) if ctxUpdate.Flow.Type == flow.TypeAPI { - updatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), ctxUpdate.Flow.ID) + updatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(ctx, ctxUpdate.Flow.ID) if err != nil { return err } @@ -298,12 +300,12 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, return nil } - if err := e.d.SessionManager().IssueCookie(r.Context(), w, r, ctxUpdate.Session); err != nil { + if err := e.d.SessionManager().IssueCookie(ctx, w, r, ctxUpdate.Session); err != nil { return errors.WithStack(err) } if x.IsJSONRequest(r) { - updatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(r.Context(), ctxUpdate.Flow.ID) + updatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(ctx, ctxUpdate.Flow.ID) if err != nil { return err } @@ -320,8 +322,11 @@ func (e *HookExecutor) PostSettingsHook(w http.ResponseWriter, r *http.Request, return nil } -func (e *HookExecutor) PreSettingsHook(w http.ResponseWriter, r *http.Request, a *Flow) error { - for _, executor := range e.d.PreSettingsHooks(r.Context()) { +func (e *HookExecutor) PreSettingsHook(ctx context.Context, w http.ResponseWriter, r *http.Request, a *Flow) (err error) { + ctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.flow.settings.HookExecutor.PreSettingsHook") + defer otelx.End(span, &err) + + for _, executor := range e.d.PreSettingsHooks(ctx) { if err := executor.ExecuteSettingsPreHook(w, r, a); err != nil { return err } diff --git a/selfservice/flow/settings/hook_test.go b/selfservice/flow/settings/hook_test.go index 242eacf0e8da..70bb94212279 100644 --- a/selfservice/flow/settings/hook_test.go +++ b/selfservice/flow/settings/hook_test.go @@ -57,7 +57,7 @@ func TestSettingsExecutor(t *testing.T) { f, err := settings.NewFlow(conf, time.Minute, r, sess.Identity, ft) require.NoError(t, err) - if handleErr(t, w, r, reg.SettingsHookExecutor().PreSettingsHook(w, r, f)) { + if handleErr(t, w, r, reg.SettingsHookExecutor().PreSettingsHook(r.Context(), w, r, f)) { _, _ = w.Write([]byte("ok")) } }) @@ -73,7 +73,7 @@ func TestSettingsExecutor(t *testing.T) { a.RequestURL = x.RequestURL(r).String() require.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(r.Context(), a)) _ = handleErr(t, w, r, reg.SettingsHookExecutor(). - PostSettingsHook(w, r, strategy, &settings.UpdateContext{Flow: a, Session: sess}, i)) + PostSettingsHook(ctx, w, r, strategy, &settings.UpdateContext{Flow: a, Session: sess}, i)) }) ts := httptest.NewServer(router) t.Cleanup(ts.Close) diff --git a/selfservice/flow/settings/strategy.go b/selfservice/flow/settings/strategy.go index 011166540b22..be10fb92568f 100644 --- a/selfservice/flow/settings/strategy.go +++ b/selfservice/flow/settings/strategy.go @@ -28,8 +28,8 @@ type Strategy interface { SettingsStrategyID() string NodeGroup() node.UiNodeGroup RegisterSettingsRoutes(*x.RouterPublic) - PopulateSettingsMethod(*http.Request, *identity.Identity, *Flow) error - Settings(w http.ResponseWriter, r *http.Request, f *Flow, s *session.Session) (*UpdateContext, error) + PopulateSettingsMethod(context.Context, *http.Request, *identity.Identity, *Flow) error + Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *Flow, s *session.Session) (*UpdateContext, error) } type Strategies []Strategy diff --git a/selfservice/strategy/code/strategy_recovery.go b/selfservice/strategy/code/strategy_recovery.go index 3d050b7b60f8..8f0dcc0f3aa6 100644 --- a/selfservice/strategy/code/strategy_recovery.go +++ b/selfservice/strategy/code/strategy_recovery.go @@ -213,7 +213,7 @@ func (s *Strategy) recoveryIssueSession(w http.ResponseWriter, r *http.Request, f.ContinueWith = append(f.ContinueWith, flow.NewContinueWithSetToken(sess.Token)) } - sf, err := s.deps.SettingsHandler().NewFlow(w, r, sess.Identity, f.Type) + sf, err := s.deps.SettingsHandler().NewFlow(ctx, w, r, sess.Identity, f.Type) if err != nil { return s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err)) } @@ -275,7 +275,8 @@ func (s *Strategy) recoveryUseCode(w http.ResponseWriter, r *http.Request, body return s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err)) } - recovered, err := s.deps.IdentityPool().GetIdentity(ctx, code.IdentityID, identity.ExpandDefault) + // Important to expand everything here, as we need the data for recovery. + recovered, err := s.deps.IdentityPool().GetIdentity(ctx, code.IdentityID, identity.ExpandEverything) if err != nil { return s.HandleRecoveryError(w, r, f, nil, err) } diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index 8fff0986bf8d..0ad04d244817 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -4,6 +4,7 @@ package link import ( + context "context" "encoding/json" "net/http" "net/url" @@ -257,7 +258,7 @@ func (s *Strategy) Recover(w http.ResponseWriter, r *http.Request, f *recovery.F return s.HandleRecoveryError(w, r, nil, body, err) } - return s.recoveryUseToken(w, r, f.ID, body) + return s.recoveryUseToken(ctx, w, r, f.ID, body) } if _, err := s.d.SessionManager().FetchFromRequest(r.Context(), r); err == nil { @@ -294,7 +295,7 @@ func (s *Strategy) Recover(w http.ResponseWriter, r *http.Request, f *recovery.F } } -func (s *Strategy) recoveryIssueSession(w http.ResponseWriter, r *http.Request, f *recovery.Flow, id *identity.Identity) error { +func (s *Strategy) recoveryIssueSession(ctx context.Context, w http.ResponseWriter, r *http.Request, f *recovery.Flow, id *identity.Identity) error { f.UI.Messages.Clear() f.State = flow.StatePassedChallenge f.SetCSRFToken(s.d.CSRFHandler().RegenerateToken(w, r)) @@ -320,7 +321,12 @@ func (s *Strategy) recoveryIssueSession(w http.ResponseWriter, r *http.Request, return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } - sf, err := s.d.SettingsHandler().NewFlow(w, r, sess.Identity, flow.TypeBrowser) + // Force load. + if err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, sess.Identity, identity.ExpandEverything); err != nil { + return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) + } + + sf, err := s.d.SettingsHandler().NewFlow(ctx, w, r, sess.Identity, flow.TypeBrowser) if err != nil { return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } @@ -345,7 +351,7 @@ func (s *Strategy) recoveryIssueSession(w http.ResponseWriter, r *http.Request, return errors.WithStack(flow.ErrCompletedByStrategy) } -func (s *Strategy) recoveryUseToken(w http.ResponseWriter, r *http.Request, fID uuid.UUID, body *recoverySubmitPayload) error { +func (s *Strategy) recoveryUseToken(ctx context.Context, w http.ResponseWriter, r *http.Request, fID uuid.UUID, body *recoverySubmitPayload) error { token, err := s.d.RecoveryTokenPersister().UseRecoveryToken(r.Context(), fID, body.Token) if err != nil { if errors.Is(err, sqlcon.ErrNoRows) { @@ -376,7 +382,8 @@ func (s *Strategy) recoveryUseToken(w http.ResponseWriter, r *http.Request, fID return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } - recovered, err := s.d.IdentityPool().GetIdentity(r.Context(), token.IdentityID, identity.ExpandDefault) + // Important to expand everything here, as we need the data for recovery. + recovered, err := s.d.IdentityPool().GetIdentity(r.Context(), token.IdentityID, identity.ExpandEverything) if err != nil { return s.HandleRecoveryError(w, r, f, nil, err) } @@ -388,7 +395,7 @@ func (s *Strategy) recoveryUseToken(w http.ResponseWriter, r *http.Request, fID } } - return s.recoveryIssueSession(w, r, f, recovered) + return s.recoveryIssueSession(ctx, w, r, f, recovered) } func (s *Strategy) retryRecoveryFlowWithMessage(w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error { diff --git a/selfservice/strategy/lookup/settings.go b/selfservice/strategy/lookup/settings.go index 46ebcc733cf8..183f770bde03 100644 --- a/selfservice/strategy/lookup/settings.go +++ b/selfservice/strategy/lookup/settings.go @@ -101,8 +101,8 @@ func (p *updateSettingsFlowWithLookupMethod) SetFlowID(rid uuid.UUID) { p.Flow = rid.String() } -func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.lookup.strategy.Settings") +func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.lookup.strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithLookupMethod @@ -217,7 +217,7 @@ func (s *Strategy) continueSettingsFlowDisable(ctx context.Context, ctxUpdate *s } func (s *Strategy) continueSettingsFlowReveal(ctx context.Context, ctxUpdate *settings.UpdateContext) error { - hasLookup, err := s.identityHasLookup(ctx, ctxUpdate.Session.IdentityID) + hasLookup, err := s.identityHasLookup(ctx, ctxUpdate.Session.Identity) if err != nil { return err } @@ -330,13 +330,14 @@ func (s *Strategy) continueSettingsFlowConfirm(ctx context.Context, ctxUpdate *s return nil } -func (s *Strategy) identityHasLookup(ctx context.Context, id uuid.UUID) (bool, error) { - confidential, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, id) - if err != nil { - return false, err +func (s *Strategy) identityHasLookup(ctx context.Context, id *identity.Identity) (bool, error) { + if len(id.Credentials) == 0 { + if err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, id, identity.ExpandCredentials); err != nil { + return false, err + } } - count, err := s.CountActiveMultiFactorCredentials(ctx, confidential.Credentials) + count, err := s.CountActiveMultiFactorCredentials(ctx, id.Credentials) if err != nil { return false, err } @@ -344,10 +345,12 @@ func (s *Strategy) identityHasLookup(ctx context.Context, id uuid.UUID) (bool, e return count > 0, nil } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, f *settings.Flow) error { +func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.lookup.Strategy.PopulateSettingsMethod") + defer otelx.End(span, &err) f.UI.SetCSRF(s.d.GenerateCSRFToken(r)) - hasLookup, err := s.identityHasLookup(r.Context(), id.ID) + hasLookup, err := s.identityHasLookup(ctx, id) if err != nil { return err } diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index 6539d2cfd094..f2837e769b73 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -477,7 +477,7 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt s.forwardError(ctx, w, r, a, s.handleError(ctx, w, r, a, state.ProviderId, nil, err)) return } - if err := s.linkProvider(w, r, &settings.UpdateContext{Session: sess, Flow: a}, et, claims, provider); err != nil { + if err := s.linkProvider(ctx, w, r, &settings.UpdateContext{Session: sess, Flow: a}, et, claims, provider); err != nil { s.forwardError(ctx, w, r, a, s.handleError(ctx, w, r, a, state.ProviderId, nil, err)) return } @@ -561,7 +561,7 @@ func (s *Strategy) forwardError(ctx context.Context, w http.ResponseWriter, r *h if sess, err := s.d.SessionManager().FetchFromRequest(ctx, r); err == nil { i = sess.Identity } - s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.NodeGroup(), ff, i, err) + s.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, s.NodeGroup(), ff, i, err) default: panic(errors.Errorf("unexpected type: %T", ff)) } diff --git a/selfservice/strategy/oidc/strategy_settings.go b/selfservice/strategy/oidc/strategy_settings.go index 4c721225c134..7f2c6d42f5fa 100644 --- a/selfservice/strategy/oidc/strategy_settings.go +++ b/selfservice/strategy/oidc/strategy_settings.go @@ -51,14 +51,14 @@ var UnlinkAllFirstFactorConnectionsError = &jsonschema.ValidationError{ Message: "can not unlink OpenID Connect connection because it is the last remaining first factor credential", InstancePtr: "#/", } -func (s *Strategy) RegisterSettingsRoutes(router *x.RouterPublic) {} +func (s *Strategy) RegisterSettingsRoutes(*x.RouterPublic) {} func (s *Strategy) SettingsStrategyID() string { return s.ID().String() } -func (s *Strategy) decoderSettings(p *updateSettingsFlowWithOidcMethod, r *http.Request) error { - ds, err := s.d.Config().DefaultIdentityTraitsSchemaURL(r.Context()) +func (s *Strategy) decoderSettings(ctx context.Context, p *updateSettingsFlowWithOidcMethod, r *http.Request) error { + ds, err := s.d.Config().DefaultIdentityTraitsSchemaURL(ctx) if err != nil { return err } @@ -141,8 +141,9 @@ func (s *Strategy) linkableProviders(conf *ConfigurationCollection, confidential return result, nil } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, sr *settings.Flow) error { - ctx := r.Context() +func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, sr *settings.Flow) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.PopulateSettingsMethod") + defer otelx.End(span, &err) if sr.Type != flow.TypeBrowser { return nil @@ -153,17 +154,12 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity return err } - confidential, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, id.ID) - if err != nil { - return err - } - - linkable, err := s.linkableProviders(conf, confidential) + linkable, err := s.linkableProviders(conf, id) if err != nil { return err } - linked, err := s.linkedProviders(conf, confidential) + linked, err := s.linkedProviders(conf, id) if err != nil { return err } @@ -178,7 +174,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity sr.UI.GetNodes().Append(NewLinkNode(l.Config().ID, stringsx.Coalesce(l.Config().Label, l.Config().ID))) } - count, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, confidential) + count, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, id) if err != nil { return err } @@ -259,12 +255,12 @@ func (p *updateSettingsFlowWithOidcMethod) SetFlowID(rid uuid.UUID) { p.FlowID = rid.String() } -func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.oidc.strategy.Settings") +func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithOidcMethod - if err := s.decoderSettings(&p, r); err != nil { + if err := s.decoderSettings(ctx, &p, r); err != nil { return nil, err } f.TransientPayload = p.TransientPayload @@ -276,22 +272,22 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. } if len(p.Link) > 0 { - if err := s.initLinkProvider(w, r, ctxUpdate, &p); err != nil { + if err := s.initLinkProvider(ctx, w, r, ctxUpdate, &p); err != nil { return nil, err } return ctxUpdate, nil } else if len(p.Unlink) > 0 { - if err := s.unlinkProvider(w, r, ctxUpdate, &p); err != nil { + if err := s.unlinkProvider(ctx, w, r, ctxUpdate, &p); err != nil { return nil, err } return ctxUpdate, nil } - return nil, s.handleSettingsError(w, r, ctxUpdate, &p, errors.WithStack(herodot.ErrInternalServerError.WithReason("Expected either link or unlink to be set when continuing flow but both are unset."))) + return nil, s.handleSettingsError(ctx, w, r, ctxUpdate, &p, errors.WithStack(herodot.ErrInternalServerError.WithReason("Expected either link or unlink to be set when continuing flow but both are unset."))) } else if err != nil { - return nil, s.handleSettingsError(w, r, ctxUpdate, &p, err) + return nil, s.handleSettingsError(ctx, w, r, ctxUpdate, &p, err) } if len(p.Link)+len(p.Unlink) == 0 { @@ -305,17 +301,17 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. switch l, u := len(p.Link), len(p.Unlink); { case l > 0 && u > 0: - return nil, s.handleSettingsError(w, r, ctxUpdate, &p, errors.WithStack(&jsonschema.ValidationError{ + return nil, s.handleSettingsError(ctx, w, r, ctxUpdate, &p, errors.WithStack(&jsonschema.ValidationError{ Message: "it is not possible to link and unlink providers in the same request", InstancePtr: "#/", })) case l > 0: - if err := s.initLinkProvider(w, r, ctxUpdate, &p); err != nil { + if err := s.initLinkProvider(ctx, w, r, ctxUpdate, &p); err != nil { return nil, err } return ctxUpdate, nil case u > 0: - if err := s.unlinkProvider(w, r, ctxUpdate, &p); err != nil { + if err := s.unlinkProvider(ctx, w, r, ctxUpdate, &p); err != nil { return nil, err } return ctxUpdate, nil @@ -354,29 +350,28 @@ func (s *Strategy) isLinkable(ctx context.Context, ctxUpdate *settings.UpdateCon return i, nil } -func (s *Strategy) initLinkProvider(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod) error { - ctx := r.Context() +func (s *Strategy) initLinkProvider(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod) error { if _, err := s.isLinkable(ctx, ctxUpdate, p.Link); err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) { - return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth())) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth())) } provider, err := s.provider(ctx, p.Link) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } req, err := s.validateFlow(ctx, r, ctxUpdate.Flow.ID) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } state, pkce, err := s.GenerateState(ctx, provider, ctxUpdate.Flow.ID) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if err := s.d.ContinuityManager().Pause(ctx, w, r, sessionName, continuity.WithPayload(&AuthCodeContainer{ @@ -385,7 +380,7 @@ func (s *Strategy) initLinkProvider(w http.ResponseWriter, r *http.Request, ctxU Traits: p.Traits, }), continuity.WithLifespan(time.Minute*30)); err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } var up map[string]string @@ -395,7 +390,7 @@ func (s *Strategy) initLinkProvider(w http.ResponseWriter, r *http.Request, ctxU codeURL, err := getAuthRedirectURL(ctx, provider, req, state, up, pkce) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if x.IsJSONRequest(r) { @@ -407,67 +402,67 @@ func (s *Strategy) initLinkProvider(w http.ResponseWriter, r *http.Request, ctxU return errors.WithStack(flow.ErrCompletedByStrategy) } -func (s *Strategy) linkProvider(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider) error { - ctx := r.Context() +func (s *Strategy) linkProvider(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider) error { p := &updateSettingsFlowWithOidcMethod{ Link: provider.Config().ID, FlowID: ctxUpdate.Flow.ID.String(), } + if ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) { - return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth())) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth())) } i, err := s.isLinkable(ctx, ctxUpdate, p.Link) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if err := s.linkCredentials(ctx, i, token, provider.Config().ID, claims.Subject, provider.Config().OrganizationID); err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } - 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.Identity, ctxUpdate.Flow) + if err := s.d.SettingsHookExecutor().PostSettingsHook(ctx, w, r, s.SettingsStrategyID(), ctxUpdate, i, settings.WithCallback(func(ctxUpdate *settings.UpdateContext) error { + // Credential population is done by PostSettingsHook on ctxUpdate.Session.Identity + return s.PopulateSettingsMethod(ctx, r, ctxUpdate.Session.Identity, ctxUpdate.Flow) })); err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } return nil } -func (s *Strategy) unlinkProvider(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod) error { - ctx := r.Context() +func (s *Strategy) unlinkProvider(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod) error { if ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) { - return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth())) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth())) } providers, err := s.Config(ctx) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } i, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } availableProviders, err := s.linkedProviders(providers, i) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } var cc identity.CredentialsOIDC creds, err := i.ParseCredentials(s.ID(), &cc) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } count, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, i) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if count < 2 { - return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(UnlinkAllFirstFactorConnectionsError)) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(UnlinkAllFirstFactorConnectionsError)) } var found bool @@ -487,28 +482,29 @@ func (s *Strategy) unlinkProvider(w http.ResponseWriter, r *http.Request, ctxUpd } if !found { - return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(UnknownConnectionValidationError)) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(UnknownConnectionValidationError)) } creds.Identifiers = updatedIdentifiers creds.Config, err = json.Marshal(&identity.CredentialsOIDC{Providers: updatedProviders}) if err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(err)) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(err)) } 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.Identity, ctxUpdate.Flow) + if err := s.d.SettingsHookExecutor().PostSettingsHook(ctx, w, r, s.SettingsStrategyID(), ctxUpdate, i, settings.WithCallback(func(ctxUpdate *settings.UpdateContext) error { + // Credential population is done by PostSettingsHook on ctxUpdate.Session.Identity + return s.PopulateSettingsMethod(ctx, r, ctxUpdate.Session.Identity, ctxUpdate.Flow) })); err != nil { - return s.handleSettingsError(w, r, ctxUpdate, p, err) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } return errors.WithStack(flow.ErrCompletedByStrategy) } -func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod, err error) error { +func (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod, err error) error { if e := new(settings.FlowNeedsReAuth); errors.As(err, &e) { - if err := s.d.ContinuityManager().Pause(r.Context(), w, r, + if err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.Session.Identity)...); err != nil { return err } diff --git a/selfservice/strategy/oidc/strategy_settings_test.go b/selfservice/strategy/oidc/strategy_settings_test.go index af9d7cf8be75..887c2414b9eb 100644 --- a/selfservice/strategy/oidc/strategy_settings_test.go +++ b/selfservice/strategy/oidc/strategy_settings_test.go @@ -645,7 +645,7 @@ func TestPopulateSettingsMethod(t *testing.T) { populate := func(t *testing.T, reg *driver.RegistryDefault, ctx context.Context, i *identity.Identity, f *settings.Flow) *container.Container { require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i)) req := new(http.Request) - require.NoError(t, ns(t, reg, ctx).PopulateSettingsMethod(req.WithContext(ctx), i, f)) + require.NoError(t, ns(t, reg, ctx).PopulateSettingsMethod(ctx, req, i, f)) require.NotNil(t, f.UI) require.NotNil(t, f.UI.Nodes) assert.Equal(t, "POST", f.UI.Method) @@ -665,7 +665,7 @@ func TestPopulateSettingsMethod(t *testing.T) { require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i)) f := &settings.Flow{Type: flow.TypeAPI, ID: x.NewUUID(), UI: container.New("")} req := new(http.Request) - require.NoError(t, ns(t, reg, ctx).PopulateSettingsMethod(req.WithContext(ctx), i, f)) + require.NoError(t, ns(t, reg, ctx).PopulateSettingsMethod(ctx, req, i, f)) require.Empty(t, f.UI.Nodes) }) diff --git a/selfservice/strategy/passkey/passkey_registration.go b/selfservice/strategy/passkey/passkey_registration.go index cad0e02fda5d..fe3ec305e8b1 100644 --- a/selfservice/strategy/passkey/passkey_registration.go +++ b/selfservice/strategy/passkey/passkey_registration.go @@ -147,7 +147,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, regFlow *reg herodot.ErrInternalServerError.WithReasonf("Expected WebAuthN in internal context to be an object but got: %s", err))) } - if webAuthnSess.UserID == nil || len(webAuthnSess.UserID) == 0 { + if len(webAuthnSess.UserID) == 0 { return s.handleRegistrationError(w, r, regFlow, params, errors.WithStack( herodot.ErrInternalServerError.WithReasonf("Expected WebAuthN session data to contain a user ID"))) } diff --git a/selfservice/strategy/passkey/passkey_settings.go b/selfservice/strategy/passkey/passkey_settings.go index 6d5e73b808e8..f698a292930c 100644 --- a/selfservice/strategy/passkey/passkey_settings.go +++ b/selfservice/strategy/passkey/passkey_settings.go @@ -48,24 +48,21 @@ const ( InternalContextKeySessionData = "session_data" ) -func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, f *settings.Flow) error { +func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.passkey.Strategy.PopulateSettingsMethod") + defer otelx.End(span, &err) + if f.Type != flow.TypeBrowser { return nil } f.UI.SetCSRF(s.d.GenerateCSRFToken(r)) - - confidentialIdentity, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), id.ID) - if err != nil { - return err - } - - count, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(r.Context(), confidentialIdentity) + count, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, id) if err != nil { return err } - if webAuthns, err := s.identityListWebAuthn(confidentialIdentity); errors.Is(err, sqlcon.ErrNoRows) { + if webAuthns, err := s.identityListWebAuthn(id); errors.Is(err, sqlcon.ErrNoRows) { // Do nothing } else if err != nil { return err @@ -82,12 +79,12 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity } } - web, err := webauthn.New(s.d.Config().PasskeyConfig(r.Context())) + web, err := webauthn.New(s.d.Config().PasskeyConfig(ctx)) if err != nil { return errors.WithStack(err) } - identifier := s.PasskeyDisplayNameFromIdentity(r.Context(), id) + identifier := s.PasskeyDisplayNameFromIdentity(ctx, id) if identifier == "" { f.UI.Messages.Add(text.NewErrorValidationIdentifierMissing()) return nil @@ -96,7 +93,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity user := &webauthnx.User{ Name: identifier, ID: []byte(randx.MustString(64, randx.AlphaNum)), - Config: s.d.Config().PasskeyConfig(r.Context()), + Config: s.d.Config().PasskeyConfig(ctx), } option, sessionData, err := web.BeginRegistration(user) if err != nil { @@ -113,7 +110,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity return errors.WithStack(err) } - f.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(r.Context()))) + f.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx))) f.UI.Nodes.Upsert(node.NewInputField( node.PasskeyRegisterTrigger, @@ -165,8 +162,8 @@ func (s *Strategy) identityListWebAuthn(id *identity.Identity) (*identity.Creden return &cc, nil } -func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.passkey.strategy.Settings") +func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.passkey.strategy.Settings") defer otelx.End(span, &err) if f.Type != flow.TypeBrowser { diff --git a/selfservice/strategy/password/settings.go b/selfservice/strategy/password/settings.go index 33ab114230c3..ebe85e262849 100644 --- a/selfservice/strategy/password/settings.go +++ b/selfservice/strategy/password/settings.go @@ -75,8 +75,8 @@ func (p *updateSettingsFlowWithPasswordMethod) SetFlowID(rid uuid.UUID) { p.Flow = rid.String() } -func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.password.strategy.Settings") +func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithPasswordMethod @@ -84,21 +84,21 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. if errors.Is(err, settings.ErrContinuePreviousAction) { return ctxUpdate, s.continueSettingsFlow(ctx, r, ctxUpdate, p) } else if err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.SettingsStrategyID(), s.d); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if err := s.decodeSettingsFlow(r, &p); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } // This does not come from the payload! p.Flow = ctxUpdate.Flow.ID.String() if err := s.continueSettingsFlow(ctx, r, ctxUpdate, p); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } return ctxUpdate, nil @@ -172,7 +172,10 @@ func (s *Strategy) continueSettingsFlow(ctx context.Context, r *http.Request, ct return nil } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, _ *identity.Identity, f *settings.Flow) error { +func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, _ *identity.Identity, f *settings.Flow) (err error) { + _, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.Strategy.PopulateSettingsMethod") + defer otelx.End(span, &err) + f.UI.SetCSRF(s.d.GenerateCSRFToken(r)) f.UI.Nodes.Upsert(NewPasswordNode("password", node.InputAttributeAutocompleteNewPassword).WithMetaLabel(text.NewInfoNodeInputPassword())) f.UI.Nodes.Append(node.NewInputField("method", "password", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelSave())) @@ -180,10 +183,10 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, _ *identity.Identity, return nil } -func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasswordMethod, err error) error { +func (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasswordMethod, err error) error { // Do not pause flow if the flow type is an API flow as we can't save cookies in those flows. if e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser { - if err := s.d.ContinuityManager().Pause(r.Context(), w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil { + if err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil { return err } } diff --git a/selfservice/strategy/password/validator.go b/selfservice/strategy/password/validator.go index 688cd53f896b..00d15e3c80b6 100644 --- a/selfservice/strategy/password/validator.go +++ b/selfservice/strategy/password/validator.go @@ -180,7 +180,9 @@ func (s *DefaultPasswordValidator) Validate(ctx context.Context, identifier, pas func (s *DefaultPasswordValidator) validate(ctx context.Context, identifier, password string) error { passwordPolicyConfig := s.reg.Config().PasswordPolicyConfig(ctx) + //nolint:gosec // disable G115 if len(password) < int(passwordPolicyConfig.MinPasswordLength) { + //nolint:gosec // disable G115 return text.NewErrorValidationPasswordMinLength(int(passwordPolicyConfig.MinPasswordLength), len(password)) } @@ -215,6 +217,7 @@ func (s *DefaultPasswordValidator) validate(ctx context.Context, identifier, pas } } + //nolint:gosec // disable G115 if c > int64(s.reg.Config().PasswordPolicyConfig(ctx).MaxBreaches) { return text.NewErrorValidationPasswordTooManyBreaches(c) } diff --git a/selfservice/strategy/profile/strategy.go b/selfservice/strategy/profile/strategy.go index 996a2b31f5e1..bae200463565 100644 --- a/selfservice/strategy/profile/strategy.go +++ b/selfservice/strategy/profile/strategy.go @@ -83,8 +83,11 @@ func (s *Strategy) SettingsStrategyID() string { func (s *Strategy) RegisterSettingsRoutes(_ *x.RouterPublic) {} -func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, f *settings.Flow) error { - schemas, err := s.d.IdentityTraitsSchemas(r.Context()) +func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.profile.Strategy.PopulateSettingsMethod") + defer otelx.End(span, &err) + + schemas, err := s.d.IdentityTraitsSchemas(ctx) if err != nil { return err } @@ -96,7 +99,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity // use a schema compiler that disables identifiers schemaCompiler := jsonschema.NewCompiler() - nodes, err := container.NodesFromJSONSchema(r.Context(), node.ProfileGroup, traitsSchema.URL.String(), "", schemaCompiler) + nodes, err := container.NodesFromJSONSchema(ctx, node.ProfileGroup, traitsSchema.URL.String(), "", schemaCompiler) if err != nil { return err } @@ -112,8 +115,8 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity return nil } -func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.profile.strategy.Settings") +func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.profile.strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithProfileMethod @@ -121,7 +124,7 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. if errors.Is(err, settings.ErrContinuePreviousAction) { return ctxUpdate, s.continueFlow(ctx, r, ctxUpdate, p) } else if err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, nil, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err) } if err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.SettingsStrategyID(), s.d); err != nil { @@ -130,7 +133,7 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. option, err := s.newSettingsProfileDecoder(ctx, ctxUpdate.GetSessionIdentity()) if err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, nil, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err) } if err := s.dc.Decode(r, &p, option, @@ -138,14 +141,14 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. decoderx.HTTPDecoderSetValidatePayloads(true), decoderx.HTTPDecoderJSONFollowsFormFormat(), ); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, nil, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err) } // Reset after decoding form p.SetFlowID(ctxUpdate.Flow.ID) if err := s.continueFlow(ctx, r, ctxUpdate, p); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, nil, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err) } return ctxUpdate, nil @@ -243,9 +246,9 @@ func (s *Strategy) hydrateForm(r *http.Request, ar *settings.Flow, traits json.R // handleSettingsError is a convenience function for handling all types of errors that may occur (e.g. validation error) // during a settings request. -func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, puc *settings.UpdateContext, traits json.RawMessage, p updateSettingsFlowWithProfileMethod, err error) error { +func (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, puc *settings.UpdateContext, traits json.RawMessage, p updateSettingsFlowWithProfileMethod, err error) error { if e := new(settings.FlowNeedsReAuth); errors.As(err, &e) { - if err := s.d.ContinuityManager().Pause(r.Context(), w, r, + if err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, puc.GetSessionIdentity())...); err != nil { return err diff --git a/selfservice/strategy/totp/settings.go b/selfservice/strategy/totp/settings.go index be0cce18afeb..65953f741c61 100644 --- a/selfservice/strategy/totp/settings.go +++ b/selfservice/strategy/totp/settings.go @@ -85,8 +85,8 @@ func (p *updateSettingsFlowWithTotpMethod) SetFlowID(rid uuid.UUID) { p.Flow = rid.String() } -func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.oidc.strategy.Settings") +func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithTotpMethod @@ -94,27 +94,27 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. if errors.Is(err, settings.ErrContinuePreviousAction) { return ctxUpdate, s.continueSettingsFlow(ctx, r, ctxUpdate, p) } else if err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if err := s.decodeSettingsFlow(r, &p); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if p.UnlinkTOTP { // This is a submit so we need to manually set the type to TOTP p.Method = s.SettingsStrategyID() if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil { - return nil, s.handleSettingsError(w, r, ctxUpdate, p, err) + return nil, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } } else if err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.SettingsStrategyID(), s.d); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } // This does not come from the payload! p.Flow = ctxUpdate.Flow.ID.String() if err := s.continueSettingsFlow(ctx, r, ctxUpdate, p); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } return ctxUpdate, nil @@ -147,7 +147,7 @@ func (s *Strategy) continueSettingsFlow(ctx context.Context, r *http.Request, ct return errors.WithStack(settings.NewFlowNeedsReAuth()) } - hasTOTP, err := s.identityHasTOTP(ctx, ctxUpdate.Session.IdentityID) + hasTOTP, err := s.identityHasTOTP(ctx, ctxUpdate.Session.Identity) if err != nil { return err } @@ -244,13 +244,14 @@ func (s *Strategy) continueSettingsFlowRemoveTOTP(ctx context.Context, ctxUpdate return i, nil } -func (s *Strategy) identityHasTOTP(ctx context.Context, id uuid.UUID) (bool, error) { - confidential, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, id) - if err != nil { - return false, err +func (s *Strategy) identityHasTOTP(ctx context.Context, id *identity.Identity) (bool, error) { + if len(id.Credentials) == 0 { + if err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, id, identity.ExpandCredentials); err != nil { + return false, err + } } - count, err := s.CountActiveMultiFactorCredentials(ctx, confidential.Credentials) + count, err := s.CountActiveMultiFactorCredentials(ctx, id.Credentials) if err != nil { return false, err } @@ -258,10 +259,13 @@ func (s *Strategy) identityHasTOTP(ctx context.Context, id uuid.UUID) (bool, err return count > 0, nil } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, f *settings.Flow) error { +func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.totp.Strategy.PopulateSettingsMethod") + defer otelx.End(span, &err) + f.UI.SetCSRF(s.d.GenerateCSRFToken(r)) - hasTOTP, err := s.identityHasTOTP(r.Context(), id.ID) + hasTOTP, err := s.identityHasTOTP(ctx, id) if err != nil { return err } @@ -271,10 +275,10 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity f.UI.Nodes.Upsert(NewUnlinkTOTPNode()) } else { e := NewSchemaExtension(id.ID.String()) - _ = s.d.IdentityValidator().ValidateWithRunner(r.Context(), id, e) + _ = s.d.IdentityValidator().ValidateWithRunner(ctx, id, e) // No TOTP set up yet, add nodes allowing us to add it. - key, err := NewKey(r.Context(), e.AccountName, s.d) + key, err := NewKey(ctx, e.AccountName, s.d) if err != nil { return err } @@ -298,10 +302,10 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity return nil } -func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithTotpMethod, err error) error { +func (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithTotpMethod, err error) error { // Do not pause flow if the flow type is an API flow as we can't save cookies in those flows. if e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser { - if err := s.d.ContinuityManager().Pause(r.Context(), w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil { + if err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil { return err } } diff --git a/selfservice/strategy/webauthn/settings.go b/selfservice/strategy/webauthn/settings.go index 126e147f2979..b9900927653d 100644 --- a/selfservice/strategy/webauthn/settings.go +++ b/selfservice/strategy/webauthn/settings.go @@ -103,8 +103,8 @@ func (p *updateSettingsFlowWithWebAuthnMethod) SetFlowID(rid uuid.UUID) { p.Flow = rid.String() } -func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.webauthn.strategy.Settings") +func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.strategy.Settings") defer otelx.End(span, &err) if f.Type != flow.TypeBrowser { @@ -116,18 +116,18 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. if errors.Is(err, settings.ErrContinuePreviousAction) { return ctxUpdate, s.continueSettingsFlow(ctx, w, r, ctxUpdate, p) } else if err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if err := s.decodeSettingsFlow(r, &p); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } if len(p.Register)+len(p.Remove) > 0 { // This method has only two submit buttons p.Method = s.SettingsStrategyID() if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil { - return nil, s.handleSettingsError(w, r, ctxUpdate, p, err) + return nil, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } } else { span.SetAttributes(attribute.String("not_responsible_reason", "neither register nor remove is set")) @@ -137,7 +137,7 @@ func (s *Strategy) Settings(w http.ResponseWriter, r *http.Request, f *settings. // This does not come from the payload! p.Flow = ctxUpdate.Flow.ID.String() if err := s.continueSettingsFlow(ctx, w, r, ctxUpdate, p); err != nil { - return ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err) + return ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err) } return ctxUpdate, nil @@ -223,7 +223,7 @@ func (s *Strategy) continueSettingsFlowRemove(ctx context.Context, w http.Respon } if count < 2 && wasPasswordless { - return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(webauthnx.ErrNotEnoughCredentials)) + return s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(webauthnx.ErrNotEnoughCredentials)) } if len(updated) == 0 { @@ -339,24 +339,21 @@ func (s *Strategy) identityListWebAuthn(id *identity.Identity) (*identity.Creden return &cc, nil } -func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity, f *settings.Flow) error { +func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.Strategy.PopulateSettingsMethod") + defer otelx.End(span, &err) + if f.Type != flow.TypeBrowser { return nil } f.UI.SetCSRF(s.d.GenerateCSRFToken(r)) - - confidentialIdentity, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), id.ID) - if err != nil { - return err - } - - count, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(r.Context(), confidentialIdentity) + count, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, id) if err != nil { return err } - if webAuthns, err := s.identityListWebAuthn(confidentialIdentity); errors.Is(err, sqlcon.ErrNoRows) { + if webAuthns, err := s.identityListWebAuthn(id); errors.Is(err, sqlcon.ErrNoRows) { // Do nothing } else if err != nil { return err @@ -372,7 +369,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity } } - web, err := webauthn.New(s.d.Config().WebAuthnConfig(r.Context())) + web, err := webauthn.New(s.d.Config().WebAuthnConfig(ctx)) if err != nil { return errors.WithStack(err) } @@ -392,7 +389,7 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity return errors.WithStack(err) } - f.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(r.Context()))) + f.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx))) f.UI.Nodes.Upsert(webauthnx.NewWebAuthnConnectionName()) f.UI.Nodes.Upsert(webauthnx.NewWebAuthnConnectionTrigger(string(injectWebAuthnOptions)). WithMetaLabel(text.NewInfoSelfServiceSettingsRegisterWebAuthn())) @@ -400,10 +397,10 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity return nil } -func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithWebAuthnMethod, err error) error { +func (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithWebAuthnMethod, err error) error { // Do not pause flow if the flow type is an API flow as we can't save cookies in those flows. if e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser { - if err := s.d.ContinuityManager().Pause(r.Context(), w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil { + if err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil { return err } } diff --git a/session/manager_http.go b/session/manager_http.go index 2b3ffc6aa1e4..7fbaeff5fd98 100644 --- a/session/manager_http.go +++ b/session/manager_http.go @@ -253,7 +253,9 @@ func (s *ManagerHTTP) FetchFromRequest(ctx context.Context, r *http.Request) (_ return nil, errors.WithStack(NewErrNoCredentialsForSession()) } - se, err := s.r.SessionPersister().GetSessionByToken(ctx, token, ExpandEverything, identity.ExpandEverything) + se, err := s.r.SessionPersister().GetSessionByToken(ctx, token, + // Don't change this unless you want bad performance down the line (because we constantly are unsure if we have the full data fetched or not). + ExpandEverything, identity.ExpandEverything) if err != nil { if errors.Is(err, herodot.ErrNotFound) || errors.Is(err, sqlcon.ErrNoRows) { return nil, errors.WithStack(NewErrNoActiveSessionFound()) From 0a828a49e7dc10e6be32a9bfae28e83a26e3e679 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:09:38 +0000 Subject: [PATCH 066/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 7fad5191220380b443c0a2d169942c5d84f52d9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:45:06 +0100 Subject: [PATCH 067/158] chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#4189) Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md) - [Commits](https://github.com/golang-jwt/jwt/compare/v4.5.0...v4.5.1) --- updated-dependencies: - dependency-name: github.com/golang-jwt/jwt/v4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2a2b2e00ed7b..b866e2ab7e24 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/gobuffalo/httptest v1.5.2 github.com/gobuffalo/pop/v6 v6.1.2-0.20230318123913-c85387acc9a0 github.com/gofrs/uuid v4.4.0+incompatible - github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang-jwt/jwt/v4 v4.5.1 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 github.com/golang/mock v1.6.0 diff --git a/go.sum b/go.sum index 97de7e85e1e2..84a69ac01de5 100644 --- a/go.sum +++ b/go.sum @@ -280,8 +280,8 @@ github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 h1:xisWqjiKEff2B0KfFYGpCqc3M3zdTz+OHQHRc09FeYk= From 57eb414b38471bcaee3df19b75d132456aaaa4f6 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:36:47 +0000 Subject: [PATCH 068/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6221231834d5..5b28f5bb8851 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -463,6 +463,16 @@ https://github.com/ory-corp/cloud/issues/7176 Closes https://github.com/ory/kratos/issues/1570 Closes https://github.com/ory/kratos/issues/3779 +- Remove duplicate queries during settings flow and use better index hint for + credentials lookup ([#4193](https://github.com/ory/kratos/issues/4193)) + ([c33965e](https://github.com/ory/kratos/commit/c33965e5735ead3acddac87ef84c3a730874f9ab)): + + This patch reduces duplicate GetIdentity queries as part of submitting the + settings flow, and improves an index to significantly reduce credential + lookup. + + For better debugging, more tracing ha been added to the settings module. + - Remove more unused indices ([#4186](https://github.com/ory/kratos/issues/4186)) ([b294804](https://github.com/ory/kratos/commit/b2948044de4eee1841110162fe874055182bd2d2)) From 3dbeb64b3f99a3aeba5f7126c301b72fda4c3e3c Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:37:53 +0100 Subject: [PATCH 069/158] fix: gracefully handle unused index (#4196) --- .../sql/20241106142200000002_identities.autocommit.down.sql | 2 -- .../sql/20241106142200000002_identities.autocommit.up.sql | 2 -- .../20241106142200000002_identities.mysql.autocommit.down.sql | 3 --- .../20241106142200000002_identities.mysql.autocommit.up.sql | 2 -- 4 files changed, 9 deletions(-) diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql index 407d5f476304..d6b71451755d 100644 --- a/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql @@ -1,4 +1,2 @@ -CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx - ON identity_credential_identifiers (identity_credential_id ASC); DROP INDEX IF EXISTS identity_credential_identifiers_ici_nid_i_idx; diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql index 6e0cb618c7b7..75f08549f757 100644 --- a/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql @@ -1,4 +1,2 @@ CREATE INDEX IF NOT EXISTS identity_credential_identifiers_ici_nid_i_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC, identifier ASC); - -DROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx; diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql index 4f0903c917e5..1eef66d657ce 100644 --- a/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.down.sql @@ -1,4 +1 @@ -CREATE INDEX identity_credential_identifiers_identity_credential_id_idx - ON identity_credential_identifiers (identity_credential_id ASC); - DROP INDEX identity_credential_identifiers_ici_nid_i_idx ON identity_credential_identifiers; diff --git a/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql index aee0879d44ea..bd4b293aa682 100644 --- a/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql +++ b/persistence/sql/migrations/sql/20241106142200000002_identities.mysql.autocommit.up.sql @@ -1,4 +1,2 @@ CREATE INDEX identity_credential_identifiers_ici_nid_i_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC, identifier ASC); - -DROP INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers; From 53a5a8b93cec274456df3d988eb3bd12bc11fa87 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:09:37 +0100 Subject: [PATCH 070/158] refactor: hash comparator instantiation (#4195) --- hash/hash_comparator.go | 159 +++++++++++++++++++++++--------------- internal/client-go/go.sum | 1 + 2 files changed, 97 insertions(+), 63 deletions(-) diff --git a/hash/hash_comparator.go b/hash/hash_comparator.go index f324eb1de32a..2e51af5ddca5 100644 --- a/hash/hash_comparator.go +++ b/hash/hash_comparator.go @@ -60,54 +60,97 @@ func NewCryptDecoder() *crypt.Decoder { var CryptDecoder = NewCryptDecoder() +type SupportedHasher struct { + Comparator func(ctx context.Context, password []byte, hash []byte) error + Name string + Is func(hash []byte) bool +} + +func AddSupportedHasher(s SupportedHasher) { + supportedHashers = append(supportedHashers, s) +} + +var supportedHashers = []SupportedHasher{ + { + Comparator: CompareMD5Crypt, + Name: "md5crypt", + Is: IsMD5CryptHash, + }, + { + Comparator: CompareBcrypt, + Name: "bcrypt", + Is: IsBcryptHash, + }, + { + Comparator: CompareSHA256Crypt, + Name: "sha256crypt", + Is: IsSHA256CryptHash, + }, + { + Comparator: CompareSHA512Crypt, + Name: "sha512crypt", + Is: IsSHA512CryptHash, + }, + { + Comparator: CompareArgon2id, + Name: "argon2id", + Is: IsArgon2idHash, + }, + { + Comparator: CompareArgon2i, + Name: "argon2i", + Is: IsArgon2iHash, + }, + { + Comparator: ComparePbkdf2, + Name: "pbkdf2", + Is: IsPbkdf2Hash, + }, + { + Comparator: CompareScrypt, + Name: "scrypt", + Is: IsScryptHash, + }, + { + Comparator: CompareSSHA, + Name: "ssha", + Is: IsSSHAHash, + }, + { + Comparator: CompareSHA, + Name: "sha", + Is: IsSHAHash, + }, + { + Comparator: CompareFirebaseScrypt, + Name: "firebasescrypt", + Is: IsFirebaseScryptHash, + }, + { + Comparator: CompareMD5, + Name: "md5", + Is: IsMD5Hash, + }, + { + Comparator: CompareHMAC, + Name: "hmac", + Is: IsHMACHash, + }, +} + func Compare(ctx context.Context, password []byte, hash []byte) error { ctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Compare") defer span.End() - switch { - case IsMD5CryptHash(hash): - span.SetAttributes(attribute.String("hash.type", "md5crypt")) - return CompareMD5Crypt(ctx, password, hash) - case IsBcryptHash(hash): - span.SetAttributes(attribute.String("hash.type", "bcrypt")) - return CompareBcrypt(ctx, password, hash) - case IsSHA256CryptHash(hash): - span.SetAttributes(attribute.String("hash.type", "sha256")) - return CompareSHA256Crypt(ctx, password, hash) - case IsSHA512CryptHash(hash): - span.SetAttributes(attribute.String("hash.type", "sha512")) - return CompareSHA512Crypt(ctx, password, hash) - case IsArgon2idHash(hash): - span.SetAttributes(attribute.String("hash.type", "argon2id")) - return CompareArgon2id(ctx, password, hash) - case IsArgon2iHash(hash): - span.SetAttributes(attribute.String("hash.type", "argon2i")) - return CompareArgon2i(ctx, password, hash) - case IsPbkdf2Hash(hash): - span.SetAttributes(attribute.String("hash.type", "pbkdf2")) - return ComparePbkdf2(ctx, password, hash) - case IsScryptHash(hash): - span.SetAttributes(attribute.String("hash.type", "scrypt")) - return CompareScrypt(ctx, password, hash) - case IsSSHAHash(hash): - span.SetAttributes(attribute.String("hash.type", "ssha")) - return CompareSSHA(ctx, password, hash) - case IsSHAHash(hash): - span.SetAttributes(attribute.String("hash.type", "sha")) - return CompareSHA(ctx, password, hash) - case IsFirebaseScryptHash(hash): - span.SetAttributes(attribute.String("hash.type", "firebasescrypt")) - return CompareFirebaseScrypt(ctx, password, hash) - case IsMD5Hash(hash): - span.SetAttributes(attribute.String("hash.type", "md5")) - return CompareMD5(ctx, password, hash) - case IsHMACHash(hash): - span.SetAttributes(attribute.String("hash.type", "hmac")) - return CompareHMAC(ctx, password, hash) - default: - span.SetAttributes(attribute.String("hash.type", "unknown")) - return errors.WithStack(ErrUnknownHashAlgorithm) + for _, h := range supportedHashers { + if h.Is(hash) { + span.SetAttributes(attribute.String("hash.type", h.Name)) + return h.Comparator(ctx, password, hash) + } } + + span.SetAttributes(attribute.String("hash.type", "unknown")) + return errors.WithStack(ErrUnknownHashAlgorithm) } func CompareMD5Crypt(_ context.Context, password []byte, hash []byte) error { @@ -218,7 +261,7 @@ func CompareSSHA(_ context.Context, password []byte, hash []byte) error { raw := append(password[:], salt[:]...) - return compareSHAHelper(hasher, raw, hash) + return CompareSHAHelper(hasher, raw, hash) } func CompareSHA(_ context.Context, password []byte, hash []byte) error { @@ -230,7 +273,7 @@ func CompareSHA(_ context.Context, password []byte, hash []byte) error { r := strings.NewReplacer("{SALT}", string(salt), "{PASSWORD}", string(password)) raw := []byte(r.Replace(string(pf))) - return compareSHAHelper(hasher, raw, hash) + return CompareSHAHelper(hasher, raw, hash) } func CompareFirebaseScrypt(_ context.Context, password []byte, hash []byte) error { @@ -328,23 +371,13 @@ func IsMD5Hash(hash []byte) bool { return isMD5Hash.Match(hash) } func IsHMACHash(hash []byte) bool { return isHMACHash.Match(hash) } func IsValidHashFormat(hash []byte) bool { - if IsMD5CryptHash(hash) || - IsBcryptHash(hash) || - IsSHA256CryptHash(hash) || - IsSHA512CryptHash(hash) || - IsArgon2idHash(hash) || - IsArgon2iHash(hash) || - IsPbkdf2Hash(hash) || - IsScryptHash(hash) || - IsSSHAHash(hash) || - IsSHAHash(hash) || - IsFirebaseScryptHash(hash) || - IsMD5Hash(hash) || - IsHMACHash(hash) { - return true - } else { - return false + for _, h := range supportedHashers { + if h.Is(hash) { + return true + } } + + return false } func decodeArgon2idHash(encodedHash string) (p *config.Argon2, salt, hash []byte, err error) { @@ -482,8 +515,8 @@ func decodeSHAHash(encodedHash string) (hasher string, pf, salt, hash []byte, er return hasher, pf, salt, hash, nil } -// used for CompareSHA and CompareSSHA -func compareSHAHelper(hasher string, raw []byte, hash []byte) error { +// CompareSHAHelper compares the raw password with the hash using the given hasher. +func CompareSHAHelper(hasher string, raw []byte, hash []byte) error { var sha []byte switch hasher { diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 2040ca57160276aab96a34c9d82c56d897a4fbbe Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:11:41 +0000 Subject: [PATCH 071/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 6a40ec86f4719bc3306f839d8f65b8ce4fe7c56b Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:00:48 +0000 Subject: [PATCH 072/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b28f5bb8851..6cb9a2cfa68d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -374,6 +374,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Explicity set updated_at field when updating identity ([#4131](https://github.com/ory/kratos/issues/4131)) ([66afac1](https://github.com/ory/kratos/commit/66afac173dc08b1d6666b107cf7050a2b0b27774)) +- Gracefully handle unused index + ([#4196](https://github.com/ory/kratos/issues/4196)) + ([3dbeb64](https://github.com/ory/kratos/commit/3dbeb64b3f99a3aeba5f7126c301b72fda4c3e3c)) - Pass on correct context during verification ([#4151](https://github.com/ory/kratos/issues/4151)) ([7e0b500](https://github.com/ory/kratos/commit/7e0b500aada9c1931c759a43db7360e85afb57e3)) @@ -388,6 +391,9 @@ https://github.com/ory-corp/cloud/issues/7176 ### Code Refactoring +- Hash comparator instantiation + ([#4195](https://github.com/ory/kratos/issues/4195)) + ([53a5a8b](https://github.com/ory/kratos/commit/53a5a8b93cec274456df3d988eb3bd12bc11fa87)) - Remove total count from listSessions and improve secondary indices ([#4173](https://github.com/ory/kratos/issues/4173)) ([e24f993](https://github.com/ory/kratos/commit/e24f993ea4236bac4e23bd4250c11b5932040fd9)): From b6278af5c7ed7fb845a71ad0e64f8b87402a8f4b Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Fri, 8 Nov 2024 14:22:56 +0100 Subject: [PATCH 073/158] fix: order-by clause and span names (#4200) --- persistence/sql/identity/persister_identity.go | 2 +- selfservice/hook/show_verification_ui.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index a2fb51a0d5ab..489b1fbb4360 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -755,7 +755,7 @@ func QueryForCredentials(con *pop.Connection, where ...Where) (credentialsPerIde ).LeftJoin(identifiersTableNameWithIndexHint(con), "identity_credential_identifiers.identity_credential_id = identity_credentials.id AND identity_credential_identifiers.nid = identity_credentials.nid", ).Order( - "identity_credentials.id ASC", + "identity_credential_identifiers.identifier ASC", ) for _, w := range where { q = q.Where("("+w.Condition+")", w.Args...) diff --git a/selfservice/hook/show_verification_ui.go b/selfservice/hook/show_verification_ui.go index 65a5935ec7a6..580292a26ccd 100644 --- a/selfservice/hook/show_verification_ui.go +++ b/selfservice/hook/show_verification_ui.go @@ -53,7 +53,7 @@ func (e *ShowVerificationUIHook) ExecutePostRegistrationPostPersistHook(_ http.R // ExecuteLoginPostHook adds redirect headers and status code if the request is a browser request. // If the request is not a browser request, this hook does nothing. func (e *ShowVerificationUIHook) ExecuteLoginPostHook(_ http.ResponseWriter, r *http.Request, _ node.UiNodeGroup, f *login.Flow, _ *session.Session) error { - return otelx.WithSpan(r.Context(), "selfservice.hook.ShowVerificationUIHook.ExecutePostRegistrationPostPersistHook", func(ctx context.Context) error { + return otelx.WithSpan(r.Context(), "selfservice.hook.ShowVerificationUIHook.ExecuteLoginPostHook", func(ctx context.Context) error { return e.execute(r.WithContext(ctx), f) }) } From f75bf140d28305ed4c2b7e9437251614873dffd1 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:13:45 +0000 Subject: [PATCH 074/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cb9a2cfa68d..c74a6a0f1bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-07)](#2024-11-07) +- [ (2024-11-08)](#2024-11-08) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-07) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-08) ## Breaking Changes @@ -377,6 +377,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Gracefully handle unused index ([#4196](https://github.com/ory/kratos/issues/4196)) ([3dbeb64](https://github.com/ory/kratos/commit/3dbeb64b3f99a3aeba5f7126c301b72fda4c3e3c)) +- Order-by clause and span names + ([#4200](https://github.com/ory/kratos/issues/4200)) + ([b6278af](https://github.com/ory/kratos/commit/b6278af5c7ed7fb845a71ad0e64f8b87402a8f4b)) - Pass on correct context during verification ([#4151](https://github.com/ory/kratos/issues/4151)) ([7e0b500](https://github.com/ory/kratos/commit/7e0b500aada9c1931c759a43db7360e85afb57e3)) From 1008639428a6b72e0aa47bd13fe9c1d120aafb6e Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:52:39 +0100 Subject: [PATCH 075/158] feat: drop unused indices post index migration (#4201) --- .../20241108105000000001_index_cleanup.autocommit.down.sql | 5 +++++ .../sql/20241108105000000001_index_cleanup.autocommit.up.sql | 5 +++++ ...41108105000000001_index_cleanup.mysql.autocommit.down.sql | 5 +++++ ...0241108105000000001_index_cleanup.mysql.autocommit.up.sql | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.up.sql create mode 100644 persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.down.sql create mode 100644 persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.up.sql diff --git a/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.down.sql b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.down.sql new file mode 100644 index 000000000000..12d9fec56edb --- /dev/null +++ b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.down.sql @@ -0,0 +1,5 @@ +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_ici_i_idx + ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC); + +CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx + ON identity_credential_identifiers (identity_credential_id ASC); diff --git a/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.up.sql b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.up.sql new file mode 100644 index 000000000000..f672d728035a --- /dev/null +++ b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.up.sql @@ -0,0 +1,5 @@ +-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release) +DROP INDEX IF EXISTS identity_credential_identifiers_nid_ici_i_idx; + +-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release) +DROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx; diff --git a/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.down.sql b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.down.sql new file mode 100644 index 000000000000..6f1e2d289538 --- /dev/null +++ b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.down.sql @@ -0,0 +1,5 @@ +CREATE INDEX identity_credential_identifiers_nid_ici_i_idx + ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC); + +CREATE INDEX identity_credential_identifiers_identity_credential_id_idx + ON identity_credential_identifiers (identity_credential_id ASC); diff --git a/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.up.sql b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.up.sql new file mode 100644 index 000000000000..28f9cbdb8700 --- /dev/null +++ b/persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.autocommit.up.sql @@ -0,0 +1,5 @@ +-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release) +DROP INDEX identity_credential_identifiers_nid_ici_i_idx ON identity_credential_identifiers; + +-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release) +DROP INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers; From 253c5b67278939974a80d8ba2c8636268da89da2 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:43:24 +0000 Subject: [PATCH 076/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c74a6a0f1bb5..0c4de4c0e9ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-08)](#2024-11-08) +- [ (2024-11-12)](#2024-11-12) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-08) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-12) ## Breaking Changes @@ -443,6 +443,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Allow listing identities by organization ID ([#4115](https://github.com/ory/kratos/issues/4115)) ([b4c453b](https://github.com/ory/kratos/commit/b4c453b0472f67d0a52b345691f66aa48777a897)) +- Drop unused indices post index migration + ([#4201](https://github.com/ory/kratos/issues/4201)) + ([1008639](https://github.com/ory/kratos/commit/1008639428a6b72e0aa47bd13fe9c1d120aafb6e)) - Fast add credential type lookups ([#4177](https://github.com/ory/kratos/issues/4177)) ([eeb1355](https://github.com/ory/kratos/commit/eeb13552118504f17b48f2c7e002e777f5ee73f4)) From a90df5852ba96704863cc576edcb8286eaa9b3f9 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:10:35 +0100 Subject: [PATCH 077/158] docs: clarify facebook graph API versioning (#4208) --- selfservice/strategy/oidc/provider_facebook.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selfservice/strategy/oidc/provider_facebook.go b/selfservice/strategy/oidc/provider_facebook.go index 8bbca9b24e83..2f7a0a58aff0 100644 --- a/selfservice/strategy/oidc/provider_facebook.go +++ b/selfservice/strategy/oidc/provider_facebook.go @@ -69,6 +69,11 @@ func (g *ProviderFacebook) Claims(ctx context.Context, token *oauth2.Token, quer } appSecretProof := g.generateAppSecretProof(token) + // Do not use the versioned Graph API here. If you do, it will break once the version is deprecated. See also: + // + // When you use https://graph.facebook.com/me without specifying a version, Facebook defaults to the oldest + // available version your app supports. This behavior ensures backward compatibility but can lead to unintended + // issues if that version becomes deprecated. u, err := url.Parse(fmt.Sprintf("https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender&appsecret_proof=%s", appSecretProof)) if err != nil { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err)) From b40606c9ea62086e2c18b4707ace64f3302d61ce Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:01:42 +0000 Subject: [PATCH 078/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4de4c0e9ab..71b9de6726b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-12)](#2024-11-12) +- [ (2024-11-13)](#2024-11-13) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-12) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-13) ## Breaking Changes @@ -409,6 +409,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Add return_to query parameter to OAS Verification Flow for Native Apps ([#4086](https://github.com/ory/kratos/issues/4086)) ([b22135f](https://github.com/ory/kratos/commit/b22135fa05d7fb47dfeaccd7cdc183d16921a7ac)) +- Clarify facebook graph API versioning + ([#4208](https://github.com/ory/kratos/issues/4208)) + ([a90df58](https://github.com/ory/kratos/commit/a90df5852ba96704863cc576edcb8286eaa9b3f9)) - Usage of `organization` parameter in native self-service flows ([#4176](https://github.com/ory/kratos/issues/4176)) ([cb71e38](https://github.com/ory/kratos/commit/cb71e38147d21f73e9bd1e081dc3443abb63353e)) From afa76180e77df0ee0f96eef3b3f2b2d3fe08a33d Mon Sep 17 00:00:00 2001 From: Patrik Date: Thu, 14 Nov 2024 13:37:28 +0100 Subject: [PATCH 079/158] feat: add failure reason to events (#4203) --- internal/client-go/go.sum | 1 + selfservice/flow/login/error.go | 6 ++- selfservice/flow/login/hook.go | 2 + selfservice/flow/recovery/error.go | 6 ++- selfservice/flow/recovery/hook.go | 2 +- selfservice/flow/registration/error.go | 6 ++- selfservice/flow/registration/hook.go | 2 +- selfservice/flow/settings/error.go | 6 ++- selfservice/flow/settings/hook.go | 2 +- selfservice/flow/verification/error.go | 6 ++- selfservice/flow/verification/hook.go | 2 +- selfservice/hook/session_issuer.go | 2 + x/events/events.go | 58 +++++++++++++++++++++----- 13 files changed, 77 insertions(+), 24 deletions(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/selfservice/flow/login/error.go b/selfservice/flow/login/error.go index e7da5da2ca81..ec58345eb3c6 100644 --- a/selfservice/flow/login/error.go +++ b/selfservice/flow/login/error.go @@ -6,6 +6,8 @@ package login import ( "net/http" + "github.com/gofrs/uuid" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/selfservice/sessiontokenexchange" @@ -88,12 +90,12 @@ func (s *ErrorHandler) WriteFlowError(w http.ResponseWriter, r *http.Request, f Info("Encountered self-service login error.") if f == nil { - trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), "", "", false)) + trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), uuid.Nil, "", "", false, err)) s.forward(w, r, nil, err) return } - trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), string(f.Type), string(f.RequestedAAL), f.Refresh)) + trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), f.ID, string(f.Type), string(f.RequestedAAL), f.Refresh, err)) if expired, inner := s.PrepareReplacementForExpiredFlow(w, r, f, err); inner != nil { s.WriteFlowError(w, r, f, group, inner) diff --git a/selfservice/flow/login/hook.go b/selfservice/flow/login/hook.go index 5d4cb270eb41..5978cb5a3b33 100644 --- a/selfservice/flow/login/hook.go +++ b/selfservice/flow/login/hook.go @@ -221,6 +221,7 @@ func (e *HookExecutor) PostLoginHook( span.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{ SessionID: s.ID, IdentityID: i.ID, + FlowID: f.ID, FlowType: string(f.Type), RequestedAAL: string(f.RequestedAAL), IsRefresh: f.Refresh, @@ -262,6 +263,7 @@ func (e *HookExecutor) PostLoginHook( span.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{ SessionID: s.ID, + FlowID: f.ID, IdentityID: i.ID, FlowType: string(f.Type), RequestedAAL: string(f.RequestedAAL), IsRefresh: f.Refresh, Method: f.Active.String(), SSOProvider: provider, })) diff --git a/selfservice/flow/recovery/error.go b/selfservice/flow/recovery/error.go index 837bc6e0e4b3..f46f637254e7 100644 --- a/selfservice/flow/recovery/error.go +++ b/selfservice/flow/recovery/error.go @@ -7,6 +7,8 @@ import ( "net/http" "net/url" + "github.com/gofrs/uuid" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/x/events" @@ -73,12 +75,12 @@ func (s *ErrorHandler) WriteFlowError( Info("Encountered self-service recovery error.") if f == nil { - trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), "", "")) + trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), uuid.Nil, "", "", recoveryErr)) s.forward(w, r, nil, recoveryErr) return } - trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), string(f.Type), f.Active.String())) + trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), recoveryErr)) if expiredError := new(flow.ExpiredError); errors.As(recoveryErr, &expiredError) { strategy, err := s.d.RecoveryStrategies(r.Context()).Strategy(f.Active.String()) diff --git a/selfservice/flow/recovery/hook.go b/selfservice/flow/recovery/hook.go index 212eb061b7f4..163bc247c8f7 100644 --- a/selfservice/flow/recovery/hook.go +++ b/selfservice/flow/recovery/hook.go @@ -105,7 +105,7 @@ func (e *HookExecutor) PostRecoveryHook(w http.ResponseWriter, r *http.Request, Debug("ExecutePostRecoveryHook completed successfully.") } - trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoverySucceeded(r.Context(), s.Identity.ID, string(a.Type), a.Active.String())) + trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoverySucceeded(r.Context(), a.ID, s.Identity.ID, string(a.Type), a.Active.String())) logger.Debug("Post recovery execution hooks completed successfully.") diff --git a/selfservice/flow/registration/error.go b/selfservice/flow/registration/error.go index 41a15f08b2b1..0bf8b0f6abdc 100644 --- a/selfservice/flow/registration/error.go +++ b/selfservice/flow/registration/error.go @@ -6,6 +6,8 @@ package registration import ( "net/http" + "github.com/gofrs/uuid" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/identity" @@ -93,11 +95,11 @@ func (s *ErrorHandler) WriteFlowError( Info("Encountered self-service flow error.") if f == nil { - trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), "", "")) + trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), uuid.Nil, "", "", err)) s.forward(w, r, nil, err) return } - trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), string(f.Type), f.Active.String())) + trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), err)) if expired, inner := s.PrepareReplacementForExpiredFlow(w, r, f, err); inner != nil { s.forward(w, r, f, err) diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index 33379368c265..ab7400b60936 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -213,7 +213,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque WithField("identity_id", i.ID). Info("A new identity has registered using self-service registration.") - span.AddEvent(events.NewRegistrationSucceeded(ctx, i.ID, string(registrationFlow.Type), registrationFlow.Active.String(), provider)) + span.AddEvent(events.NewRegistrationSucceeded(ctx, registrationFlow.ID, i.ID, string(registrationFlow.Type), registrationFlow.Active.String(), provider)) s := session.NewInactiveSession() diff --git a/selfservice/flow/settings/error.go b/selfservice/flow/settings/error.go index 52294a464092..d8b97bf65c18 100644 --- a/selfservice/flow/settings/error.go +++ b/selfservice/flow/settings/error.go @@ -8,6 +8,8 @@ import ( "net/http" "net/url" + "github.com/gofrs/uuid" + "github.com/ory/x/otelx" "go.opentelemetry.io/otel/trace" @@ -180,11 +182,11 @@ func (s *ErrorHandler) WriteFlowError( } if f == nil { - trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, "", "")) + trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, uuid.Nil, "", "", err)) s.forward(ctx, w, r, nil, err) return } - trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, string(f.Type), f.Active.String())) + trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, f.ID, string(f.Type), f.Active.String(), err)) if expired, inner := s.PrepareReplacementForExpiredFlow(ctx, w, r, f, id, err); inner != nil { s.forward(ctx, w, r, f, err) diff --git a/selfservice/flow/settings/hook.go b/selfservice/flow/settings/hook.go index 2170760f20de..645957b07e30 100644 --- a/selfservice/flow/settings/hook.go +++ b/selfservice/flow/settings/hook.go @@ -285,7 +285,7 @@ func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWrit Debug("Completed all PostSettingsPrePersistHooks and PostSettingsPostPersistHooks.") trace.SpanFromContext(ctx).AddEvent(events.NewSettingsSucceeded( - ctx, i.ID, string(ctxUpdate.Flow.Type), settingsType)) + ctx, ctxUpdate.Flow.ID, i.ID, string(ctxUpdate.Flow.Type), settingsType)) if ctxUpdate.Flow.Type == flow.TypeAPI { updatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(ctx, ctxUpdate.Flow.ID) diff --git a/selfservice/flow/verification/error.go b/selfservice/flow/verification/error.go index 0fcffe84869e..5ed7e308e90c 100644 --- a/selfservice/flow/verification/error.go +++ b/selfservice/flow/verification/error.go @@ -7,6 +7,8 @@ import ( "net/http" "net/url" + "github.com/gofrs/uuid" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/x/events" @@ -69,11 +71,11 @@ func (s *ErrorHandler) WriteFlowError( Info("Encountered self-service verification error.") if f == nil { - trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), "", "")) + trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), uuid.Nil, "", "", err)) s.forward(w, r, nil, err) return } - trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), string(f.Type), f.Active.String())) + trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), err)) if e := new(flow.ExpiredError); errors.As(err, &e) { strategy, err := s.d.VerificationStrategies(r.Context()).Strategy(f.Active.String()) diff --git a/selfservice/flow/verification/hook.go b/selfservice/flow/verification/hook.go index c556acd51f16..f22c41b6d20c 100644 --- a/selfservice/flow/verification/hook.go +++ b/selfservice/flow/verification/hook.go @@ -112,7 +112,7 @@ func (e *HookExecutor) PostVerificationHook(w http.ResponseWriter, r *http.Reque Debug("ExecutePostVerificationHook completed successfully.") } - trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationSucceeded(r.Context(), i.ID, string(a.Type), a.Active.String())) + trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationSucceeded(r.Context(), a.ID, i.ID, string(a.Type), a.Active.String())) e.d.Logger(). WithRequest(r). diff --git a/selfservice/hook/session_issuer.go b/selfservice/hook/session_issuer.go index 4150fdeffdec..7e6664220696 100644 --- a/selfservice/hook/session_issuer.go +++ b/selfservice/hook/session_issuer.go @@ -75,6 +75,7 @@ func (e *SessionIssuer) executePostRegistrationPostPersistHook(w http.ResponseWr trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{ SessionID: s.ID, IdentityID: s.Identity.ID, + FlowID: a.ID, FlowType: string(a.Type), Method: a.Active.String(), })) @@ -90,6 +91,7 @@ func (e *SessionIssuer) executePostRegistrationPostPersistHook(w http.ResponseWr trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{ SessionID: s.ID, IdentityID: s.Identity.ID, + FlowID: a.ID, FlowType: string(a.Type), Method: a.Active.String(), })) diff --git a/x/events/events.go b/x/events/events.go index 862a35a39e98..178aa7de0f67 100644 --- a/x/events/events.go +++ b/x/events/events.go @@ -5,6 +5,7 @@ package events import ( "context" + "errors" "net/url" "time" @@ -12,6 +13,8 @@ import ( otelattr "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "github.com/ory/herodot" + "github.com/ory/kratos/schema" "github.com/ory/x/otelx/semconv" ) @@ -56,6 +59,8 @@ const ( attributeKeyWebhookResponseStatusCode semconv.AttributeKey = "WebhookResponseStatusCode" attributeKeyWebhookAttemptNumber semconv.AttributeKey = "WebhookAttemptNumber" attributeKeyWebhookRequestID semconv.AttributeKey = "WebhookRequestID" + attributeKeyReason semconv.AttributeKey = "Reason" + attributeKeyFlowID semconv.AttributeKey = "FlowID" ) func attrSessionID(val uuid.UUID) otelattr.KeyValue { @@ -118,6 +123,14 @@ func attrWebhookRequestID(id uuid.UUID) otelattr.KeyValue { return otelattr.String(attributeKeyWebhookRequestID.String(), id.String()) } +func attrReason(err error) otelattr.KeyValue { + return otelattr.String(attributeKeyReason.String(), reasonForError(err)) +} + +func attrFlowID(id uuid.UUID) otelattr.KeyValue { + return otelattr.String(attributeKeyFlowID.String(), id.String()) +} + func NewSessionIssued(ctx context.Context, aal string, sessionID, identityID uuid.UUID) (string, trace.EventOption) { return SessionIssued.String(), trace.WithAttributes( @@ -155,7 +168,7 @@ func NewSessionLifespanExtended(ctx context.Context, sessionID, identityID uuid. } type LoginSucceededOpts struct { - SessionID, IdentityID uuid.UUID + SessionID, IdentityID, FlowID uuid.UUID FlowType, RequestedAAL, Method, SSOProvider string IsRefresh bool } @@ -172,11 +185,12 @@ func NewLoginSucceeded(ctx context.Context, o *LoginSucceededOpts) (string, trac attLoginRequestedPrivilegedSession(o.IsRefresh), attrSelfServiceMethodUsed(o.Method), attrSelfServiceSSOProviderUsed(o.SSOProvider), + attrFlowID(o.FlowID), )..., ) } -func NewRegistrationSucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method, provider string) (string, trace.EventOption) { +func NewRegistrationSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method, provider string) (string, trace.EventOption) { return RegistrationSucceeded.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), @@ -184,72 +198,84 @@ func NewRegistrationSucceeded(ctx context.Context, identityID uuid.UUID, flowTyp semconv.AttrIdentityID(identityID), attrSelfServiceMethodUsed(method), attrSelfServiceSSOProviderUsed(provider), + attrFlowID(flowID), )...) } -func NewRecoverySucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method string) (string, trace.EventOption) { +func NewRecoverySucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { return RecoverySucceeded.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType), semconv.AttrIdentityID(identityID), attrSelfServiceMethodUsed(method), + attrFlowID(flowID), )...) } -func NewSettingsSucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method string) (string, trace.EventOption) { +func NewSettingsSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { return SettingsSucceeded.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType), semconv.AttrIdentityID(identityID), attrSelfServiceMethodUsed(method), + attrFlowID(flowID), )...) } -func NewVerificationSucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method string) (string, trace.EventOption) { +func NewVerificationSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { return VerificationSucceeded.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceMethodUsed(method), attrSelfServiceFlowType(flowType), semconv.AttrIdentityID(identityID), + attrFlowID(flowID), )...) } -func NewRegistrationFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) { +func NewRegistrationFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) { return RegistrationFailed.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType), attrSelfServiceMethodUsed(method), + attrReason(err), + attrFlowID(flowID), )...) } -func NewRecoveryFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) { +func NewRecoveryFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) { return RecoveryFailed.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType), attrSelfServiceMethodUsed(method), + attrReason(err), + attrFlowID(flowID), )...) } -func NewSettingsFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) { +func NewSettingsFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) { return SettingsFailed.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType), attrSelfServiceMethodUsed(method), + attrReason(err), + attrFlowID(flowID), )...) } -func NewVerificationFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) { +func NewVerificationFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) { return VerificationFailed.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType), attrSelfServiceMethodUsed(method), + attrReason(err), + attrFlowID(flowID), )...) } @@ -283,13 +309,15 @@ func NewIdentityUpdated(ctx context.Context, identityID uuid.UUID) (string, trac ) } -func NewLoginFailed(ctx context.Context, flowType string, requestedAAL string, isRefresh bool) (string, trace.EventOption) { +func NewLoginFailed(ctx context.Context, flowID uuid.UUID, flowType, requestedAAL string, isRefresh bool, err error) (string, trace.EventOption) { return LoginFailed.String(), trace.WithAttributes(append( semconv.AttributesFromContext(ctx), attrSelfServiceFlowType(flowType), attLoginRequestedAAL(requestedAAL), attLoginRequestedPrivilegedSession(isRefresh), + attrReason(err), + attrFlowID(flowID), )...) } @@ -356,3 +384,13 @@ func NewWebhookFailed(ctx context.Context, err error) (string, trace.EventOption )..., ) } + +func reasonForError(err error) string { + if ve := new(schema.ValidationError); errors.As(err, &ve) { + return ve.Message + } + if r := *new(herodot.ReasonCarrier); errors.As(err, &r) { + return r.Reason() + } + return err.Error() +} From f104ec1b013952f7efc4a59391f3b2e2aaeac0f6 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:39:15 +0000 Subject: [PATCH 080/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 00da05da9f77bbfb68b364b3ba2a5d0a2d9e4f15 Mon Sep 17 00:00:00 2001 From: Patrik Date: Thu, 14 Nov 2024 15:20:36 +0100 Subject: [PATCH 081/158] feat: add attributes to webhook events for better debugging (#4206) --- courier/http_channel.go | 2 +- embedx/config.schema.json | 6 +- request/builder.go | 30 ++++- request/builder_test.go | 4 +- request/config.go | 42 +++---- selfservice/hook/password_migration_hook.go | 5 +- selfservice/hook/web_hook.go | 20 ++-- selfservice/hook/web_hook_integration_test.go | 112 ++++++++++++------ x/events/events.go | 95 +++++++++------ 9 files changed, 195 insertions(+), 121 deletions(-) diff --git a/courier/http_channel.go b/courier/http_channel.go index 2e405fb22abe..97df749e48ae 100644 --- a/courier/http_channel.go +++ b/courier/http_channel.go @@ -61,7 +61,7 @@ func (c *httpChannel) Dispatch(ctx context.Context, msg Message) (err error) { ctx, span := c.d.Tracer(ctx).Tracer().Start(ctx, "courier.httpChannel.Dispatch") defer otelx.End(span, &err) - builder, err := request.NewBuilder(ctx, c.requestConfig, c.d, nil) + builder, err := request.NewBuilder(ctx, c.requestConfig, c.d) if err != nil { return errors.WithStack(err) } diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 6a1cc1c90ede..020a8d74b50c 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -225,6 +225,10 @@ "title": "Web-Hook Configuration", "description": "Define what the hook should do", "properties": { + "id": { + "type": "string", + "description": "The ID of the hook. Used to identify the hook in logs and errors. For debugging purposes only." + }, "response": { "title": "Response Handling", "description": "How the web hook should handle the response", @@ -2274,7 +2278,7 @@ "id": { "type": "string", "title": "Channel id", - "description": "The channel id. Corresponds to the .via property of the identity schema for recovery, verification, etc. Currently only phone is supported.", + "description": "The channel id. Corresponds to the .via property of the identity schema for recovery, verification, etc. Currently only sms is supported.", "maxLength": 32, "enum": ["sms"] }, diff --git a/request/builder.go b/request/builder.go index 5d219a37f336..bcf2e1a4dce4 100644 --- a/request/builder.go +++ b/request/builder.go @@ -46,18 +46,36 @@ type ( deps Dependencies cache *ristretto.Cache[[]byte, []byte] } + options struct { + cache *ristretto.Cache[[]byte, []byte] + } + BuilderOption = func(*options) ) -func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies, jsonnetCache *ristretto.Cache[[]byte, []byte]) (_ *Builder, err error) { +func WithCache(cache *ristretto.Cache[[]byte, []byte]) BuilderOption { + return func(o *options) { + o.cache = cache + } +} + +func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies, o ...BuilderOption) (_ *Builder, err error) { _, span := deps.Tracer(ctx).Tracer().Start(ctx, "request.NewBuilder") defer otelx.End(span, &err) - c, err := parseConfig(config) - if err != nil { + var opts options + for _, f := range o { + f(&opts) + } + + c := Config{} + if err := json.Unmarshal(config, &c); err != nil { return nil, err } - span.SetAttributes(attribute.String("url", c.URL), attribute.String("method", c.Method)) + span.SetAttributes( + attribute.String("url", c.URL), + attribute.String("method", c.Method), + ) r, err := retryablehttp.NewRequest(c.Method, c.URL, nil) if err != nil { @@ -66,9 +84,9 @@ func NewBuilder(ctx context.Context, config json.RawMessage, deps Dependencies, return &Builder{ r: r, - Config: c, + Config: &c, deps: deps, - cache: jsonnetCache, + cache: opts.cache, }, nil } diff --git a/request/builder_test.go b/request/builder_test.go index 3b443dd2ce29..5101546148ae 100644 --- a/request/builder_test.go +++ b/request/builder_test.go @@ -245,7 +245,7 @@ func TestBuildRequest(t *testing.T) { } { t.Run( "request-type="+tc.name, func(t *testing.T) { - rb, err := NewBuilder(context.Background(), json.RawMessage(tc.rawConfig), newTestDependencyProvider(t), nil) + rb, err := NewBuilder(context.Background(), json.RawMessage(tc.rawConfig), newTestDependencyProvider(t)) require.NoError(t, err) assert.Equal(t, tc.bodyTemplateURI, rb.Config.TemplateURI) @@ -279,7 +279,7 @@ func TestBuildRequest(t *testing.T) { "method": "POST", "body": "file://./stub/cancel_body.jsonnet" }`, - ), newTestDependencyProvider(t), nil) + ), newTestDependencyProvider(t)) require.NoError(t, err) _, err = rb.BuildRequest(context.Background(), json.RawMessage(`{}`)) diff --git a/request/config.go b/request/config.go index 92fc9898fc06..9ee2ed47f66a 100644 --- a/request/config.go +++ b/request/config.go @@ -17,48 +17,36 @@ type ( } Config struct { - Method string `json:"method"` - URL string `json:"url"` - TemplateURI string `json:"body"` - Header http.Header `json:"headers"` - Auth Auth `json:"auth,omitempty"` - } -) - -func parseConfig(r json.RawMessage) (*Config, error) { - type rawConfig struct { Method string `json:"method"` URL string `json:"url"` TemplateURI string `json:"body"` - Header json.RawMessage `json:"headers"` - Auth Auth `json:"auth,omitempty"` + Header http.Header `json:"-"` + RawHeader json.RawMessage `json:"headers"` + Auth Auth `json:"auth"` } +) - var rc rawConfig - err := json.Unmarshal(r, &rc) +func (c *Config) UnmarshalJSON(raw []byte) error { + type Alias Config + var a Alias + err := json.Unmarshal(raw, &a) if err != nil { - return nil, err + return err } - rawHeader := gjson.ParseBytes(rc.Header).Map() - hdr := http.Header{} + rawHeader := gjson.ParseBytes(a.RawHeader).Map() + a.Header = make(http.Header, len(rawHeader)) _, ok := rawHeader["Content-Type"] if !ok { - hdr.Set("Content-Type", ContentTypeJSON) + a.Header.Set("Content-Type", ContentTypeJSON) } for key, value := range rawHeader { - hdr.Set(key, value.String()) + a.Header.Set(key, value.String()) } - c := Config{ - Method: rc.Method, - URL: rc.URL, - TemplateURI: rc.TemplateURI, - Header: hdr, - Auth: rc.Auth, - } + *c = Config(a) - return &c, nil + return nil } diff --git a/selfservice/hook/password_migration_hook.go b/selfservice/hook/password_migration_hook.go index 065dc5dcddc6..c22909bed068 100644 --- a/selfservice/hook/password_migration_hook.go +++ b/selfservice/hook/password_migration_hook.go @@ -20,6 +20,7 @@ import ( "github.com/ory/herodot" "github.com/ory/kratos/request" "github.com/ory/kratos/schema" + "github.com/ory/kratos/x" "github.com/ory/x/otelx" ) @@ -52,9 +53,9 @@ func (p *PasswordMigration) Execute(ctx context.Context, data *PasswordMigration defer otelx.End(span, &err) if emitEvent { - instrumentHTTPClientForEvents(ctx, httpClient) + instrumentHTTPClientForEvents(ctx, httpClient, x.NewUUID(), "password_migration_hook") } - builder, err := request.NewBuilder(ctx, p.conf, p.deps, nil) + builder, err := request.NewBuilder(ctx, p.conf, p.deps) if err != nil { return errors.WithStack(err) } diff --git a/selfservice/hook/web_hook.go b/selfservice/hook/web_hook.go index 255d50605d99..6be52d2ca8d0 100644 --- a/selfservice/hook/web_hook.go +++ b/selfservice/hook/web_hook.go @@ -299,7 +299,10 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error { canInterrupt = gjson.GetBytes(e.conf, "can_interrupt").Bool() parseResponse = gjson.GetBytes(e.conf, "response.parse").Bool() emitEvent = gjson.GetBytes(e.conf, "emit_analytics_event").Bool() || !gjson.GetBytes(e.conf, "emit_analytics_event").Exists() // default true - tracer = trace.SpanFromContext(ctx).TracerProvider().Tracer("kratos-webhooks") + webhookID = gjson.GetBytes(e.conf, "id").Str + // The trigger ID is a random ID. It can be used to correlate webhook requests across retries. + triggerID = x.NewUUID() + tracer = trace.SpanFromContext(ctx).TracerProvider().Tracer("kratos-webhooks") ) if ignoreResponse && (parseResponse || canInterrupt) { return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("A webhook is configured to ignore the response but also to parse the response. This is not possible.")) @@ -318,7 +321,7 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error { defer otelx.End(span, &finalErr) if emitEvent { - instrumentHTTPClientForEvents(ctx, httpClient) + instrumentHTTPClientForEvents(ctx, httpClient, triggerID, webhookID) } defer func(startTime time.Time) { @@ -329,7 +332,7 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error { }).WithField("duration", time.Since(startTime)) if finalErr != nil { if emitEvent && !errors.Is(finalErr, context.Canceled) { - span.AddEvent(events.NewWebhookFailed(ctx, finalErr)) + span.AddEvent(events.NewWebhookFailed(ctx, finalErr, triggerID, webhookID)) } if ignoreResponse { logger.WithError(finalErr).Warning("Webhook request failed but the error was ignored because the configuration indicated that the upstream response should be ignored") @@ -339,12 +342,12 @@ func (e *WebHook) execute(ctx context.Context, data *templateContext) error { } else { logger.Info("Webhook request succeeded") if emitEvent { - span.AddEvent(events.NewWebhookSucceeded(ctx)) + span.AddEvent(events.NewWebhookSucceeded(ctx, triggerID, webhookID)) } } }(time.Now()) - builder, err := request.NewBuilder(ctx, e.conf, e.deps, jsonnetCache) + builder, err := request.NewBuilder(ctx, e.conf, e.deps, request.WithCache(jsonnetCache)) if err != nil { return err } @@ -551,7 +554,7 @@ func isTimeoutError(err error) bool { return errors.As(err, &te) && te.Timeout() || errors.Is(err, context.DeadlineExceeded) } -func instrumentHTTPClientForEvents(ctx context.Context, httpClient *retryablehttp.Client) { +func instrumentHTTPClientForEvents(ctx context.Context, httpClient *retryablehttp.Client, triggerID uuid.UUID, webhookID string) { // TODO(@alnr): improve this implementation to redact sensitive data var ( attempt = 0 @@ -560,8 +563,9 @@ func instrumentHTTPClientForEvents(ctx context.Context, httpClient *retryablehtt ) httpClient.RequestLogHook = func(_ retryablehttp.Logger, req *http.Request, retryNumber int) { attempt = retryNumber + 1 - requestID = uuid.Must(uuid.NewV4()) + requestID = x.NewUUID() req.Header.Set("Ory-Webhook-Request-ID", requestID.String()) + req.Header.Set("Ory-Webhook-Trigger-ID", triggerID.String()) // TODO(@alnr): redact sensitive data // reqBody, _ = httputil.DumpRequestOut(req, true) reqBody = []byte("") @@ -572,6 +576,6 @@ func instrumentHTTPClientForEvents(ctx context.Context, httpClient *retryablehtt // resBody = resBody[:min(len(resBody), 2<<10)] // truncate response body to 2 kB for event // TODO(@alnr): redact sensitive data resBody := []byte("") - trace.SpanFromContext(ctx).AddEvent(events.NewWebhookDelivered(ctx, res.Request.URL, reqBody, res.StatusCode, resBody, attempt, requestID)) + trace.SpanFromContext(ctx).AddEvent(events.NewWebhookDelivered(ctx, res.Request.URL, reqBody, res.StatusCode, resBody, attempt, requestID, triggerID, webhookID)) } } diff --git a/selfservice/hook/web_hook_integration_test.go b/selfservice/hook/web_hook_integration_test.go index cae9659a285c..0dff20cbb5d0 100644 --- a/selfservice/hook/web_hook_integration_test.go +++ b/selfservice/hook/web_hook_integration_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/sjson" + "go.opentelemetry.io/otel/attribute" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "golang.org/x/exp/slices" @@ -43,9 +44,11 @@ import ( "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" + "github.com/ory/kratos/x/events" "github.com/ory/x/jsonnetsecure" "github.com/ory/x/logrusx" "github.com/ory/x/otelx" + "github.com/ory/x/otelx/semconv" "github.com/ory/x/snapshotx" ) @@ -383,6 +386,8 @@ func TestWebHooks(t *testing.T) { vals := whr.Headers.Values(k) assert.Equal(t, v, vals) } + assert.NotZero(t, whr.Headers.Get("Ory-Webhook-Request-ID")) + assert.NotZero(t, whr.Headers.Get("Ory-Webhook-Trigger-ID")) if method != "TRACE" { // According to the HTTP spec any request method, but TRACE is allowed to @@ -1162,8 +1167,6 @@ func TestWebhookEvents(t *testing.T) { URL: &url.URL{Path: "/some_end_point"}, Method: http.MethodPost, } - s := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}} - _ = s f := &login.Flow{ID: x.NewUUID()} webhookReceiver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -1171,15 +1174,31 @@ func TestWebhookEvents(t *testing.T) { w.WriteHeader(200) w.Write([]byte("ok")) } else { - w.WriteHeader(400) + w.WriteHeader(500) w.Write([]byte("fail")) } })) t.Cleanup(webhookReceiver.Close) + getAttributes := func(attrs []attribute.KeyValue) (webhookID, triggerID, requestID string) { + for _, kv := range attrs { + switch semconv.AttributeKey(kv.Key) { + case events.AttributeKeyWebhookID: + webhookID = kv.Value.Emit() + case events.AttributeKeyWebhookTriggerID: + triggerID = kv.Value.Emit() + case events.AttributeKeyWebhookRequestID: + requestID = kv.Value.Emit() + } + } + return + } + t.Run("success", func(t *testing.T) { + whID := x.NewUUID() wh := hook.NewWebHook(&whDeps, json.RawMessage(fmt.Sprintf(` { + "id": %q, "url": %q, "method": "GET", "body": "file://stub/test_body.jsonnet", @@ -1187,7 +1206,7 @@ func TestWebhookEvents(t *testing.T) { "ignore": false, "parse": false } - }`, webhookReceiver.URL+"/ok"))) + }`, whID, webhookReceiver.URL+"/ok"))) recorder := tracetest.NewSpanRecorder() tracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer("test") @@ -1201,31 +1220,37 @@ func TestWebhookEvents(t *testing.T) { ended := recorder.Ended() require.NotEmpty(t, ended) - i := slices.IndexFunc(ended, func(sp sdktrace.ReadOnlySpan) bool { - return sp.Name() == "selfservice.webhook" - }) + i := slices.IndexFunc(ended, func(sp sdktrace.ReadOnlySpan) bool { return sp.Name() == "selfservice.webhook" }) require.GreaterOrEqual(t, i, 0) - events := ended[i].Events() - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { - return ev.Name == "WebhookDelivered" - }) + evs := ended[i].Events() + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == events.WebhookDelivered.String() }) require.GreaterOrEqual(t, i, 0) - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { - return ev.Name == "WebhookSucceeded" - }) + actualWhID, deliveredTriggerID, deliveredRequestID := getAttributes(evs[i].Attributes) + require.Equal(t, whID.String(), actualWhID) + require.NotEmpty(t, deliveredTriggerID) + require.NotEmpty(t, deliveredRequestID) + assert.NotEqual(t, deliveredTriggerID, deliveredRequestID) + + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == events.WebhookSucceeded.String() }) require.GreaterOrEqual(t, i, 0) - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { - return ev.Name == "WebhookFailed" - }) + actualWhID, succeededTriggerID, _ := getAttributes(evs[i].Attributes) + require.Equal(t, whID.String(), actualWhID) + require.NotEmpty(t, succeededTriggerID) + + assert.Equal(t, deliveredTriggerID, succeededTriggerID) + + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == events.WebhookFailed.String() }) require.Equal(t, -1, i) }) t.Run("failed", func(t *testing.T) { + whID := x.NewUUID() wh := hook.NewWebHook(&whDeps, json.RawMessage(fmt.Sprintf(` { + "id": %q, "url": %q, "method": "GET", "body": "file://stub/test_body.jsonnet", @@ -1233,7 +1258,7 @@ func TestWebhookEvents(t *testing.T) { "ignore": false, "parse": false } - }`, webhookReceiver.URL+"/fail"))) + }`, whID, webhookReceiver.URL+"/fail"))) recorder := tracetest.NewSpanRecorder() tracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer("test") @@ -1246,25 +1271,40 @@ func TestWebhookEvents(t *testing.T) { ended := recorder.Ended() require.NotEmpty(t, ended) - i := slices.IndexFunc(ended, func(sp sdktrace.ReadOnlySpan) bool { - return sp.Name() == "selfservice.webhook" - }) + i := slices.IndexFunc(ended, func(sp sdktrace.ReadOnlySpan) bool { return sp.Name() == "selfservice.webhook" }) require.GreaterOrEqual(t, i, 0) - events := ended[i].Events() - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { - return ev.Name == "WebhookDelivered" - }) - require.GreaterOrEqual(t, i, 0) + evs := ended[i].Events() + + var deliveredEvents []sdktrace.Event + deliveredTriggerIDs := map[string]struct{}{} + deliveredRequestIDs := map[string]struct{}{} + for _, ev := range evs { + if ev.Name == events.WebhookDelivered.String() { + deliveredEvents = append(deliveredEvents, ev) + actualWhID, triggerID, requestID := getAttributes(ev.Attributes) + require.Equal(t, whID.String(), actualWhID) + require.NotEmpty(t, triggerID) + require.NotEmpty(t, requestID) + deliveredTriggerIDs[triggerID] = struct{}{} + deliveredRequestIDs[requestID] = struct{}{} + } + } - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { - return ev.Name == "WebhookFailed" - }) + assert.Len(t, deliveredEvents, 3) + assert.Len(t, deliveredTriggerIDs, 1) + assert.Len(t, deliveredRequestIDs, 3) + + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == "WebhookFailed" }) require.GreaterOrEqual(t, i, 0) - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { - return ev.Name == "WebhookSucceeded" - }) + actualWhID, failedTriggerID, _ := getAttributes(evs[i].Attributes) + require.Equal(t, whID.String(), actualWhID) + require.NotEmpty(t, failedTriggerID) + + assert.Contains(t, deliveredTriggerIDs, failedTriggerID) + + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == "WebhookSucceeded" }) require.Equal(t, i, -1) }) @@ -1297,18 +1337,18 @@ func TestWebhookEvents(t *testing.T) { }) require.GreaterOrEqual(t, i, 0) - events := ended[i].Events() - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { + evs := ended[i].Events() + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == "WebhookDelivered" }) require.Equal(t, -1, i) - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == "WebhookFailed" }) require.Equal(t, -1, i) - i = slices.IndexFunc(events, func(ev sdktrace.Event) bool { + i = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == "WebhookSucceeded" }) require.Equal(t, i, -1) diff --git a/x/events/events.go b/x/events/events.go index 178aa7de0f67..95b0b856a4a9 100644 --- a/x/events/events.go +++ b/x/events/events.go @@ -44,91 +44,101 @@ const ( ) const ( - attributeKeySessionID semconv.AttributeKey = "SessionID" - attributeKeySessionAAL semconv.AttributeKey = "SessionAAL" - attributeKeySessionExpiresAt semconv.AttributeKey = "SessionExpiresAt" - attributeKeySelfServiceFlowType semconv.AttributeKey = "SelfServiceFlowType" - attributeKeySelfServiceMethodUsed semconv.AttributeKey = "SelfServiceMethodUsed" - attributeKeySelfServiceSSOProviderUsed semconv.AttributeKey = "SelfServiceSSOProviderUsed" - attributeKeyLoginRequestedAAL semconv.AttributeKey = "LoginRequestedAAL" - attributeKeyLoginRequestedPrivilegedSession semconv.AttributeKey = "LoginRequestedPrivilegedSession" - attributeKeyTokenizedSessionTTL semconv.AttributeKey = "TokenizedSessionTTL" - attributeKeyWebhookURL semconv.AttributeKey = "WebhookURL" - attributeKeyWebhookRequestBody semconv.AttributeKey = "WebhookRequestBody" - attributeKeyWebhookResponseBody semconv.AttributeKey = "WebhookResponseBody" - attributeKeyWebhookResponseStatusCode semconv.AttributeKey = "WebhookResponseStatusCode" - attributeKeyWebhookAttemptNumber semconv.AttributeKey = "WebhookAttemptNumber" - attributeKeyWebhookRequestID semconv.AttributeKey = "WebhookRequestID" - attributeKeyReason semconv.AttributeKey = "Reason" - attributeKeyFlowID semconv.AttributeKey = "FlowID" + AttributeKeySessionID semconv.AttributeKey = "SessionID" + AttributeKeySessionAAL semconv.AttributeKey = "SessionAAL" + AttributeKeySessionExpiresAt semconv.AttributeKey = "SessionExpiresAt" + AttributeKeySelfServiceFlowType semconv.AttributeKey = "SelfServiceFlowType" + AttributeKeySelfServiceMethodUsed semconv.AttributeKey = "SelfServiceMethodUsed" + AttributeKeySelfServiceSSOProviderUsed semconv.AttributeKey = "SelfServiceSSOProviderUsed" + AttributeKeyLoginRequestedAAL semconv.AttributeKey = "LoginRequestedAAL" + AttributeKeyLoginRequestedPrivilegedSession semconv.AttributeKey = "LoginRequestedPrivilegedSession" + AttributeKeyTokenizedSessionTTL semconv.AttributeKey = "TokenizedSessionTTL" + AttributeKeyWebhookID semconv.AttributeKey = "WebhookID" + AttributeKeyWebhookURL semconv.AttributeKey = "WebhookURL" + AttributeKeyWebhookRequestBody semconv.AttributeKey = "WebhookRequestBody" + AttributeKeyWebhookResponseBody semconv.AttributeKey = "WebhookResponseBody" + AttributeKeyWebhookResponseStatusCode semconv.AttributeKey = "WebhookResponseStatusCode" + AttributeKeyWebhookAttemptNumber semconv.AttributeKey = "WebhookAttemptNumber" + AttributeKeyWebhookRequestID semconv.AttributeKey = "WebhookRequestID" + AttributeKeyWebhookTriggerID semconv.AttributeKey = "WebhookTriggerID" + AttributeKeyReason semconv.AttributeKey = "Reason" + AttributeKeyFlowID semconv.AttributeKey = "FlowID" ) func attrSessionID(val uuid.UUID) otelattr.KeyValue { - return otelattr.String(attributeKeySessionID.String(), val.String()) + return otelattr.String(AttributeKeySessionID.String(), val.String()) } func attrTokenizedSessionTTL(ttl time.Duration) otelattr.KeyValue { - return otelattr.String(attributeKeyTokenizedSessionTTL.String(), ttl.String()) + return otelattr.String(AttributeKeyTokenizedSessionTTL.String(), ttl.String()) } func attrSessionAAL(val string) otelattr.KeyValue { - return otelattr.String(attributeKeySessionAAL.String(), val) + return otelattr.String(AttributeKeySessionAAL.String(), val) } func attLoginRequestedAAL(val string) otelattr.KeyValue { - return otelattr.String(attributeKeyLoginRequestedAAL.String(), val) + return otelattr.String(AttributeKeyLoginRequestedAAL.String(), val) } func attSessionExpiresAt(expiresAt time.Time) otelattr.KeyValue { - return otelattr.String(attributeKeySessionExpiresAt.String(), expiresAt.String()) + return otelattr.String(AttributeKeySessionExpiresAt.String(), expiresAt.String()) } func attLoginRequestedPrivilegedSession(val bool) otelattr.KeyValue { - return otelattr.Bool(attributeKeyLoginRequestedPrivilegedSession.String(), val) + return otelattr.Bool(AttributeKeyLoginRequestedPrivilegedSession.String(), val) } func attrSelfServiceFlowType(val string) otelattr.KeyValue { - return otelattr.String(attributeKeySelfServiceFlowType.String(), val) + return otelattr.String(AttributeKeySelfServiceFlowType.String(), val) } func attrSelfServiceMethodUsed(val string) otelattr.KeyValue { - return otelattr.String(attributeKeySelfServiceMethodUsed.String(), val) + return otelattr.String(AttributeKeySelfServiceMethodUsed.String(), val) } func attrSelfServiceSSOProviderUsed(val string) otelattr.KeyValue { - return otelattr.String(attributeKeySelfServiceSSOProviderUsed.String(), val) + return otelattr.String(AttributeKeySelfServiceSSOProviderUsed.String(), val) +} + +func attrWebhookID(id string) otelattr.KeyValue { + return otelattr.String(AttributeKeyWebhookID.String(), id) } func attrWebhookURL(URL *url.URL) otelattr.KeyValue { - return otelattr.String(attributeKeyWebhookURL.String(), URL.Redacted()) + return otelattr.String(AttributeKeyWebhookURL.String(), URL.Redacted()) } func attrWebhookReq(body []byte) otelattr.KeyValue { - return otelattr.String(attributeKeyWebhookRequestBody.String(), string(body)) + return otelattr.String(AttributeKeyWebhookRequestBody.String(), string(body)) } func attrWebhookRes(body []byte) otelattr.KeyValue { - return otelattr.String(attributeKeyWebhookResponseBody.String(), string(body)) + return otelattr.String(AttributeKeyWebhookResponseBody.String(), string(body)) } func attrWebhookStatus(status int) otelattr.KeyValue { - return otelattr.Int(attributeKeyWebhookResponseStatusCode.String(), status) + return otelattr.Int(AttributeKeyWebhookResponseStatusCode.String(), status) } func attrWebhookAttempt(n int) otelattr.KeyValue { - return otelattr.Int(attributeKeyWebhookAttemptNumber.String(), n) + return otelattr.Int(AttributeKeyWebhookAttemptNumber.String(), n) } func attrWebhookRequestID(id uuid.UUID) otelattr.KeyValue { - return otelattr.String(attributeKeyWebhookRequestID.String(), id.String()) + return otelattr.String(AttributeKeyWebhookRequestID.String(), id.String()) +} + +func attrWebhookTriggerID(id uuid.UUID) otelattr.KeyValue { + return otelattr.String(AttributeKeyWebhookTriggerID.String(), id.String()) } func attrReason(err error) otelattr.KeyValue { - return otelattr.String(attributeKeyReason.String(), reasonForError(err)) + return otelattr.String(AttributeKeyReason.String(), reasonForError(err)) } func attrFlowID(id uuid.UUID) otelattr.KeyValue { - return otelattr.String(attributeKeyFlowID.String(), id.String()) + return otelattr.String(AttributeKeyFlowID.String(), id.String()) } func NewSessionIssued(ctx context.Context, aal string, sessionID, identityID uuid.UUID) (string, trace.EventOption) { @@ -355,7 +365,7 @@ func NewSessionJWTIssued(ctx context.Context, sessionID, identityID uuid.UUID, t ) } -func NewWebhookDelivered(ctx context.Context, URL *url.URL, reqBody []byte, status int, resBody []byte, attempt int, requestID uuid.UUID) (string, trace.EventOption) { +func NewWebhookDelivered(ctx context.Context, URL *url.URL, reqBody []byte, status int, resBody []byte, attempt int, requestID, triggerID uuid.UUID, webhookID string) (string, trace.EventOption) { return WebhookDelivered.String(), trace.WithAttributes( append( @@ -366,20 +376,29 @@ func NewWebhookDelivered(ctx context.Context, URL *url.URL, reqBody []byte, stat attrWebhookURL(URL), attrWebhookAttempt(attempt), attrWebhookRequestID(requestID), + attrWebhookID(webhookID), + attrWebhookTriggerID(triggerID), )..., ) } -func NewWebhookSucceeded(ctx context.Context) (string, trace.EventOption) { +func NewWebhookSucceeded(ctx context.Context, triggerID uuid.UUID, webhookID string) (string, trace.EventOption) { return WebhookSucceeded.String(), - trace.WithAttributes(semconv.AttributesFromContext(ctx)...) + trace.WithAttributes( + append( + semconv.AttributesFromContext(ctx), + attrWebhookID(webhookID), + attrWebhookTriggerID(triggerID), + )...) } -func NewWebhookFailed(ctx context.Context, err error) (string, trace.EventOption) { +func NewWebhookFailed(ctx context.Context, err error, triggerID uuid.UUID, id string) (string, trace.EventOption) { return WebhookFailed.String(), trace.WithAttributes( append( semconv.AttributesFromContext(ctx), + attrWebhookID(id), + attrWebhookTriggerID(triggerID), otelattr.String("Error", err.Error()), )..., ) From 02f1a93945be0bc6e3054375f69cea68bb097534 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:12:40 +0000 Subject: [PATCH 082/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b9de6726b8..4ea9a1a0e328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-13)](#2024-11-13) +- [ (2024-11-14)](#2024-11-14) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-13) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-14) ## Breaking Changes @@ -418,6 +418,9 @@ https://github.com/ory-corp/cloud/issues/7176 ### Features +- Add attributes to webhook events for better debugging + ([#4206](https://github.com/ory/kratos/issues/4206)) + ([00da05d](https://github.com/ory/kratos/commit/00da05da9f77bbfb68b364b3ba2a5d0a2d9e4f15)) - Add explicit config flag for secure cookies ([#4180](https://github.com/ory/kratos/issues/4180)) ([2aabe12](https://github.com/ory/kratos/commit/2aabe12e5329acc807c495445999e5591bdf982b)): @@ -426,6 +429,9 @@ https://github.com/ory-corp/cloud/issues/7176 previous behavior of using the dev mode to decide if the cookie should be secure or not. +- Add failure reason to events + ([#4203](https://github.com/ory/kratos/issues/4203)) + ([afa7618](https://github.com/ory/kratos/commit/afa76180e77df0ee0f96eef3b3f2b2d3fe08a33d)) - Add oid as subject source for microsoft ([#4171](https://github.com/ory/kratos/issues/4171)) ([77beb4d](https://github.com/ory/kratos/commit/77beb4de5209cee0bea4b63dfec21d656cf64473)), From 82660f04e2f33d0aa86fccee42c90773a901d400 Mon Sep 17 00:00:00 2001 From: Patrik Date: Thu, 14 Nov 2024 16:29:05 +0100 Subject: [PATCH 083/158] fix: do not roll back transaction on partial identity insert error (#4211) --- identity/handler_test.go | 42 +++++++++++----- identity/manager.go | 10 +++- identity/test/pool.go | 48 +++++++++++++++++++ internal/client-go/go.sum | 1 + .../sql/identity/persister_identity.go | 13 +++-- 5 files changed, 97 insertions(+), 17 deletions(-) diff --git a/identity/handler_test.go b/identity/handler_test.go index 7f1c5285a8cf..6ff1080ee256 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -754,7 +754,7 @@ func TestHandler(t *testing.T) { }) t.Run("suite=PATCH identities", func(t *testing.T) { - t.Run("case=fails on > 100 identities", func(t *testing.T) { + t.Run("case=fails with too many patches", func(t *testing.T) { tooMany := make([]*identity.BatchIdentityPatch, identity.BatchPatchIdentitiesLimit+1) for i := range tooMany { tooMany[i] = &identity.BatchIdentityPatch{Create: validCreateIdentityBody("too-many-patches", i)} @@ -767,8 +767,8 @@ func TestHandler(t *testing.T) { t.Run("case=fails some on a bad identity", func(t *testing.T) { // Test setup: we have a list of valid identitiy patches and a list of invalid ones. // Each run adds one invalid patch to the list and sends it to the server. - // --> we expect the server to fail all patches in the list. - // Finally, we send just the valid patches + // --> we expect the server to fail only the bad patches in the list. + // Finally, we send just valid patches // --> we expect the server to succeed all patches in the list. t.Run("case=invalid patches fail", func(t *testing.T) { @@ -782,24 +782,23 @@ func TestHandler(t *testing.T) { {Create: &identity.CreateIdentityBody{Traits: json.RawMessage(`"invalid traits"`)}}, // <-- invalid traits {Create: validCreateIdentityBody("valid", 4)}, } + expectedToPass := []*identity.BatchIdentityPatch{patches[0], patches[1], patches[3], patches[5], patches[7]} // Create unique IDs for each patch - var patchIDs []string + patchIDs := make([]string, len(patches)) for i, p := range patches { id := uuid.NewV5(uuid.Nil, fmt.Sprintf("%d", i)) p.ID = &id - patchIDs = append(patchIDs, id.String()) + patchIDs[i] = id.String() } req := &identity.BatchPatchIdentitiesBody{Identities: patches} body := send(t, adminTS, "PATCH", "/identities", http.StatusOK, req) var actions []string - for _, a := range body.Get("identities.#.action").Array() { - actions = append(actions, a.String()) - } - assert.Equal(t, + require.NoErrorf(t, json.Unmarshal(([]byte)(body.Get("identities.#.action").Raw), &actions), "%s", body) + assert.Equalf(t, []string{"create", "create", "error", "create", "error", "create", "error", "create"}, - actions, body) + actions, "%s", body) // Check that all patch IDs are returned for i, gotPatchID := range body.Get("identities.#.patch_id").Array() { @@ -811,6 +810,27 @@ func TestHandler(t *testing.T) { assert.Equal(t, "Conflict", body.Get("identities.4.error.status").String()) assert.Equal(t, "Bad Request", body.Get("identities.6.error.status").String()) + var identityIDs []uuid.UUID + require.NoErrorf(t, json.Unmarshal(([]byte)(body.Get("identities.#.identity").Raw), &identityIDs), "%s", body) + + actualIdentities, _, err := reg.Persister().ListIdentities(ctx, identity.ListIdentityParameters{IdsFilter: identityIDs}) + require.NoError(t, err) + actualIdentityIDs := make([]uuid.UUID, len(actualIdentities)) + for i, id := range actualIdentities { + actualIdentityIDs[i] = id.ID + } + assert.ElementsMatchf(t, identityIDs, actualIdentityIDs, "%s", body) + + expectedTraits := make(map[string]string, len(expectedToPass)) + for i, p := range expectedToPass { + expectedTraits[identityIDs[i].String()] = string(p.Create.Traits) + } + actualTraits := make(map[string]string, len(actualIdentities)) + for _, id := range actualIdentities { + actualTraits[id.ID.String()] = string(id.Traits) + } + + assert.Equal(t, expectedTraits, actualTraits) }) t.Run("valid patches succeed", func(t *testing.T) { @@ -1928,7 +1948,7 @@ func validCreateIdentityBody(prefix string, i int) *identity.CreateIdentityBody identity.VerifiableAddressStatusCompleted, } - for j := 0; j < 4; j++ { + for j := range 4 { email := fmt.Sprintf("%s-%d-%d@ory.sh", prefix, i, j) traits.Emails = append(traits.Emails, email) verifiableAddresses = append(verifiableAddresses, identity.VerifiableAddress{ diff --git a/identity/manager.go b/identity/manager.go index 89c0259e6658..a09a08a778cd 100644 --- a/identity/manager.go +++ b/identity/manager.go @@ -333,6 +333,12 @@ type CreateIdentitiesError struct { failedIdentities map[*Identity]*herodot.DefaultError } +func NewCreateIdentitiesError(capacity int) *CreateIdentitiesError { + return &CreateIdentitiesError{ + failedIdentities: make(map[*Identity]*herodot.DefaultError, capacity), + } +} + func (e *CreateIdentitiesError) Error() string { e.init() return fmt.Sprintf("create identities error: %d identities failed", len(e.failedIdentities)) @@ -370,7 +376,7 @@ func (e *CreateIdentitiesError) Find(ident *Identity) *FailedIdentity { return nil } func (e *CreateIdentitiesError) ErrOrNil() error { - if len(e.failedIdentities) == 0 { + if e == nil || len(e.failedIdentities) == 0 { return nil } return e @@ -385,7 +391,7 @@ func (m *Manager) CreateIdentities(ctx context.Context, identities []*Identity, ctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, "identity.Manager.CreateIdentities") defer otelx.End(span, &err) - createIdentitiesError := &CreateIdentitiesError{} + createIdentitiesError := NewCreateIdentitiesError(len(identities)) validIdentities := make([]*Identity, 0, len(identities)) for _, ident := range identities { if ident.SchemaID == "" { diff --git a/identity/test/pool.go b/identity/test/pool.go index 4f898917449f..2e53fa2a53a2 100644 --- a/identity/test/pool.go +++ b/identity/test/pool.go @@ -350,12 +350,60 @@ func TestPool(ctx context.Context, p persistence.Persister, m *identity.Manager, assert.Equal(t, id.Credentials["password"].Identifiers, credFromDB.Identifiers) assert.WithinDuration(t, time.Now().UTC(), credFromDB.CreatedAt, time.Minute) assert.WithinDuration(t, time.Now().UTC(), credFromDB.UpdatedAt, time.Minute) + // because of mysql precision assert.WithinDuration(t, id.CreatedAt, idFromDB.CreatedAt, time.Second) assert.WithinDuration(t, id.UpdatedAt, idFromDB.UpdatedAt, time.Second) require.NoError(t, p.DeleteIdentity(ctx, id.ID)) } }) + + t.Run("create exactly the non-conflicting ones", func(t *testing.T) { + identities := make([]*identity.Identity, 100) + for i := range identities { + identities[i] = NewTestIdentity(4, "persister-create-multiple-2", i%60) + } + err := p.CreateIdentities(ctx, identities...) + if dbname == "mysql" { + // partial inserts are not supported on mysql + assert.ErrorIs(t, err, sqlcon.ErrUniqueViolation) + return + } + + errWithCtx := new(identity.CreateIdentitiesError) + require.ErrorAsf(t, err, &errWithCtx, "%#v", err) + + for _, id := range identities[:60] { + require.NotZero(t, id.ID) + + idFromDB, err := p.GetIdentity(ctx, id.ID, identity.ExpandEverything) + require.NoError(t, err) + + credFromDB := idFromDB.Credentials[identity.CredentialsTypePassword] + assert.Equal(t, id.ID, idFromDB.ID) + assert.Equal(t, id.SchemaID, idFromDB.SchemaID) + assert.Equal(t, id.SchemaURL, idFromDB.SchemaURL) + assert.Equal(t, id.State, idFromDB.State) + + // We test that the values are plausible in the handler test already. + assert.Equal(t, len(id.VerifiableAddresses), len(idFromDB.VerifiableAddresses)) + assert.Equal(t, len(id.RecoveryAddresses), len(idFromDB.RecoveryAddresses)) + + assert.Equal(t, id.Credentials["password"].Identifiers, credFromDB.Identifiers) + assert.WithinDuration(t, time.Now().UTC(), credFromDB.CreatedAt, time.Minute) + assert.WithinDuration(t, time.Now().UTC(), credFromDB.UpdatedAt, time.Minute) + // because of mysql precision + assert.WithinDuration(t, id.CreatedAt, idFromDB.CreatedAt, time.Second) + assert.WithinDuration(t, id.UpdatedAt, idFromDB.UpdatedAt, time.Second) + + require.NoError(t, p.DeleteIdentity(ctx, id.ID)) + } + + for _, id := range identities[60:] { + failed := errWithCtx.Find(id) + assert.NotNil(t, failed) + } + }) }) t.Run("case=should error when the identity ID does not exist", func(t *testing.T) { diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 489b1fbb4360..5b29017779ca 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -561,7 +561,8 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... } }() - return p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { + var partialErr *identity.CreateIdentitiesError + if err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { conn := &batch.TracerConnection{ Tracer: p.r.Tracer(ctx), Connection: tx, @@ -569,6 +570,7 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... succeededIDs = make([]uuid.UUID, 0, len(identities)) failedIdentityIDs := make(map[uuid.UUID]struct{}) + partialErr = nil // Don't use batch.WithPartialInserts, because identities have no other // constraints other than the primary key that could cause conflicts. @@ -620,7 +622,7 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... // If any of the batch inserts failed on conflict, let's delete the corresponding // identities and return a list of failed identities in the error. if len(failedIdentityIDs) > 0 { - partialErr := &identity.CreateIdentitiesError{} + partialErr = identity.NewCreateIdentitiesError(len(failedIdentityIDs)) failedIDs := make([]uuid.UUID, 0, len(failedIdentityIDs)) for _, ident := range identities { @@ -637,7 +639,7 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... return sqlcon.HandleError(err) } - return partialErr + return nil } else { // No failures: report all identities as created. for _, ident := range identities { @@ -646,7 +648,10 @@ func (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ... } return nil - }) + }); err != nil { + return err + } + return partialErr.ErrOrNil() } func (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i *identity.Identity, expand identity.Expandables) (err error) { From c7e46a4668d69ea37beb1af0ca8015916ab0877e Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:30:57 +0000 Subject: [PATCH 084/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 7c24b7755ecac7454f1a3b9cf097c92a5e2a80a6 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:21:00 +0000 Subject: [PATCH 085/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ea9a1a0e328..91c4a3307736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -363,6 +363,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Add exists clause ([#4191](https://github.com/ory/kratos/issues/4191)) ([a313dd6](https://github.com/ory/kratos/commit/a313dd6ba6d823deb40f14c738e3b609dbaad56c)) +- Do not roll back transaction on partial identity insert error + ([#4211](https://github.com/ory/kratos/issues/4211)) + ([82660f0](https://github.com/ory/kratos/commit/82660f04e2f33d0aa86fccee42c90773a901d400)) - Duplicate autocomplete trigger ([6bbf915](https://github.com/ory/kratos/commit/6bbf91593a37e4973a86f610290ebab44df8dc81)) - Enable b2b_sso hook in more places From e1f29c2d3524f9444ec067c52d2c9f1d44fa6539 Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Mon, 18 Nov 2024 14:43:32 +0100 Subject: [PATCH 086/158] fix: add missing autocomplete attributes to identifier_first strategy (#4215) --- ...od=PopulateLoginMethodIdentifierFirstIdentification.json | 1 + selfservice/strategy/idfirst/strategy_login.go | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json b/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json index 086d65ade752..73e408add0a0 100644 --- a/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json +++ b/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json @@ -20,6 +20,7 @@ "type": "text", "value": "", "required": true, + "autocomplete": "username email", "disabled": false, "node_type": "input" }, diff --git a/selfservice/strategy/idfirst/strategy_login.go b/selfservice/strategy/idfirst/strategy_login.go index 17c39fe914d3..0cc7b274b30e 100644 --- a/selfservice/strategy/idfirst/strategy_login.go +++ b/selfservice/strategy/idfirst/strategy_login.go @@ -136,6 +136,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, if !ok { continue } + attrs.Autocomplete = "username email" attrs.Type = node.InputAttributeTypeHidden f.UI.Nodes[k].Attributes = attrs @@ -184,7 +185,10 @@ func (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Requ return err } - f.UI.SetNode(node.NewInputField("identifier", "", s.NodeGroup(), node.InputAttributeTypeText, node.WithRequiredInputAttribute).WithMetaLabel(identifierLabel)) + f.UI.SetNode(node.NewInputField("identifier", "", s.NodeGroup(), node.InputAttributeTypeText, node.WithInputAttributes(func(a *node.InputAttributes) { + a.Autocomplete = "username email" + a.Required = true + })).WithMetaLabel(identifierLabel)) f.UI.GetNodes().Append(node.NewInputField("method", s.ID(), s.NodeGroup(), node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelContinue())) return nil } From 05409be352081386a7a9db2e270d77b07f57e926 Mon Sep 17 00:00:00 2001 From: Ferdynand Naczynski Date: Mon, 18 Nov 2024 14:48:09 +0100 Subject: [PATCH 087/158] chore: pin GHA PM action version (#4213) --- .github/workflows/pm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pm.yml b/.github/workflows/pm.yml index b661cd23126e..dc6a5bcd129f 100644 --- a/.github/workflows/pm.yml +++ b/.github/workflows/pm.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: ory-corp/planning-automation-action@main + - uses: ory-corp/planning-automation-action@v0.1 with: project: 5 organization: ory-corp From f076fe4e1487f67f355eaa7f238090abf3796578 Mon Sep 17 00:00:00 2001 From: Patrik Date: Mon, 18 Nov 2024 16:36:29 +0100 Subject: [PATCH 088/158] docs: remove unused SMS config from schema (#4212) --- embedx/config.schema.json | 72 +------------------ .../root.courierSMS.yaml | 13 ++-- 2 files changed, 5 insertions(+), 80 deletions(-) diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 020a8d74b50c..48bda1c3d7f7 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -2148,7 +2148,7 @@ "smtps://subdomain.my-mailserver:1234/?server_name=my-mailserver (allows TLS to work if the server is hosted on a sudomain that uses a non-wildcard domain certificate)" ], "type": "string", - "pattern": "^smtps?:\\/\\/.*" + "pattern": "^smtps?://.*" }, "client_cert_path": { "title": "SMTP Client certificate path", @@ -2199,76 +2199,6 @@ }, "additionalProperties": false }, - "sms": { - "title": "SMS sender configuration", - "description": "Configures outgoing sms messages using HTTP protocol with generic SMS provider", - "type": "object", - "properties": { - "enabled": { - "description": "Determines if SMS functionality is enabled", - "type": "boolean", - "default": false - }, - "from": { - "title": "SMS Sender Address", - "description": "The recipient of a sms will see this as the sender address.", - "type": "string", - "default": "Ory Kratos" - }, - "request_config": { - "type": "object", - "properties": { - "url": { - "title": "HTTP address of API endpoint", - "description": "This URL will be used to connect to the SMS provider.", - "examples": ["https://api.twillio.com/sms/send"], - "type": "string", - "pattern": "^https?:\\/\\/.*" - }, - "method": { - "type": "string", - "description": "The HTTP method to use (GET, POST, etc)." - }, - "headers": { - "type": "object", - "description": "The HTTP headers that must be applied to request", - "additionalProperties": { - "type": "string" - } - }, - "body": { - "type": "string", - "format": "uri", - "pattern": "^(http|https|file|base64)://", - "description": "URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods, which support HTTP body payloads", - "examples": [ - "file:///path/to/body.jsonnet", - "file://./body.jsonnet", - "base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=", - "https://oryapis.com/default_body.jsonnet" - ] - }, - "auth": { - "type": "object", - "title": "Auth mechanisms", - "description": "Define which auth mechanism to use for auth with the SMS provider", - "oneOf": [ - { - "$ref": "#/definitions/webHookAuthApiKeyProperties" - }, - { - "$ref": "#/definitions/webHookAuthBasicAuthProperties" - } - ] - }, - "additionalProperties": false - }, - "required": ["url", "method"], - "additionalProperties": false - } - }, - "additionalProperties": false - }, "channels": { "type": "array", "items": { diff --git a/test/schema/fixtures/config.schema.test.success/root.courierSMS.yaml b/test/schema/fixtures/config.schema.test.success/root.courierSMS.yaml index b9b73bcb10a0..dc015e064a2d 100644 --- a/test/schema/fixtures/config.schema.test.success/root.courierSMS.yaml +++ b/test/schema/fixtures/config.schema.test.success/root.courierSMS.yaml @@ -13,12 +13,7 @@ courier: smtp: connection_uri: smtps://foo:bar@my-mailserver:1234/ from_address: no-reply@ory.kratos.sh - sms: - enabled: true - from: "+19592155527" - request_config: - url: https://sms.example.com - method: POST - body: file://request.config.twilio.jsonnet - headers: - 'Content-Type': "application/x-www-form-urlencoded" + channels: + - id: sms + type: http + request_config: "#/definitions/httpRequestConfig" From 05c5e4885e6f313d6e729f49d96bae9a6760b85b Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:28:11 +0000 Subject: [PATCH 089/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c4a3307736..091e97d7e179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-14)](#2024-11-14) +- [ (2024-11-18)](#2024-11-18) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-14) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-18) ## Breaking Changes @@ -363,6 +363,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Add exists clause ([#4191](https://github.com/ory/kratos/issues/4191)) ([a313dd6](https://github.com/ory/kratos/commit/a313dd6ba6d823deb40f14c738e3b609dbaad56c)) +- Add missing autocomplete attributes to identifier_first strategy + ([#4215](https://github.com/ory/kratos/issues/4215)) + ([e1f29c2](https://github.com/ory/kratos/commit/e1f29c2d3524f9444ec067c52d2c9f1d44fa6539)) - Do not roll back transaction on partial identity insert error ([#4211](https://github.com/ory/kratos/issues/4211)) ([82660f0](https://github.com/ory/kratos/commit/82660f04e2f33d0aa86fccee42c90773a901d400)) @@ -415,6 +418,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Clarify facebook graph API versioning ([#4208](https://github.com/ory/kratos/issues/4208)) ([a90df58](https://github.com/ory/kratos/commit/a90df5852ba96704863cc576edcb8286eaa9b3f9)) +- Remove unused SMS config from schema + ([#4212](https://github.com/ory/kratos/issues/4212)) + ([f076fe4](https://github.com/ory/kratos/commit/f076fe4e1487f67f355eaa7f238090abf3796578)) - Usage of `organization` parameter in native self-service flows ([#4176](https://github.com/ory/kratos/issues/4176)) ([cb71e38](https://github.com/ory/kratos/commit/cb71e38147d21f73e9bd1e081dc3443abb63353e)) From 7d0e78a4f6631b0662beee3b8e9dd0d774b875ea Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:31:36 +0100 Subject: [PATCH 090/158] fix: incorrect query plan (#4218) --- internal/client-go/go.sum | 1 + persistence/sql/identity/persister_identity.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 5b29017779ca..8d5a08f04415 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -924,8 +924,8 @@ func (p *IdentityPersister) ListIdentities(ctx context.Context, params identity. wheres += fmt.Sprintf(` AND ic.nid = ? AND ici.nid = ? - AND ((ic.identity_credential_type_id IN (?, ?, ?) AND ici.identifier %s ?) - OR (ic.identity_credential_type_id IN (?) AND ici.identifier %s ?)) + AND ((ici.identity_credential_type_id IN (?, ?, ?) AND ici.identifier %s ?) + OR (ici.identity_credential_type_id IN (?) AND ici.identifier %s ?)) `, identifierOperator, identifierOperator) args = append(args, nid, nid, From 751ba69f66b0308581016430741b1fae4c5cddfd Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:33:09 +0000 Subject: [PATCH 091/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 0d25727b15a1a28d1fdbaa95c18be03f3cf3c56c Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:24:18 +0000 Subject: [PATCH 092/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 091e97d7e179..f787b19b49f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-18)](#2024-11-18) +- [ (2024-11-21)](#2024-11-21) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-18) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-21) ## Breaking Changes @@ -383,6 +383,8 @@ https://github.com/ory-corp/cloud/issues/7176 - Gracefully handle unused index ([#4196](https://github.com/ory/kratos/issues/4196)) ([3dbeb64](https://github.com/ory/kratos/commit/3dbeb64b3f99a3aeba5f7126c301b72fda4c3e3c)) +- Incorrect query plan ([#4218](https://github.com/ory/kratos/issues/4218)) + ([7d0e78a](https://github.com/ory/kratos/commit/7d0e78a4f6631b0662beee3b8e9dd0d774b875ea)) - Order-by clause and span names ([#4200](https://github.com/ory/kratos/issues/4200)) ([b6278af](https://github.com/ory/kratos/commit/b6278af5c7ed7fb845a71ad0e64f8b87402a8f4b)) From e6d2d4d0c04e60ab5b0658b9e5c4c52104446368 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Thu, 21 Nov 2024 17:46:42 +0100 Subject: [PATCH 093/158] fix: use context for readiness probes (#4219) --- driver/factory_test.go | 5 ++++- driver/registry_default.go | 14 ++++++++++---- go.mod | 4 ++-- go.sum | 12 ++++-------- persistence/reference.go | 10 +++++----- persistence/sql/persister.go | 9 ++------- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/driver/factory_test.go b/driver/factory_test.go index d5c646550a87..f5b622520deb 100644 --- a/driver/factory_test.go +++ b/driver/factory_test.go @@ -7,6 +7,7 @@ import ( "context" "os" "testing" + "time" "github.com/ory/x/servicelocatorx" @@ -35,7 +36,9 @@ func TestDriverNew(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, config.DefaultSQLiteMemoryDSN, r.Config().DSN(ctx)) - require.NoError(t, r.Persister().Ping()) + pingCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + t.Cleanup(cancel) + require.NoError(t, r.Persister().Ping(pingCtx)) assert.NotEqual(t, uuid.Nil.String(), r.Persister().NetworkID(context.Background()).String()) diff --git a/driver/registry_default.go b/driver/registry_default.go index fdf78f41f44e..464f7881f626 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -253,8 +253,8 @@ func (m *RegistryDefault) HealthHandler(_ context.Context) *healthx.Handler { if m.healthxHandler == nil { m.healthxHandler = healthx.NewHandler(m.Writer(), config.Version, healthx.ReadyCheckers{ - "database": func(_ *http.Request) error { - return m.Ping() + "database": func(r *http.Request) error { + return m.PingContext(r.Context()) }, "migrations": func(r *http.Request) error { if m.migrationStatus != nil && !m.migrationStatus.HasPending() { @@ -683,7 +683,9 @@ func (m *RegistryDefault) Init(ctx context.Context, ctxer contextx.Contextualize return err } - if err := p.Ping(); err != nil { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if err := c.Store.SQLDB().PingContext(ctx); err != nil { m.Logger().WithError(err).Warnf("Unable to ping database, retrying.") return err } @@ -810,8 +812,12 @@ func (m *RegistryDefault) Persister() persistence.Persister { return m.persister } +func (m *RegistryDefault) PingContext(ctx context.Context) error { + return m.persister.Ping(ctx) +} + func (m *RegistryDefault) Ping() error { - return m.persister.Ping() + return m.persister.Ping(context.Background()) } func (m *RegistryDefault) WithCSRFTokenGenerator(cg x.CSRFToken) { diff --git a/go.mod b/go.mod index b866e2ab7e24..96d6e9b287ba 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22 replace ( // https://github.com/gobuffalo/pop/pull/833 - github.com/gobuffalo/pop/v6 => github.com/ory/pop/v6 v6.2.0 + github.com/gobuffalo/pop/v6 => github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b github.com/gorilla/sessions => github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.22 @@ -70,7 +70,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.665-0.20241031130226-ae5097122246 + github.com/ory/x v0.0.669 github.com/peterhellberg/link v1.2.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 84a69ac01de5..54629e85036f 100644 --- a/go.sum +++ b/go.sum @@ -252,8 +252,6 @@ github.com/gobuffalo/github_flavored_markdown v1.1.4 h1:WacrEGPXUDX+BpU1GM/Y0ADg github.com/gobuffalo/github_flavored_markdown v1.1.4/go.mod h1:Vl9686qrVVQou4GrHRK/KOG3jCZOKLUqV8MMOAYtlso= github.com/gobuffalo/helpers v0.6.7 h1:C9CedoRSfgWg2ZoIkVXgjI5kgmSpL34Z3qdnzpfNVd8= github.com/gobuffalo/helpers v0.6.7/go.mod h1:j0u1iC1VqlCaJEEVkZN8Ia3TEzfj/zoXANqyJExTMTA= -github.com/gobuffalo/here v0.6.7 h1:hpfhh+kt2y9JLDfhYUxxCRxQol540jsVfKUZzjlbp8o= -github.com/gobuffalo/here v0.6.7/go.mod h1:vuCfanjqckTuRlqAitJz6QC4ABNnS27wLb816UhsPcc= github.com/gobuffalo/httptest v1.5.2 h1:GpGy520SfY1QEmyPvaqmznTpG4gEQqQ82HtHqyNEreM= github.com/gobuffalo/httptest v1.5.2/go.mod h1:FA23yjsWLGj92mVV74Qtc8eqluc11VqcWr8/C1vxt4g= github.com/gobuffalo/nulls v0.4.2 h1:GAqBR29R3oPY+WCC7JL9KKk9erchaNuV6unsOSZGQkw= @@ -547,8 +545,6 @@ github.com/mailhog/storage v1.0.1 h1:uut2nlG5hIxbsl6f8DGznPAHwQLf3/7Na2t4gmrIais github.com/mailhog/storage v1.0.1/go.mod h1:4EAUf5xaEVd7c/OhvSxOOwQ66jT6q2er+BDBQ0EVrew= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno= -github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -638,12 +634,12 @@ github.com/ory/mail/v3 v3.0.0 h1:8LFMRj473vGahFD/ntiotWEd4S80FKYFtiZTDfOQ+sM= github.com/ory/mail/v3 v3.0.0/go.mod h1:JGAVeZF8YAlxbaFDUHqRZAKBCSeW2w1vuxf28hFbZAw= github.com/ory/nosurf v1.2.7 h1:YrHrbSensQyU6r6HT/V5+HPdVEgrOTMJiLoJABSBOp4= github.com/ory/nosurf v1.2.7/go.mod h1:d4L3ZBa7Amv55bqxCBtCs63wSlyaiCkWVl4vKf3OUxA= -github.com/ory/pop/v6 v6.2.0 h1:hRFOGAOEHw91kUHQ32k5NHqCkcHrRou/romvrJP1w0E= -github.com/ory/pop/v6 v6.2.0/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= +github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b h1:BIzoOe2/wynZBQak1po0tzgvARseIKsR2bF6b+SZoKE= +github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/ory/x v0.0.665-0.20241031130226-ae5097122246 h1:h6Jt8glJkehgQxr7MP3q5gmR4Ub0RWqlVgPXTnJU5rs= -github.com/ory/x v0.0.665-0.20241031130226-ae5097122246/go.mod h1:7SCTki3N0De3ZpqlxhxU/94ZrOCfNEnXwVtd0xVt+L8= +github.com/ory/x v0.0.669 h1:pBrju8B5Oie9RjebOwWf1Sj+6dPNIPI3nkVeC8rjUno= +github.com/ory/x v0.0.669/go.mod h1:0Av1u/Gh7WXCrEDJJnySAJrDzluaWllOfl5zqf9Dky8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= diff --git a/persistence/reference.go b/persistence/reference.go index d3ceeb8d26b5..72986eb6fe61 100644 --- a/persistence/reference.go +++ b/persistence/reference.go @@ -58,13 +58,13 @@ type Persister interface { CleanupDatabase(context.Context, time.Duration, time.Duration, int) error Close(context.Context) error - Ping() error - MigrationStatus(c context.Context) (popx.MigrationStatuses, error) - MigrateDown(c context.Context, steps int) error - MigrateUp(c context.Context) error + Ping(context.Context) error + MigrationStatus(context.Context) (popx.MigrationStatuses, error) + MigrateDown(ctx context.Context, steps int) error + MigrateUp(context.Context) error Migrator() *popx.Migrator MigrationBox() *popx.MigrationBox - GetConnection(ctx context.Context) *pop.Connection + GetConnection(context.Context) *pop.Connection x.TransactionalPersister Networker } diff --git a/persistence/sql/persister.go b/persistence/sql/persister.go index 6939857c372b..9962b373255f 100644 --- a/persistence/sql/persister.go +++ b/persistence/sql/persister.go @@ -178,13 +178,8 @@ func (p *Persister) Close(ctx context.Context) error { return errors.WithStack(p.GetConnection(ctx).Close()) } -func (p *Persister) Ping() error { - type pinger interface { - Ping() error - } - - // This can not be contextualized because of some gobuffalo/pop limitations. - return errors.WithStack(p.c.Store.(pinger).Ping()) +func (p *Persister) Ping(ctx context.Context) error { + return errors.WithStack(p.c.Store.SQLDB().PingContext(ctx)) } func (p *Persister) CleanupDatabase(ctx context.Context, wait time.Duration, older time.Duration, batchSize int) error { From 0062d45b6c9a6323f9dccb10f63dce752836c29e Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:31:49 +0100 Subject: [PATCH 094/158] docs: improve SecurityError error message for ory elements local (#4205) --- internal/client-go/go.sum | 1 + ...Login-flow=passwordless-case=passkey_button_exists.json | 2 +- ...resh-case=refresh_passwordless_credentials-browser.json | 2 +- ...=refresh-case=refresh_passwordless_credentials-spa.json | 2 +- ...tings-case=a_device_is_shown_which_can_be_unlinked.json | 2 +- ...pleteSettings-case=one_activation_element_is_shown.json | 2 +- ...ormHydration-method=PopulateLoginMethodFirstFactor.json | 2 +- ...ation-method=PopulateLoginMethodFirstFactorRefresh.json | 2 +- ...d=PopulateLoginMethodIdentifierFirstIdentification.json | 2 +- ...estRegistration-case=passkey_button_exists-browser.json | 2 +- .../TestRegistration-case=passkey_button_exists-spa.json | 2 +- ...webauthn_payload_is_set_when_identity_has_webauthn.json | 2 +- ...uld_fail_if_webauthn_login_is_invalid-type=browser.json | 2 +- ...=should_fail_if_webauthn_login_is_invalid-type=spa.json | 2 +- ...less_enabled=false-case=mfa_v0_credentials-browser.json | 2 +- ...wordless_enabled=false-case=mfa_v0_credentials-spa.json | 2 +- ...less_enabled=false-case=mfa_v1_credentials-browser.json | 2 +- ...wordless_enabled=false-case=mfa_v1_credentials-spa.json | 2 +- ...enabled=true-case=passwordless_credentials-browser.json | 2 +- ...ess_enabled=true-case=passwordless_credentials-spa.json | 2 +- ...tings-case=a_device_is_shown_which_can_be_unlinked.json | 2 +- ...pleteSettings-case=one_activation_element_is_shown.json | 2 +- ...resh-case=mfa_enabled_and_user_has_mfa_credentials.json | 2 +- ...less_enabled_and_user_has_passwordless_credentials.json | 2 +- ...d=PopulateLoginMethodSecondFactor-case=mfa_enabled.json | 2 +- ...stRegistration-case=webauthn_button_exists-browser.json | 2 +- .../TestRegistration-case=webauthn_button_exists-spa.json | 2 +- x/webauthnx/js/webauthn.js | 7 ++++++- 28 files changed, 33 insertions(+), 27 deletions(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json index 8e6ca347223f..39b1e8a8ca59 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json @@ -38,7 +38,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json index 83a0dab00cf1..269754d1dbd0 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json index 83a0dab00cf1..269754d1dbd0 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index b8193ddec074..354fdfab6feb 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -110,7 +110,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index f670fa605662..3065bddabb0f 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -62,7 +62,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json index 5ca8b52290f1..9ea8913db0aa 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json index d2838777ddb8..0d33b6d7d9fb 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json @@ -18,7 +18,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json index 1fe32d3cd487..911497b207da 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json index bba362f0c308..c0d75cd7cd1d 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json index bba362f0c308..c0d75cd7cd1d 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json index 08d46bc5ee98..4d8766c503b9 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json @@ -42,7 +42,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json index 68c962a81650..d26936d42077 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json index 68c962a81650..d26936d42077 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json index a3dd14c42d98..a17789700612 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json index a3dd14c42d98..a17789700612 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json index a3dd14c42d98..a17789700612 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json index a3dd14c42d98..a17789700612 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json index a3dd14c42d98..a17789700612 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json index a3dd14c42d98..a17789700612 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index c905ffda56b7..1d38764e30a6 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -116,7 +116,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index e6fb889e7262..628b00fd8b5f 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -68,7 +68,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json index e4ca52133186..bd8b5253db96 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json index e4ca52133186..bd8b5253db96 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json index e4ca52133186..bd8b5253db96 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json index d1236c755981..4d51e6ea1536 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json index d1236c755981..4d51e6ea1536 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-tNeczCRytwdJg3Ncuj/DqWtyToUJS9Nnvt0FUbtglMgj8rowm19qLRKdWebaaDhpxiWxIi/6piZrgEUjOu/MCA==", + "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/x/webauthnx/js/webauthn.js b/x/webauthnx/js/webauthn.js index 052ce7f355c5..4bc0d4427aa9 100644 --- a/x/webauthnx/js/webauthn.js +++ b/x/webauthnx/js/webauthn.js @@ -272,7 +272,12 @@ }) .catch((err) => { // Calling this again will enable the autocomplete once again. - console.error(err) + if (err instanceof DOMException && err.name === "SecurityError") { + console.error(`A security exception occurred while loading Passkeys / WebAuthn. To troubleshoot, please head over to https://www.ory.sh/docs/troubleshooting/passkeys-webauthn-security-error. The original error message is: ${err.message}`) + } else { + console.error("[Ory/Passkey] An unknown error occurred while getting passkey credentials", err) + } + console.trace(err) window.abortPasskeyConditionalUI && __oryPasskeyLoginAutocompleteInit() }) From a82d288014411ae4eb82c718bfe825ca55b4fab0 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:32:07 +0100 Subject: [PATCH 095/158] feat: support android webauthn origins (#4155) This patch adds the ability to verify Android APK origins used during WebAuthn/Passkey exchange. Upgrades go-webauthn and includes fixes for Go 1.23 and workarounds for Swagger. --- .docker/Dockerfile-build | 2 +- .docker/Dockerfile-debug | 2 +- .github/workflows/ci.yaml | 8 +- .github/workflows/format.yml | 2 +- .github/workflows/licenses.yml | 2 +- .golangci.yml | 1 + .schema/openapi/patches/selfservice.yaml | 58 +++++++---- driver/config/config.go | 1 + embedx/config.schema.json | 1 - go.mod | 18 ++-- go.sum | 28 +++--- hash/hash_comparator.go | 11 +-- identity/credentials_webauthn.go | 97 ++++++++++++++++--- identity/credentials_webauthn_test.go | 10 +- identity/handler_test.go | 8 +- internal/client-go/model_login_flow_state.go | 2 +- .../client-go/model_recovery_flow_state.go | 2 +- .../model_registration_flow_state.go | 2 +- .../client-go/model_settings_flow_state.go | 2 +- .../model_verification_flow_state.go | 2 +- internal/httpclient/model_login_flow_state.go | 2 +- .../httpclient/model_recovery_flow_state.go | 2 +- .../model_registration_flow_state.go | 2 +- .../httpclient/model_settings_flow_state.go | 2 +- .../model_verification_flow_state.go | 2 +- schema/handler.go | 13 ++- schema/handler_test.go | 2 +- .../success/android/internal_context.json | 7 ++ .../success/android/response.json | 9 ++ .../success/{ => browser}/identity.json | 0 .../{ => browser}/internal_context.json | 0 .../success/{ => browser}/response.json | 0 selfservice/strategy/passkey/passkey_login.go | 3 +- .../passkey/passkey_registration_test.go | 65 ++++++++++++- .../strategy/passkey/testfixture_test.go | 33 +++++-- selfservice/strategy/webauthn/login.go | 4 +- spec/api.json | 22 ++--- spec/swagger.json | 25 ----- test/e2e/mock/httptarget/go.mod | 2 +- 39 files changed, 308 insertions(+), 146 deletions(-) create mode 100644 selfservice/strategy/passkey/fixtures/registration/success/android/internal_context.json create mode 100644 selfservice/strategy/passkey/fixtures/registration/success/android/response.json rename selfservice/strategy/passkey/fixtures/registration/success/{ => browser}/identity.json (100%) rename selfservice/strategy/passkey/fixtures/registration/success/{ => browser}/internal_context.json (100%) rename selfservice/strategy/passkey/fixtures/registration/success/{ => browser}/response.json (100%) diff --git a/.docker/Dockerfile-build b/.docker/Dockerfile-build index bd619930f0a9..687d8834012f 100644 --- a/.docker/Dockerfile-build +++ b/.docker/Dockerfile-build @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1-experimental -FROM golang:1.22-bullseye AS builder +FROM golang:1.23-bullseye AS builder RUN apt-get update && apt-get upgrade -y &&\ mkdir -p /var/lib/sqlite diff --git a/.docker/Dockerfile-debug b/.docker/Dockerfile-debug index a309b5ad92bb..97a0e2b72525 100644 --- a/.docker/Dockerfile-debug +++ b/.docker/Dockerfile-debug @@ -1,4 +1,4 @@ -FROM golang:1.22-bullseye +FROM golang:1.23-bullseye ENV CGO_ENABLED 1 RUN apt-get update && apt-get install -y --no-install-recommends inotify-tools psmisc diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e7c488f0d496..9fc74bd6397f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -79,7 +79,7 @@ jobs: fetch-depth: 2 - uses: actions/setup-go@v4 with: - go-version: "1.22" + go-version: "1.23" - run: go list -json > go.list - name: Run nancy uses: sonatype-nexus-community/nancy-github-action@v1.0.2 @@ -93,7 +93,7 @@ jobs: GOGC: 100 with: args: --timeout 10m0s - version: v1.59.1 + version: v1.61.0 - name: Build Kratos run: make install - name: Run go-acc (tests) @@ -169,7 +169,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v4 with: - go-version: "1.22" + go-version: "1.23" - name: Install selfservice-ui-react-native uses: actions/checkout@v3 @@ -273,7 +273,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v4 with: - go-version: "1.22" + go-version: "1.23" - run: go build -tags sqlite,json1 . - name: Install selfservice-ui-react-native diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 7e243923b8ca..bb107819d849 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: "1.22" + go-version: "1.23" - run: make format - name: Indicate formatting issues run: git diff HEAD --exit-code --color diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 8a86486031de..9d1589506da2 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "1.22" + go-version: "1.23" - uses: actions/setup-node@v2 with: node-version: "18" diff --git a/.golangci.yml b/.golangci.yml index e83dd5a56a2e..81b4a23960df 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,3 +29,4 @@ issues: - "Set is deprecated: use context-based WithConfigValue instead" - "SetDefaultIdentitySchemaFromRaw is deprecated: Use context-based WithDefaultIdentitySchemaFromRaw instead" - "SetDefaultIdentitySchema is deprecated: Use context-based WithDefaultIdentitySchema instead" + - "G115" diff --git a/.schema/openapi/patches/selfservice.yaml b/.schema/openapi/patches/selfservice.yaml index 3fe24b62fb5a..39a329bf5928 100644 --- a/.schema/openapi/patches/selfservice.yaml +++ b/.schema/openapi/patches/selfservice.yaml @@ -32,11 +32,15 @@ passkey: "#/components/schemas/updateRegistrationFlowWithPasskeyMethod" profile: "#/components/schemas/updateRegistrationFlowWithProfileMethod" - op: add - path: /components/schemas/registrationFlowState/enum + path: /components/schemas/registrationFlowState value: - - choose_method - - sent_email - - passed_challenge + title: Registration flow state (experimental) + description: The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change! + type: string + enum: + - choose_method + - sent_email + - passed_challenge # end # All modifications for the login flow @@ -67,11 +71,15 @@ passkey: "#/components/schemas/updateLoginFlowWithPasskeyMethod" identifier_first: "#/components/schemas/updateLoginFlowWithIdentifierFirstMethod" - op: add - path: /components/schemas/loginFlowState/enum + path: /components/schemas/loginFlowState value: - - choose_method - - sent_email - - passed_challenge + title: Login flow state (experimental) + description: The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change! + type: string + enum: + - choose_method + - sent_email + - passed_challenge # end # All modifications for the recovery flow @@ -90,11 +98,15 @@ link: "#/components/schemas/updateRecoveryFlowWithLinkMethod" code: "#/components/schemas/updateRecoveryFlowWithCodeMethod" - op: add - path: /components/schemas/recoveryFlowState/enum + path: /components/schemas/recoveryFlowState + type: string value: - - choose_method - - sent_email - - passed_challenge + title: Recovery flow state (experimental) + description: The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change! + enum: + - choose_method + - sent_email + - passed_challenge # End # All modifications for the verification flow @@ -113,11 +125,15 @@ link: "#/components/schemas/updateVerificationFlowWithLinkMethod" code: "#/components/schemas/updateVerificationFlowWithCodeMethod" - op: add - path: /components/schemas/verificationFlowState/enum + path: /components/schemas/verificationFlowState + type: string value: - - choose_method - - sent_email - - passed_challenge + title: Verification flow state (experimental) + description: The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change! + enum: + - choose_method + - sent_email + - passed_challenge # End # All modifications for the settings flow @@ -146,10 +162,14 @@ passkey: "#/components/schemas/updateSettingsFlowWithPasskeyMethod" lookup_secret: "#/components/schemas/updateSettingsFlowWithLookupMethod" - op: add - path: /components/schemas/settingsFlowState/enum + path: /components/schemas/settingsFlowState value: - - show_form - - success + title: Settings flow state (experimental) + description: The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change! + type: string + enum: + - show_form + - success # end # Some issues with AdditionalProperties diff --git a/driver/config/config.go b/driver/config/config.go index 4eb0566963d2..b1e16e393f13 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -1531,6 +1531,7 @@ func (p *Config) PasskeyConfig(ctx context.Context) *webauthn.Config { AuthenticatorSelection: protocol.AuthenticatorSelection{ AuthenticatorAttachment: "platform", RequireResidentKey: pointerx.Ptr(true), + ResidentKey: protocol.ResidentKeyRequirementRequired, UserVerification: protocol.VerificationPreferred, }, EncodeUserIDAsString: false, diff --git a/embedx/config.schema.json b/embedx/config.schema.json index 48bda1c3d7f7..5fcf826f4c2a 100644 --- a/embedx/config.schema.json +++ b/embedx/config.schema.json @@ -1903,7 +1903,6 @@ "description": "A list of explicit RP origins. If left empty, this defaults to either `origin` or `id`, prepended with the current protocol schema (HTTP or HTTPS).", "items": { "type": "string", - "format": "uri", "examples": [ "https://www.ory.sh", "https://auth.ory.sh" diff --git a/go.mod b/go.mod index 96d6e9b287ba..61b7fc71dfd7 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,12 @@ module github.com/ory/kratos -go 1.22 +go 1.23 + +toolchain go1.23.2 replace ( + github.com/go-swagger/go-swagger => github.com/aeneasr/go-swagger v0.19.1-0.20241013070044-bccef3a12e26 // See https://github.com/go-swagger/go-swagger/issues/3131 + // github.com/go-swagger/go-swagger => ../../go-swagger/go-swagger // https://github.com/gobuffalo/pop/pull/833 github.com/gobuffalo/pop/v6 => github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b @@ -34,7 +38,7 @@ require ( github.com/go-openapi/strfmt v0.23.0 github.com/go-playground/validator/v10 v10.22.0 github.com/go-swagger/go-swagger v0.31.0 - github.com/go-webauthn/webauthn v0.10.2 // DO NOT UPGRADE TO 0.11.0 WITHOUT ADDRESSING ory/kratos#4034 + github.com/go-webauthn/webauthn v0.11.2 github.com/gobuffalo/httptest v1.5.2 github.com/gobuffalo/pop/v6 v6.1.2-0.20230318123913-c85387acc9a0 github.com/gofrs/uuid v4.4.0+incompatible @@ -91,12 +95,12 @@ require ( go.opentelemetry.io/otel v1.28.0 go.opentelemetry.io/otel/sdk v1.28.0 go.opentelemetry.io/otel/trace v1.28.0 - golang.org/x/crypto v0.25.0 + golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/net v0.27.0 golang.org/x/oauth2 v0.21.0 - golang.org/x/sync v0.7.0 - golang.org/x/text v0.16.0 + golang.org/x/sync v0.8.0 + golang.org/x/text v0.17.0 google.golang.org/grpc v1.65.0 ) @@ -111,7 +115,7 @@ require ( github.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/rjeczalik/notify v0.9.3 // indirect - golang.org/x/term v0.22.0 // indirect + golang.org/x/term v0.23.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect mvdan.cc/sh/v3 v3.6.0 // indirect ) @@ -164,7 +168,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect - github.com/go-webauthn/x v0.1.12 // indirect + github.com/go-webauthn/x v0.1.14 // indirect github.com/gobuffalo/envy v1.10.2 // indirect github.com/gobuffalo/fizz v1.14.4 // indirect github.com/gobuffalo/flect v1.0.2 // indirect diff --git a/go.sum b/go.sum index 54629e85036f..7acd5b069c70 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/aeneasr/go-swagger v0.19.1-0.20241013070044-bccef3a12e26 h1:rwCKVbnpzxQ0F/AhO9FkXnrKqRmqej4epjhe1CpNkB0= +github.com/aeneasr/go-swagger v0.19.1-0.20241013070044-bccef3a12e26/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -232,14 +234,12 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-swagger/go-swagger v0.31.0 h1:H8eOYQnY2u7vNKWDNykv2xJP3pBhRG/R+SOCAmKrLlc= -github.com/go-swagger/go-swagger v0.31.0/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-webauthn/webauthn v0.10.2 h1:OG7B+DyuTytrEPFmTX503K77fqs3HDK/0Iv+z8UYbq4= -github.com/go-webauthn/webauthn v0.10.2/go.mod h1:Gd1IDsGAybuvK1NkwUTLbGmeksxuRJjVN2PE/xsPxHs= -github.com/go-webauthn/x v0.1.12 h1:RjQ5cvApzyU/xLCiP+rub0PE4HBZsLggbxGR5ZpUf/A= -github.com/go-webauthn/x v0.1.12/go.mod h1:XlRcGkNH8PT45TfeJYc6gqpOtiOendHhVmnOxh+5yHs= +github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc= +github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0= +github.com/go-webauthn/x v0.1.14 h1:1wrB8jzXAofojJPAaRxnZhRgagvLGnLjhCAwg3kTpT0= +github.com/go-webauthn/x v0.1.14/go.mod h1:UuVvFZ8/NbOnkDz3y1NaxtUN87pmtpC1PQ+/5BBQRdc= github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= github.com/gobuffalo/fizz v1.14.4 h1:8uume7joF6niTNWN582IQ2jhGTUoa9g1fiV/tIoGdBs= @@ -864,8 +864,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4 golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -979,8 +979,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1058,8 +1058,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1072,8 +1072,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/hash/hash_comparator.go b/hash/hash_comparator.go index 2e51af5ddca5..ca23fc4abfd4 100644 --- a/hash/hash_comparator.go +++ b/hash/hash_comparator.go @@ -9,8 +9,8 @@ import ( "crypto/aes" "crypto/cipher" "crypto/hmac" - "crypto/md5" //#nosec G501 -- compatibility for imported passwords - "crypto/sha1" //#nosec G505 -- compatibility for imported passwords + "crypto/md5" //nolint:all // System compatibility for imported passwords + "crypto/sha1" //nolint:all // System compatibility for imported passwords "crypto/sha256" "crypto/sha512" "crypto/subtle" @@ -21,6 +21,9 @@ import ( "regexp" "strings" + "github.com/go-crypt/crypt" + "github.com/go-crypt/crypt/algorithm/md5crypt" + "github.com/go-crypt/crypt/algorithm/shacrypt" "github.com/pkg/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -33,10 +36,6 @@ import ( "golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/scrypt" - "github.com/go-crypt/crypt" - "github.com/go-crypt/crypt/algorithm/md5crypt" - "github.com/go-crypt/crypt/algorithm/shacrypt" - "github.com/ory/kratos/driver/config" ) diff --git a/identity/credentials_webauthn.go b/identity/credentials_webauthn.go index 0816046d23e4..ae6b2e34cb77 100644 --- a/identity/credentials_webauthn.go +++ b/identity/credentials_webauthn.go @@ -6,6 +6,8 @@ package identity import ( "time" + "github.com/go-webauthn/webauthn/protocol" + "github.com/go-webauthn/webauthn/webauthn" "github.com/ory/kratos/x/webauthnx/aaguid" @@ -27,11 +29,17 @@ func CredentialFromWebAuthn(credential *webauthn.Credential, isPasswordless bool IsPasswordless: isPasswordless, AttestationType: credential.AttestationType, AddedAt: time.Now().UTC().Round(time.Second), - Authenticator: AuthenticatorWebAuthn{ + Authenticator: &AuthenticatorWebAuthn{ AAGUID: credential.Authenticator.AAGUID, SignCount: credential.Authenticator.SignCount, CloneWarning: credential.Authenticator.CloneWarning, }, + Flags: &CredentialWebAuthnFlags{ + UserPresent: credential.Flags.UserPresent, + UserVerified: credential.Flags.UserVerified, + BackupEligible: credential.Flags.BackupEligible, + BackupState: credential.Flags.BackupState, + }, } id := aaguid.Lookup(credential.Authenticator.AAGUID) if id != nil { @@ -49,8 +57,16 @@ func (c CredentialsWebAuthn) ToWebAuthn() (result []webauthn.Credential) { } // PasswordlessOnly returns only passwordless credentials. -func (c CredentialsWebAuthn) PasswordlessOnly() (result []webauthn.Credential) { +func (c CredentialsWebAuthn) PasswordlessOnly(authenticatorResponseFlags *protocol.AuthenticatorFlags) (result []webauthn.Credential) { for k, cc := range c { + // Upgrade path for legacy webauthn credentials. Only possible if we are handling a response from an authenticator. + if c[k].Flags == nil && authenticatorResponseFlags != nil { + c[k].Flags = &CredentialWebAuthnFlags{ + BackupEligible: authenticatorResponseFlags.HasBackupEligible(), + BackupState: authenticatorResponseFlags.HasBackupState(), + } + } + if cc.IsPasswordless { result = append(result, *c[k].ToWebAuthn()) } @@ -61,38 +77,91 @@ func (c CredentialsWebAuthn) PasswordlessOnly() (result []webauthn.Credential) { // ToWebAuthnFiltered returns only the appropriate credentials for the requested // AAL. For AAL1, only passwordless credentials are returned, for AAL2, only // non-passwordless credentials are returned. -func (c CredentialsWebAuthn) ToWebAuthnFiltered(aal AuthenticatorAssuranceLevel) (result []webauthn.Credential) { +// +// authenticatorResponseFlags should be passed if the response is from an authenticator. It will be used to +// upgrade legacy webauthn credentials' BackupEligible and BackupState flags. +func (c CredentialsWebAuthn) ToWebAuthnFiltered(aal AuthenticatorAssuranceLevel, authenticatorResponseFlags *protocol.AuthenticatorFlags) (result []webauthn.Credential) { for k, cc := range c { + // Upgrade path for legacy webauthn credentials. Only possible if we are handling a response from an authenticator. + if c[k].Flags == nil && authenticatorResponseFlags != nil { + c[k].Flags = &CredentialWebAuthnFlags{ + BackupEligible: authenticatorResponseFlags.HasBackupEligible(), + BackupState: authenticatorResponseFlags.HasBackupState(), + } + } + if (aal == AuthenticatorAssuranceLevel1 && cc.IsPasswordless) || (aal == AuthenticatorAssuranceLevel2 && !cc.IsPasswordless) { result = append(result, *c[k].ToWebAuthn()) } - } return result } func (c *CredentialWebAuthn) ToWebAuthn() *webauthn.Credential { - return &webauthn.Credential{ + wc := &webauthn.Credential{ ID: c.ID, PublicKey: c.PublicKey, AttestationType: c.AttestationType, - Authenticator: webauthn.Authenticator{ + Transport: c.Transport, + } + + if c.Authenticator != nil { + wc.Authenticator = webauthn.Authenticator{ AAGUID: c.Authenticator.AAGUID, SignCount: c.Authenticator.SignCount, CloneWarning: c.Authenticator.CloneWarning, - }, + } } + + if c.Flags != nil { + wc.Flags = webauthn.CredentialFlags{ + UserPresent: c.Flags.UserPresent, + UserVerified: c.Flags.UserVerified, + BackupEligible: c.Flags.BackupEligible, + BackupState: c.Flags.BackupState, + } + } + + if c.Attestation != nil { + wc.Attestation = webauthn.CredentialAttestation{ + ClientDataJSON: c.Attestation.ClientDataJSON, + ClientDataHash: c.Attestation.ClientDataHash, + AuthenticatorData: c.Attestation.AuthenticatorData, + PublicKeyAlgorithm: c.Attestation.PublicKeyAlgorithm, + Object: c.Attestation.Object, + } + } + + return wc } type CredentialWebAuthn struct { - ID []byte `json:"id"` - PublicKey []byte `json:"public_key"` - AttestationType string `json:"attestation_type"` - Authenticator AuthenticatorWebAuthn `json:"authenticator"` - DisplayName string `json:"display_name"` - AddedAt time.Time `json:"added_at"` - IsPasswordless bool `json:"is_passwordless"` + ID []byte `json:"id"` + PublicKey []byte `json:"public_key"` + AttestationType string `json:"attestation_type"` + Authenticator *AuthenticatorWebAuthn `json:"authenticator,omitempty"` + DisplayName string `json:"display_name"` + AddedAt time.Time `json:"added_at"` + IsPasswordless bool `json:"is_passwordless"` + Flags *CredentialWebAuthnFlags `json:"flags,omitempty"` + Transport []protocol.AuthenticatorTransport `json:"transport,omitempty"` + Attestation *CredentialWebAuthnAttestation `json:"attestation,omitempty"` +} + +type CredentialWebAuthnFlags struct { + UserPresent bool `json:"user_present"` + UserVerified bool `json:"user_verified"` + BackupEligible bool `json:"backup_eligible"` + BackupState bool `json:"backup_state"` +} + +type CredentialWebAuthnAttestation struct { + ClientDataJSON []byte `json:"client_dataJSON"` + ClientDataHash []byte `json:"client_data_hash"` + AuthenticatorData []byte `json:"authenticator_data"` + PublicKeyAlgorithm int64 `json:"public_key_algorithm"` + Object []byte `json:"object"` } type AuthenticatorWebAuthn struct { diff --git a/identity/credentials_webauthn_test.go b/identity/credentials_webauthn_test.go index ed3dc9689a7b..8918898e71be 100644 --- a/identity/credentials_webauthn_test.go +++ b/identity/credentials_webauthn_test.go @@ -28,16 +28,16 @@ func TestCredentialConversion(t *testing.T) { actual := CredentialFromWebAuthn(expected, false).ToWebAuthn() assert.Equal(t, expected, actual) - actualList := CredentialsWebAuthn{*CredentialFromWebAuthn(expected, false)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel2) + actualList := CredentialsWebAuthn{*CredentialFromWebAuthn(expected, false)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel2, nil) assert.Equal(t, []webauthn.Credential{*expected}, actualList) - actualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, true)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel1) + actualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, true)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel1, nil) assert.Equal(t, []webauthn.Credential{*expected}, actualList) - actualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, true)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel2) + actualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, true)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel2, nil) assert.Len(t, actualList, 0) - actualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, false)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel1) + actualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, false)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel1, nil) assert.Len(t, actualList, 0) fromWebAuthn := CredentialFromWebAuthn(expected, true) @@ -58,7 +58,7 @@ func TestPasswordlessOnly(t *testing.T) { e := *CredentialFromWebAuthn(&webauthn.Credential{ID: []byte("e")}, true) expected := CredentialsWebAuthn{a, b, c, d, e} - actual := expected.PasswordlessOnly() + actual := expected.PasswordlessOnly(nil) require.Len(t, actual, 2) assert.Equal(t, []webauthn.Credential{*c.ToWebAuthn(), *e.ToWebAuthn()}, actual) } diff --git a/identity/handler_test.go b/identity/handler_test.go index 6ff1080ee256..e3362a6ecf94 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -1702,7 +1702,7 @@ func TestHandler(t *testing.T) { AddedAt: time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC), PublicKey: []byte("pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU="), DisplayName: "test", - Authenticator: identity.AuthenticatorWebAuthn{ + Authenticator: &identity.AuthenticatorWebAuthn{ AAGUID: []byte("rc4AAjW8xgpkiwsl8fBVAw=="), SignCount: 0, CloneWarning: false, @@ -1715,7 +1715,7 @@ func TestHandler(t *testing.T) { AddedAt: time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC), PublicKey: []byte("pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU="), DisplayName: "test", - Authenticator: identity.AuthenticatorWebAuthn{ + Authenticator: &identity.AuthenticatorWebAuthn{ AAGUID: []byte("rc4AAjW8xgpkiwsl8fBVAw=="), SignCount: 0, CloneWarning: false, @@ -1728,7 +1728,7 @@ func TestHandler(t *testing.T) { AddedAt: time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC), PublicKey: []byte("pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU="), DisplayName: "test", - Authenticator: identity.AuthenticatorWebAuthn{ + Authenticator: &identity.AuthenticatorWebAuthn{ AAGUID: []byte("rc4AAjW8xgpkiwsl8fBVAw=="), SignCount: 0, CloneWarning: false, @@ -1741,7 +1741,7 @@ func TestHandler(t *testing.T) { AddedAt: time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC), PublicKey: []byte("pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU="), DisplayName: "test", - Authenticator: identity.AuthenticatorWebAuthn{ + Authenticator: &identity.AuthenticatorWebAuthn{ AAGUID: []byte("rc4AAjW8xgpkiwsl8fBVAw=="), SignCount: 0, CloneWarning: false, diff --git a/internal/client-go/model_login_flow_state.go b/internal/client-go/model_login_flow_state.go index ce5570b79032..58af057c612f 100644 --- a/internal/client-go/model_login_flow_state.go +++ b/internal/client-go/model_login_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// LoginFlowState The state represents the state of the login flow. choose_method: ask the user to choose a method (e.g. login account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the login challenge was passed. +// LoginFlowState The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change! type LoginFlowState string // List of loginFlowState diff --git a/internal/client-go/model_recovery_flow_state.go b/internal/client-go/model_recovery_flow_state.go index 1c660ba043b9..d1fa3618882a 100644 --- a/internal/client-go/model_recovery_flow_state.go +++ b/internal/client-go/model_recovery_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// RecoveryFlowState The state represents the state of the recovery flow. choose_method: ask the user to choose a method (e.g. recover account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the recovery challenge was passed. +// RecoveryFlowState The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change! type RecoveryFlowState string // List of recoveryFlowState diff --git a/internal/client-go/model_registration_flow_state.go b/internal/client-go/model_registration_flow_state.go index 86f3fd38cff0..15fd9f532d4b 100644 --- a/internal/client-go/model_registration_flow_state.go +++ b/internal/client-go/model_registration_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// RegistrationFlowState choose_method: ask the user to choose a method (e.g. registration with email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the registration challenge was passed. +// RegistrationFlowState The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change! type RegistrationFlowState string // List of registrationFlowState diff --git a/internal/client-go/model_settings_flow_state.go b/internal/client-go/model_settings_flow_state.go index f994c786a2d8..70093c9c4a03 100644 --- a/internal/client-go/model_settings_flow_state.go +++ b/internal/client-go/model_settings_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// SettingsFlowState show_form: No user data has been collected, or it is invalid, and thus the form should be shown. success: Indicates that the settings flow has been updated successfully with the provided data. Done will stay true when repeatedly checking. If set to true, done will revert back to false only when a flow with invalid (e.g. \"please use a valid phone number\") data was sent. +// SettingsFlowState The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change! type SettingsFlowState string // List of settingsFlowState diff --git a/internal/client-go/model_verification_flow_state.go b/internal/client-go/model_verification_flow_state.go index bea74568c94d..56b65e0c0a5b 100644 --- a/internal/client-go/model_verification_flow_state.go +++ b/internal/client-go/model_verification_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// VerificationFlowState The state represents the state of the verification flow. choose_method: ask the user to choose a method (e.g. recover account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the recovery challenge was passed. +// VerificationFlowState The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change! type VerificationFlowState string // List of verificationFlowState diff --git a/internal/httpclient/model_login_flow_state.go b/internal/httpclient/model_login_flow_state.go index ce5570b79032..58af057c612f 100644 --- a/internal/httpclient/model_login_flow_state.go +++ b/internal/httpclient/model_login_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// LoginFlowState The state represents the state of the login flow. choose_method: ask the user to choose a method (e.g. login account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the login challenge was passed. +// LoginFlowState The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change! type LoginFlowState string // List of loginFlowState diff --git a/internal/httpclient/model_recovery_flow_state.go b/internal/httpclient/model_recovery_flow_state.go index 1c660ba043b9..d1fa3618882a 100644 --- a/internal/httpclient/model_recovery_flow_state.go +++ b/internal/httpclient/model_recovery_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// RecoveryFlowState The state represents the state of the recovery flow. choose_method: ask the user to choose a method (e.g. recover account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the recovery challenge was passed. +// RecoveryFlowState The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change! type RecoveryFlowState string // List of recoveryFlowState diff --git a/internal/httpclient/model_registration_flow_state.go b/internal/httpclient/model_registration_flow_state.go index 86f3fd38cff0..15fd9f532d4b 100644 --- a/internal/httpclient/model_registration_flow_state.go +++ b/internal/httpclient/model_registration_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// RegistrationFlowState choose_method: ask the user to choose a method (e.g. registration with email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the registration challenge was passed. +// RegistrationFlowState The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change! type RegistrationFlowState string // List of registrationFlowState diff --git a/internal/httpclient/model_settings_flow_state.go b/internal/httpclient/model_settings_flow_state.go index f994c786a2d8..70093c9c4a03 100644 --- a/internal/httpclient/model_settings_flow_state.go +++ b/internal/httpclient/model_settings_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// SettingsFlowState show_form: No user data has been collected, or it is invalid, and thus the form should be shown. success: Indicates that the settings flow has been updated successfully with the provided data. Done will stay true when repeatedly checking. If set to true, done will revert back to false only when a flow with invalid (e.g. \"please use a valid phone number\") data was sent. +// SettingsFlowState The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change! type SettingsFlowState string // List of settingsFlowState diff --git a/internal/httpclient/model_verification_flow_state.go b/internal/httpclient/model_verification_flow_state.go index bea74568c94d..56b65e0c0a5b 100644 --- a/internal/httpclient/model_verification_flow_state.go +++ b/internal/httpclient/model_verification_flow_state.go @@ -16,7 +16,7 @@ import ( "fmt" ) -// VerificationFlowState The state represents the state of the verification flow. choose_method: ask the user to choose a method (e.g. recover account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the recovery challenge was passed. +// VerificationFlowState The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change! type VerificationFlowState string // List of verificationFlowState diff --git a/schema/handler.go b/schema/handler.go index fe2842b14a56..acf6a0dc786a 100644 --- a/schema/handler.go +++ b/schema/handler.go @@ -69,7 +69,16 @@ func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) { // //nolint:deadcode,unused //lint:ignore U1000 Used to generate Swagger and OpenAPI definitions -type identitySchema = json.RawMessage +type identitySchema json.RawMessage + +func (m identitySchema) MarshalJSON() ([]byte, error) { + return json.RawMessage(m).MarshalJSON() +} + +func (m *identitySchema) UnmarshalJSON(data []byte) error { + mm := json.RawMessage(*m) + return mm.UnmarshalJSON(data) +} // Get Identity JSON Schema Response // @@ -151,7 +160,7 @@ type identitySchemaContainer struct { // The ID of the Identity JSON Schema ID string `json:"id"` // The actual Identity JSON Schema - Schema identitySchema `json:"schema"` + Schema json.RawMessage `json:"schema"` } // List Identity JSON Schemas Response diff --git a/schema/handler_test.go b/schema/handler_test.go index 615d8092269d..36aeb3aea75d 100644 --- a/schema/handler_test.go +++ b/schema/handler_test.go @@ -189,7 +189,7 @@ func TestHandler(t *testing.T) { body := getFromTSPaginated(t, 0, 2, http.StatusOK) var result []client.IdentitySchemaContainer - require.NoError(t, json.Unmarshal(body, &result)) + require.NoError(t, json.Unmarshal(body, &result), "%s", body) ids_orig := []string{} for _, s := range schemas { diff --git a/selfservice/strategy/passkey/fixtures/registration/success/android/internal_context.json b/selfservice/strategy/passkey/fixtures/registration/success/android/internal_context.json new file mode 100644 index 000000000000..cd9949ce0093 --- /dev/null +++ b/selfservice/strategy/passkey/fixtures/registration/success/android/internal_context.json @@ -0,0 +1,7 @@ +{ + "passkey_session_data": { + "challenge": "mFtAwmtDDdwcO6200I2H6oWjzOiF21lZhQVlrC4tdaU", + "user_id": "d29OeDNJVjdYR2NRa09RVHhNVG1ZbHE1ejBDYzM1dGV3UWxFT25yaUJKcTUyb0VOR0pUMk5PeXExRXp3Z2M2dg", + "userVerification": "" + } +} diff --git a/selfservice/strategy/passkey/fixtures/registration/success/android/response.json b/selfservice/strategy/passkey/fixtures/registration/success/android/response.json new file mode 100644 index 000000000000..8b480b356790 --- /dev/null +++ b/selfservice/strategy/passkey/fixtures/registration/success/android/response.json @@ -0,0 +1,9 @@ +{ + "id": "mK2RV0b2NUGDsj8QqH0XtQ", + "rawId": "mK2RV0b2NUGDsj8QqH0XtQ", + "response": { + "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUJYVxRmHaAcJuz7n2X5FJILFPwxIhVpoURyBRglMxnFpdAAAAAOqbjWZNAR0hPOS2tIy1ddQAEJitkVdG9jVBg7I_EKh9F7WlAQIDJiABIVggjEkfDDjIm8yAYfth4u0EV7ApX4kclQONhpK5BLc7W6wiWCCHiHhRNqf8Qhc7bjoIFTqw4lafiC7yrXvojU_WMNcutA", + "clientDataJson": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibUZ0QXdtdEREZHdjTzYyMDBJMkg2b1dqek9pRjIxbFpoUVZsckM0dGRhVSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOlMyUmZOWWdKbVFpS2dkNi1zZGJqVzdwaGNMX09UUDR2R0U4TDUxUTJHQjAiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20udHJwLmFuZC5wZXJzb25hbC5xbCJ9" + }, + "type": "public-key" +} diff --git a/selfservice/strategy/passkey/fixtures/registration/success/identity.json b/selfservice/strategy/passkey/fixtures/registration/success/browser/identity.json similarity index 100% rename from selfservice/strategy/passkey/fixtures/registration/success/identity.json rename to selfservice/strategy/passkey/fixtures/registration/success/browser/identity.json diff --git a/selfservice/strategy/passkey/fixtures/registration/success/internal_context.json b/selfservice/strategy/passkey/fixtures/registration/success/browser/internal_context.json similarity index 100% rename from selfservice/strategy/passkey/fixtures/registration/success/internal_context.json rename to selfservice/strategy/passkey/fixtures/registration/success/browser/internal_context.json diff --git a/selfservice/strategy/passkey/fixtures/registration/success/response.json b/selfservice/strategy/passkey/fixtures/registration/success/browser/response.json similarity index 100% rename from selfservice/strategy/passkey/fixtures/registration/success/response.json rename to selfservice/strategy/passkey/fixtures/registration/success/browser/response.json diff --git a/selfservice/strategy/passkey/passkey_login.go b/selfservice/strategy/passkey/passkey_login.go index b7957a85ad74..5fffcdaac3c3 100644 --- a/selfservice/strategy/passkey/passkey_login.go +++ b/selfservice/strategy/passkey/passkey_login.go @@ -266,8 +266,7 @@ func (s *Strategy) loginAuthenticate(ctx context.Context, r *http.Request, f *lo WithWrap(err))) } - webAuthCreds := o.Credentials.PasswordlessOnly() - + webAuthCreds := o.Credentials.PasswordlessOnly(&webAuthnResponse.Response.AuthenticatorData.Flags) _, err = web.ValidateDiscoverableLogin( func(rawID, userHandle []byte) (user webauthn.User, err error) { return webauthnx.NewUser(userHandle, webAuthCreds, web.Config), nil diff --git a/selfservice/strategy/passkey/passkey_registration_test.go b/selfservice/strategy/passkey/passkey_registration_test.go index 86a7a7992e68..3e0338dcc357 100644 --- a/selfservice/strategy/passkey/passkey_registration_test.go +++ b/selfservice/strategy/passkey/passkey_registration_test.go @@ -8,6 +8,8 @@ import ( "net/url" "testing" + "github.com/ory/x/assertx" + "github.com/ory/kratos/selfservice/flow" "github.com/stretchr/testify/assert" @@ -28,12 +30,21 @@ import ( var ( flows = []string{"spa", "browser"} - //go:embed fixtures/registration/success/response.json + //go:embed fixtures/registration/success/browser/response.json registrationFixtureSuccessResponse []byte - //go:embed fixtures/registration/success/internal_context.json - registrationFixtureSuccessInternalContext []byte + + //go:embed fixtures/registration/success/browser/internal_context.json + registrationFixtureSuccessBrowserInternalContext []byte + + //go:embed fixtures/registration/success/android/response.json + registrationFixtureSuccessAndroidResponse []byte + + //go:embed fixtures/registration/success/android/internal_context.json + registrationFixtureSuccessAndroidInternalContext []byte + //go:embed fixtures/registration/failure/internal_context_missing_user_id.json registrationFixtureFailureInternalContextMissingUserID []byte + //go:embed fixtures/registration/failure/internal_context_wrong_user_id.json registrationFixtureFailureInternalContextWrongUserID []byte ) @@ -180,7 +191,7 @@ func TestRegistration(t *testing.T) { for _, f := range flows { t.Run("type="+f, func(t *testing.T) { - actual, _, _ := fix.submitPasskeyRegistration(t, f, testhelpers.NewClientWithCookies(t), values) + actual, _, _ := fix.submitPasskeyBrowserRegistration(t, f, testhelpers.NewClientWithCookies(t), values) assert.NotEmpty(t, gjson.Get(actual, "id").String(), "%s", actual) assert.Contains(t, gjson.Get(actual, "ui.action").String(), fix.publicTS.URL+registration.RouteSubmitFlow, "%s", actual) registrationhelpers.CheckFormContent(t, []byte(actual), node.PasskeyRegister, "csrf_token", "traits.username", "traits.foobar") @@ -220,7 +231,7 @@ func TestRegistration(t *testing.T) { for _, f := range flows { t.Run("type="+f, func(t *testing.T) { - actual, _, _ := fix.submitPasskeyRegistration(t, f, testhelpers.NewClientWithCookies(t), values, + actual, _, _ := fix.submitPasskeyBrowserRegistration(t, f, testhelpers.NewClientWithCookies(t), values, withInternalContext(sqlxx.JSONRawMessage(tc.internalContext))) if flowIsSPA(f) { assert.Equal(t, "Internal Server Error", gjson.Get(actual, "error.status").String(), "%s", actual) @@ -415,5 +426,49 @@ func TestRegistration(t *testing.T) { }) } }) + + t.Run("case=should create the identity when using android", func(t *testing.T) { + fix.useRedirNoSessionTS() + t.Cleanup(fix.useRedirTS) + fix.disableSessionAfterRegistration() + + prevRPID := fix.conf.GetProvider(fix.ctx).String(config.ViperKeyPasskeyRPID) + prevOrigins := fix.conf.GetProvider(fix.ctx).String(config.ViperKeyPasskeyRPOrigins) + + fix.conf.MustSet(fix.ctx, config.ViperKeyPasskeyRPID, "www.troweprice.com") + fix.conf.MustSet(fix.ctx, config.ViperKeyPasskeyRPOrigins, []string{"android:apk-key-hash:S2RfNYgJmQiKgd6-sdbjW7phcL_OTP4vGE8L51Q2GB0"}) + t.Cleanup(func() { + fix.conf.MustSet(fix.ctx, config.ViperKeyPasskeyRPID, prevRPID) + fix.conf.MustSet(fix.ctx, config.ViperKeyPasskeyRPOrigins, prevOrigins) + }) + + for _, f := range flows { + t.Run("type="+f, func(t *testing.T) { + email := f + "-" + testhelpers.RandomEmail() + userID := f + "-user-" + randx.MustString(8, randx.AlphaNum) + + expectReturnTo := fix.redirNoSessionTS.URL + "/registration-return-ts" + actual, res, _ := fix.submitPasskeyAndroidRegistration(t, f, testhelpers.NewClientWithCookies(t), func(v url.Values) { + values(email)(v) + v.Set(node.PasskeyRegister, string(registrationFixtureSuccessAndroidResponse)) + }, withUserID(userID)) + + if f == "spa" { + expectReturnTo = fix.publicTS.URL + assert.Equal(t, email, gjson.Get(actual, "identity.traits.username").String(), "%s", actual) + assert.False(t, gjson.Get(actual, "session").Exists(), "because the registration yielded no session, the user is not expected to be signed in: %s", actual) + } else { + assert.Equal(t, "null\n", actual, "because the registration yielded no session, the user is not expected to be signed in: %s", actual) + } + + assert.Contains(t, res.Request.URL.String(), expectReturnTo, "%+v\n\t%s", res.Request, assertx.PrettifyJSONPayload(t, actual)) + + i, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(fix.ctx, identity.CredentialsTypePasskey, userID) + require.NoError(t, err) + assert.Equal(t, "aal1", i.InternalAvailableAAL.String) + assert.Equal(t, email, gjson.GetBytes(i.Traits, "username").String(), "%s", actual) + }) + } + }) }) } diff --git a/selfservice/strategy/passkey/testfixture_test.go b/selfservice/strategy/passkey/testfixture_test.go index 1f3090177341..3f0fadfd2387 100644 --- a/selfservice/strategy/passkey/testfixture_test.go +++ b/selfservice/strategy/passkey/testfixture_test.go @@ -241,12 +241,6 @@ type submitPasskeyOpt struct { internalContext sqlxx.JSONRawMessage } -func newSubmitPasskeyOpt() *submitPasskeyOpt { - return &submitPasskeyOpt{ - internalContext: registrationFixtureSuccessInternalContext, - } -} - type submitPasskeyOption func(o *submitPasskeyOpt) func withUserID(id string) submitPasskeyOption { @@ -261,6 +255,29 @@ func withInternalContext(ic sqlxx.JSONRawMessage) submitPasskeyOption { } } +func (fix *fixture) submitPasskeyBrowserRegistration( + t *testing.T, + flowType string, + client *http.Client, + cb func(values url.Values), + opts ...submitPasskeyOption, +) (string, *http.Response, *kratos.RegistrationFlow) { + return fix.submitPasskeyRegistration(t, flowType, client, cb, append([]submitPasskeyOption{withInternalContext(registrationFixtureSuccessBrowserInternalContext)}, opts...)...) +} + +func (fix *fixture) submitPasskeyAndroidRegistration( + t *testing.T, + flowType string, + client *http.Client, + cb func(values url.Values), + opts ...submitPasskeyOption, +) (string, *http.Response, *kratos.RegistrationFlow) { + return fix.submitPasskeyRegistration(t, flowType, client, cb, + append([]submitPasskeyOption{withInternalContext( + registrationFixtureSuccessAndroidInternalContext, + )}, opts...)...) +} + func (fix *fixture) submitPasskeyRegistration( t *testing.T, flowType string, @@ -268,7 +285,7 @@ func (fix *fixture) submitPasskeyRegistration( cb func(values url.Values), opts ...submitPasskeyOption, ) (string, *http.Response, *kratos.RegistrationFlow) { - o := newSubmitPasskeyOpt() + o := &submitPasskeyOpt{} for _, fn := range opts { fn(o) } @@ -302,7 +319,7 @@ func (fix *fixture) submitPasskeyRegistration( } func (fix *fixture) makeRegistration(t *testing.T, flowType string, values func(v url.Values), opts ...submitPasskeyOption) (actual string, res *http.Response, fetchedFlow *registration.Flow) { - actual, res, actualFlow := fix.submitPasskeyRegistration(t, flowType, testhelpers.NewClientWithCookies(t), values, opts...) + actual, res, actualFlow := fix.submitPasskeyBrowserRegistration(t, flowType, testhelpers.NewClientWithCookies(t), values, opts...) fetchedFlow, err := fix.reg.RegistrationFlowPersister().GetRegistrationFlow(fix.ctx, uuid.FromStringOrNil(actualFlow.Id)) require.NoError(t, err) diff --git a/selfservice/strategy/webauthn/login.go b/selfservice/strategy/webauthn/login.go index 4279ed48bc96..97fdd1190ab2 100644 --- a/selfservice/strategy/webauthn/login.go +++ b/selfservice/strategy/webauthn/login.go @@ -71,7 +71,7 @@ func (s *Strategy) populateLoginMethod(r *http.Request, sr *login.Flow, i *ident return errors.WithStack(err) } - webAuthCreds := conf.Credentials.ToWebAuthnFiltered(aal) + webAuthCreds := conf.Credentials.ToWebAuthnFiltered(aal, nil) if len(webAuthCreds) == 0 { // Identity has no webauthn return webauthnx.ErrNoCredentials @@ -283,7 +283,7 @@ func (s *Strategy) loginAuthenticate(ctx context.Context, r *http.Request, f *lo return nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Expected WebAuthN in internal context to be an object but got: %s", err))) } - webAuthCreds := o.Credentials.ToWebAuthnFiltered(aal) + webAuthCreds := o.Credentials.ToWebAuthnFiltered(aal, &webAuthnResponse.Response.AuthenticatorData.Flags) if f.IsRefresh() { webAuthCreds = o.Credentials.ToWebAuthn() } diff --git a/spec/api.json b/spec/api.json index 954aecaab228..907845a46f7c 100644 --- a/spec/api.json +++ b/spec/api.json @@ -1425,13 +1425,13 @@ "type": "object" }, "loginFlowState": { - "description": "The state represents the state of the login flow.\n\nchoose_method: ask the user to choose a method (e.g. login account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the login challenge was passed.", + "description": "The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change!", "enum": [ "choose_method", "sent_email", "passed_challenge" ], - "title": "Login Flow State", + "title": "Login flow state (experimental)", "type": "string" }, "logoutFlow": { @@ -1723,14 +1723,13 @@ "type": "object" }, "recoveryFlowState": { - "description": "The state represents the state of the recovery flow.\n\nchoose_method: ask the user to choose a method (e.g. recover account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the recovery challenge was passed.", + "description": "The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change!", "enum": [ "choose_method", "sent_email", "passed_challenge" ], - "title": "Recovery Flow State", - "type": "string" + "title": "Recovery flow state (experimental)" }, "recoveryIdentityAddress": { "properties": { @@ -1863,13 +1862,13 @@ "type": "object" }, "registrationFlowState": { - "description": "choose_method: ask the user to choose a method (e.g. registration with email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the registration challenge was passed.", + "description": "The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change!", "enum": [ "choose_method", "sent_email", "passed_challenge" ], - "title": "State represents the state of this request:", + "title": "Registration flow state (experimental)", "type": "string" }, "selfServiceFlowExpiredError": { @@ -2092,12 +2091,12 @@ "type": "object" }, "settingsFlowState": { - "description": "show_form: No user data has been collected, or it is invalid, and thus the form should be shown.\nsuccess: Indicates that the settings flow has been updated successfully with the provided data.\nDone will stay true when repeatedly checking. If set to true, done will revert back to false only\nwhen a flow with invalid (e.g. \"please use a valid phone number\") data was sent.", + "description": "The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change!", "enum": [ "show_form", "success" ], - "title": "State represents the state of this flow. It knows two states:", + "title": "Settings flow state (experimental)", "type": "string" }, "successfulCodeExchangeResponse": { @@ -3729,14 +3728,13 @@ "type": "object" }, "verificationFlowState": { - "description": "The state represents the state of the verification flow.\n\nchoose_method: ask the user to choose a method (e.g. recover account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the recovery challenge was passed.", + "description": "The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change!", "enum": [ "choose_method", "sent_email", "passed_challenge" ], - "title": "Verification Flow State", - "type": "string" + "title": "Verification flow state (experimental)" }, "version": { "properties": { diff --git a/spec/swagger.json b/spec/swagger.json index 8b3d47305a96..031ad06841ba 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -4575,11 +4575,6 @@ } } }, - "loginFlowState": { - "description": "The state represents the state of the login flow.\n\nchoose_method: ask the user to choose a method (e.g. login account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the login challenge was passed.", - "type": "string", - "title": "Login Flow State" - }, "logoutFlow": { "description": "Logout Flow", "type": "object", @@ -4859,11 +4854,6 @@ } } }, - "recoveryFlowState": { - "description": "The state represents the state of the recovery flow.\n\nchoose_method: ask the user to choose a method (e.g. recover account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the recovery challenge was passed.", - "type": "string", - "title": "Recovery Flow State" - }, "recoveryIdentityAddress": { "type": "object", "required": [ @@ -4994,11 +4984,6 @@ } } }, - "registrationFlowState": { - "description": "choose_method: ask the user to choose a method (e.g. registration with email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the registration challenge was passed.", - "type": "string", - "title": "State represents the state of this request:" - }, "selfServiceFlowExpiredError": { "description": "Is sent when a flow is expired", "type": "object", @@ -5220,11 +5205,6 @@ } } }, - "settingsFlowState": { - "description": "show_form: No user data has been collected, or it is invalid, and thus the form should be shown.\nsuccess: Indicates that the settings flow has been updated successfully with the provided data.\nDone will stay true when repeatedly checking. If set to true, done will revert back to false only\nwhen a flow with invalid (e.g. \"please use a valid phone number\") data was sent.", - "type": "string", - "title": "State represents the state of this flow. It knows two states:" - }, "successfulCodeExchangeResponse": { "description": "The Response for Registration Flows via API", "type": "object", @@ -6698,11 +6678,6 @@ } } }, - "verificationFlowState": { - "description": "The state represents the state of the verification flow.\n\nchoose_method: ask the user to choose a method (e.g. recover account via email)\nsent_email: the email has been sent to the user\npassed_challenge: the request was successful and the recovery challenge was passed.", - "type": "string", - "title": "Verification Flow State" - }, "version": { "type": "object", "properties": { diff --git a/test/e2e/mock/httptarget/go.mod b/test/e2e/mock/httptarget/go.mod index 2d66a9ff4f48..a82d636fb196 100644 --- a/test/e2e/mock/httptarget/go.mod +++ b/test/e2e/mock/httptarget/go.mod @@ -1,6 +1,6 @@ module github.com/ory/mock -go 1.22.1 +go 1.23.1 require ( github.com/julienschmidt/httprouter v1.3.0 From 7093c3b05f1f46b6efbaf60b0291c99ba113dbcd Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:35:04 +0000 Subject: [PATCH 096/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From e6fa520058ca778e01d4e93a8ab4b31a74dd2e11 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:12:43 +0100 Subject: [PATCH 097/158] feat: add migrate sql up|down|status (#4228) This patch adds the ability to execute down migrations using: ``` kratos migrate sql down -e --steps {num_of_steps} ``` Please read `kratos migrate sql down --help` carefully. Going forward, please use the following commands ``` kratos migrate sql up ... kratos migrate sql status ... ``` instead of the previous, now deprecated ``` kratos migrate sql ... kratos migrate status ... ``` commands. See https://github.com/ory-corp/cloud/issues/7350 --- cmd/cliclient/migrate.go | 73 ++++++++----------------- cmd/migrate/root.go | 28 +++++++++- cmd/migrate/sql.go | 14 +++-- go.mod | 56 +++++++++---------- go.sum | 112 +++++++++++++++++++------------------- internal/client-go/go.sum | 1 + persistence/reference.go | 1 + 7 files changed, 147 insertions(+), 138 deletions(-) diff --git a/cmd/cliclient/migrate.go b/cmd/cliclient/migrate.go index e22dcf2ed73c..ef22fbd4b0b7 100644 --- a/cmd/cliclient/migrate.go +++ b/cmd/cliclient/migrate.go @@ -4,12 +4,9 @@ package cliclient import ( - "bufio" - "bytes" "fmt" - "os" - "strings" + "github.com/ory/x/popx" "github.com/ory/x/servicelocatorx" "github.com/pkg/errors" @@ -32,10 +29,7 @@ func NewMigrateHandler() *MigrateHandler { return &MigrateHandler{} } -func (h *MigrateHandler) MigrateSQL(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error { - var d driver.Registry - var err error - +func (h *MigrateHandler) getPersister(cmd *cobra.Command, args []string, opts []driver.RegistryOption) (d driver.Registry, err error) { if flagx.MustGetBool(cmd, "read-from-env") { d, err = driver.NewWithoutInit( cmd.Context(), @@ -47,21 +41,18 @@ func (h *MigrateHandler) MigrateSQL(cmd *cobra.Command, args []string, opts ...d configx.SkipValidation(), }) if err != nil { - return err + return nil, err } if len(d.Config().DSN(cmd.Context())) == 0 { fmt.Println(cmd.UsageString()) fmt.Println("") fmt.Println("When using flag -e, environment variable DSN must be set") - return cmdx.FailSilently(cmd) - } - if err != nil { - return err + return nil, cmdx.FailSilently(cmd) } } else { if len(args) != 1 { fmt.Println(cmd.UsageString()) - return cmdx.FailSilently(cmd) + return nil, cmdx.FailSilently(cmd) } d, err = driver.NewWithoutInit( cmd.Context(), @@ -74,54 +65,38 @@ func (h *MigrateHandler) MigrateSQL(cmd *cobra.Command, args []string, opts ...d configx.WithValue(config.ViperKeyDSN, args[0]), }) if err != nil { - return err + return nil, err } } err = d.Init(cmd.Context(), &contextx.Default{}, append(opts, driver.SkipNetworkInit)...) if err != nil { - return errors.Wrap(err, "an error occurred initializing migrations") + return nil, errors.Wrap(err, "an error occurred initializing migrations") } - var plan bytes.Buffer - _, err = d.Persister().MigrationStatus(cmd.Context()) - if err != nil { - return errors.Wrap(err, "an error occurred planning migrations:") - } + return d, nil +} - if !flagx.MustGetBool(cmd, "yes") { - fmt.Println("The following migration is planned:") - fmt.Println("") - fmt.Printf("%s", plan.String()) - fmt.Println("") - fmt.Println("To skip the next question use flag --yes (at your own risk).") - if !askForConfirmation("Do you wish to execute this migration plan?") { - fmt.Println("Migration aborted.") - return cmdx.FailSilently(cmd) - } +func (h *MigrateHandler) MigrateSQLDown(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error { + p, err := h.getPersister(cmd, args, opts) + if err != nil { + return err } + return popx.MigrateSQLDown(cmd, p.Persister()) +} - if err = d.Persister().MigrateUp(cmd.Context()); err != nil { +func (h *MigrateHandler) MigrateSQLStatus(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error { + p, err := h.getPersister(cmd, args, opts) + if err != nil { return err } - fmt.Println("Successfully applied SQL migrations!") - return nil + return popx.MigrateStatus(cmd, p.Persister()) } -func askForConfirmation(s string) bool { - reader := bufio.NewReader(os.Stdin) - - for { - fmt.Printf("%s [y/n]: ", s) - - response, err := reader.ReadString('\n') - cmdx.Must(err, "%s", err) - - response = strings.ToLower(strings.TrimSpace(response)) - if response == "y" || response == "yes" { - return true - } else if response == "n" || response == "no" { - return false - } +func (h *MigrateHandler) MigrateSQLUp(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error { + p, err := h.getPersister(cmd, args, opts) + if err != nil { + return err } + return popx.MigrateSQLUp(cmd, p.Persister()) } diff --git a/cmd/migrate/root.go b/cmd/migrate/root.go index ccac053b6ad2..38cd83ed950e 100644 --- a/cmd/migrate/root.go +++ b/cmd/migrate/root.go @@ -5,6 +5,11 @@ package migrate import ( "github.com/spf13/cobra" + + "github.com/ory/kratos/cmd/cliclient" + "github.com/ory/kratos/driver" + "github.com/ory/x/configx" + "github.com/ory/x/popx" ) func NewMigrateCmd() *cobra.Command { @@ -16,6 +21,27 @@ func NewMigrateCmd() *cobra.Command { func RegisterCommandRecursive(parent *cobra.Command) { c := NewMigrateCmd() - parent.AddCommand(c) + + configx.RegisterFlags(c.PersistentFlags()) c.AddCommand(NewMigrateSQLCmd()) + + parent.AddCommand(c) +} + +func NewMigrateSQLDownCmd(opts ...driver.RegistryOption) *cobra.Command { + return popx.NewMigrateSQLDownCmd("kratos", func(cmd *cobra.Command, args []string) error { + return cliclient.NewMigrateHandler().MigrateSQLDown(cmd, args, opts...) + }) +} + +func NewMigrateSQLUpCmd(opts ...driver.RegistryOption) *cobra.Command { + return popx.NewMigrateSQLUpCmd("kratos", func(cmd *cobra.Command, args []string) error { + return cliclient.NewMigrateHandler().MigrateSQLUp(cmd, args, opts...) + }) +} + +func NewMigrateSQLStatusCmd(opts ...driver.RegistryOption) *cobra.Command { + return popx.NewMigrateSQLStatusCmd("kratos", func(cmd *cobra.Command, args []string) error { + return cliclient.NewMigrateHandler().MigrateSQLStatus(cmd, args, opts...) + }) } diff --git a/cmd/migrate/sql.go b/cmd/migrate/sql.go index a1e940235304..148d33b12c5f 100644 --- a/cmd/migrate/sql.go +++ b/cmd/migrate/sql.go @@ -14,8 +14,9 @@ import ( // migrateSqlCmd represents the sql command func NewMigrateSQLCmd(opts ...driver.RegistryOption) *cobra.Command { c := &cobra.Command{ - Use: "sql ", - Short: "Create SQL schemas and apply migration plans", + Use: "sql ", + Deprecated: "Please use `hydra migrate sql` instead.", + Short: "Create SQL schemas and apply migration plans", Long: `Run this command on a fresh SQL installation and when you upgrade Ory Kratos to a new minor version. It is recommended to run this command close to the SQL instance (e.g. same subnet) instead of over the public internet. @@ -30,12 +31,17 @@ You can read in the database URL using the -e flag, for example: Before running this command on an existing database, create a back up! `, RunE: func(cmd *cobra.Command, args []string) error { - return cliclient.NewMigrateHandler().MigrateSQL(cmd, args, opts...) + return cliclient.NewMigrateHandler().MigrateSQLUp(cmd, args, opts...) }, } configx.RegisterFlags(c.PersistentFlags()) - c.Flags().BoolP("read-from-env", "e", false, "If set, reads the database connection string from the environment variable DSN or config file key dsn.") + c.PersistentFlags().BoolP("read-from-env", "e", false, "If set, reads the database connection string from the environment variable DSN or config file key dsn.") c.Flags().BoolP("yes", "y", false, "If set all confirmation requests are accepted without user interaction.") + + c.AddCommand(NewMigrateSQLStatusCmd(opts...)) + c.AddCommand(NewMigrateSQLUpCmd(opts...)) + c.AddCommand(NewMigrateSQLDownCmd(opts...)) + return c } diff --git a/go.mod b/go.mod index 61b7fc71dfd7..655aaf81ab54 100644 --- a/go.mod +++ b/go.mod @@ -74,7 +74,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.669 + github.com/ory/x v0.0.674 github.com/peterhellberg/link v1.2.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 @@ -91,17 +91,17 @@ require ( github.com/tidwall/sjson v1.2.5 github.com/urfave/negroni v1.0.0 github.com/zmb3/spotify/v2 v2.4.2 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 - go.opentelemetry.io/otel v1.28.0 - go.opentelemetry.io/otel/sdk v1.28.0 - go.opentelemetry.io/otel/trace v1.28.0 - golang.org/x/crypto v0.26.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 + go.opentelemetry.io/otel v1.32.0 + go.opentelemetry.io/otel/sdk v1.32.0 + go.opentelemetry.io/otel/trace v1.32.0 + golang.org/x/crypto v0.28.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/net v0.27.0 - golang.org/x/oauth2 v0.21.0 - golang.org/x/sync v0.8.0 - golang.org/x/text v0.17.0 - google.golang.org/grpc v1.65.0 + golang.org/x/net v0.30.0 + golang.org/x/oauth2 v0.23.0 + golang.org/x/sync v0.9.0 + golang.org/x/text v0.20.0 + google.golang.org/grpc v1.67.1 ) require github.com/wI2L/jsondiff v0.6.0 @@ -115,7 +115,7 @@ require ( github.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/rjeczalik/notify v0.9.3 // indirect - golang.org/x/term v0.23.0 // indirect + golang.org/x/term v0.25.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect mvdan.cc/sh/v3 v3.6.0 // indirect ) @@ -197,7 +197,7 @@ require ( github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect @@ -260,7 +260,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runc v1.1.14 // indirect - github.com/openzipkin/zipkin-go v0.4.2 // indirect + github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect @@ -270,7 +270,7 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect @@ -298,24 +298,24 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.47.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.21.0 // indirect - go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 // indirect - go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.32.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 // indirect + go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect; / indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect; / indirect - go.opentelemetry.io/otel/exporters/zipkin v1.21.0 // indirect; / indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect; / indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect; / indirect + go.opentelemetry.io/otel/exporters/zipkin v1.32.0 // indirect; / indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/sys v0.27.0 // indirect golang.org/x/tools v0.23.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/protobuf v1.34.2 + google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/protobuf v1.35.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect diff --git a/go.sum b/go.sum index 7acd5b069c70..cc07fbbdba0c 100644 --- a/go.sum +++ b/go.sum @@ -381,8 +381,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 h1:7xsUJsB2NrdcttQPa7JLEaGzvdbk7KvfrjgHZXOQRo0= github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69/go.mod h1:YLEMZOtU+AZ7dhN9T/IpGhXVGly2bvkJQ+zxj3WeVQo= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -613,8 +613,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runc v1.1.14 h1:rgSuzbmgz5DUJjeSnw337TxDbRuqjs6iqQck/2weR6w= github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA= -github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= -github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/ory/analytics-go/v5 v5.0.1 h1:LX8T5B9FN8KZXOtxgN+R3I4THRRVB6+28IKgKBpXmAM= github.com/ory/analytics-go/v5 v5.0.1/go.mod h1:lWCiCjAaJkKfgR/BN5DCLMol8BjKS1x+4jxBxff/FF0= github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA= @@ -638,8 +638,8 @@ github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b h1:BIzoOe2/wynZBQak1p github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/ory/x v0.0.669 h1:pBrju8B5Oie9RjebOwWf1Sj+6dPNIPI3nkVeC8rjUno= -github.com/ory/x v0.0.669/go.mod h1:0Av1u/Gh7WXCrEDJJnySAJrDzluaWllOfl5zqf9Dky8= +github.com/ory/x v0.0.674 h1:KPcOcjFI4zSkTwsRGErqigjcm/ax03RoExAEjeelfEY= +github.com/ory/x v0.0.674/go.mod h1:zJmnDtKje2FCP4EeFvRsKk94XXiqKCSGJMZcirAfhUs= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -695,8 +695,8 @@ github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4Ug github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -818,34 +818,34 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.47.0 h1:rw+yB4sMhufNzbVHGG9SDMSrw1CKSnRqfjJnMpAH4dE= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.47.0/go.mod h1:2NonlJyJNVbDK/hCwiLsu5gsD2bVtmIzQ/tGzWq58us= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/contrib/propagators/b3 v1.21.0 h1:uGdgDPNzwQWRwCXJgw/7h29JaRqcq9B87Iv4hJDKAZw= -go.opentelemetry.io/contrib/propagators/b3 v1.21.0/go.mod h1:D9GQXvVGT2pzyTfp1QBOnD1rzKEWzKjjwu5q2mslCUI= -go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 h1:f4beMGDKiVzg9IcX7/VuWVy+oGdjx3dNJ72YehmtY5k= -go.opentelemetry.io/contrib/propagators/jaeger v1.21.1/go.mod h1:U9jhkEl8d1LL+QXY7q3kneJWJugiN3kZJV2OWz3hkBY= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1 h1:Qb+5A+JbIjXwO7l4HkRUhgIn4Bzz0GNS2q+qdmSx+0c= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1/go.mod h1:G4vNCm7fRk0kjZ6pGNLo5SpLxAUvOfSrcaegnT8TPck= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0 h1:7F3XCD6WYzDkwbi8I8N+oYJWquPVScnRosKGgqjsR8c= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0/go.mod h1:Dk3C0BfIlZDZ5c6eVS7TYiH2vssuyUU3vUsgbrR+5V4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= +go.opentelemetry.io/contrib/propagators/b3 v1.32.0 h1:MazJBz2Zf6HTN/nK/s3Ru1qme+VhWU5hm83QxEP+dvw= +go.opentelemetry.io/contrib/propagators/b3 v1.32.0/go.mod h1:B0s70QHYPrJwPOwD1o3V/R8vETNOG9N3qZf4LDYvA30= +go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 h1:K/fOyTMD6GELKTIJBaJ9k3ppF2Njt8MeUGBOwfaWXXA= +go.opentelemetry.io/contrib/propagators/jaeger v1.32.0/go.mod h1:ISE6hda//MTWvtngG7p4et3OCngsrTVfl7c6DjN17f8= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 h1:/SKXyZLAnuj981HVc8G5ZylYK3qD2W6AYR6cJx5kIHw= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0/go.mod h1:cOEzME0M2OKeHB45lJiOKfvUCdg/r75mf7YS5w0tbmE= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= -go.opentelemetry.io/otel/exporters/zipkin v1.21.0 h1:D+Gv6lSfrFBWmQYyxKjDd0Zuld9SRXpIrEsKZvE4DO4= -go.opentelemetry.io/otel/exporters/zipkin v1.21.0/go.mod h1:83oMKR6DzmHisFOW3I+yIMGZUTjxiWaiBI8M8+TU5zE= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= +go.opentelemetry.io/otel/exporters/zipkin v1.32.0 h1:6O8HgLHPXtXE9QEKEWkBImL9mEKCGEl+m+OncVO53go= +go.opentelemetry.io/otel/exporters/zipkin v1.32.0/go.mod h1:+MFvorlowjy0iWnsKaNxC1kzczSxe71mw85h4p8yEvg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -864,8 +864,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4 golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -952,8 +952,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -962,8 +962,8 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -979,8 +979,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1045,8 +1045,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1058,8 +1058,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1072,8 +1072,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1186,10 +1186,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1202,8 +1202,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc/examples v0.0.0-20210304020650-930c79186c99 h1:qA8rMbz1wQ4DOFfM2ouD29DG9aHWBm6ZOy9BGxiUMmY= google.golang.org/grpc/examples v0.0.0-20210304020650-930c79186c99/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1219,8 +1219,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/persistence/reference.go b/persistence/reference.go index 72986eb6fe61..35af9afdb29d 100644 --- a/persistence/reference.go +++ b/persistence/reference.go @@ -65,6 +65,7 @@ type Persister interface { Migrator() *popx.Migrator MigrationBox() *popx.MigrationBox GetConnection(context.Context) *pop.Connection + Connection(ctx context.Context) *pop.Connection x.TransactionalPersister Networker } From 307c99c9f8a823da4a953c63fd05fe21ab577a09 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:14:25 +0000 Subject: [PATCH 098/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 5e266107636a5efeeaf2cb202506b10b316a3eff Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:04:25 +0000 Subject: [PATCH 099/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f787b19b49f7..a0dd476640b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-21)](#2024-11-21) +- [ (2024-11-27)](#2024-11-27) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-21) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-27) ## Breaking Changes @@ -399,6 +399,9 @@ https://github.com/ory-corp/cloud/issues/7176 ([88c68aa](https://github.com/ory/kratos/commit/88c68aa07281a638c9897e76d300d1095b17601d)) - Truncate updated at ([#4149](https://github.com/ory/kratos/issues/4149)) ([2f8aaee](https://github.com/ory/kratos/commit/2f8aaee0716835caaba0dff9b6cc457c2cdff5d4)) +- Use context for readiness probes + ([#4219](https://github.com/ory/kratos/issues/4219)) + ([e6d2d4d](https://github.com/ory/kratos/commit/e6d2d4d0c04e60ab5b0658b9e5c4c52104446368)) ### Code Refactoring @@ -420,6 +423,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Clarify facebook graph API versioning ([#4208](https://github.com/ory/kratos/issues/4208)) ([a90df58](https://github.com/ory/kratos/commit/a90df5852ba96704863cc576edcb8286eaa9b3f9)) +- Improve SecurityError error message for ory elements local + ([#4205](https://github.com/ory/kratos/issues/4205)) + ([0062d45](https://github.com/ory/kratos/commit/0062d45b6c9a6323f9dccb10f63dce752836c29e)) - Remove unused SMS config from schema ([#4212](https://github.com/ory/kratos/issues/4212)) ([f076fe4](https://github.com/ory/kratos/commit/f076fe4e1487f67f355eaa7f238090abf3796578)) @@ -443,6 +449,36 @@ https://github.com/ory-corp/cloud/issues/7176 - Add failure reason to events ([#4203](https://github.com/ory/kratos/issues/4203)) ([afa7618](https://github.com/ory/kratos/commit/afa76180e77df0ee0f96eef3b3f2b2d3fe08a33d)) +- Add migrate sql up|down|status + ([#4228](https://github.com/ory/kratos/issues/4228)) + ([e6fa520](https://github.com/ory/kratos/commit/e6fa520058ca778e01d4e93a8ab4b31a74dd2e11)): + + This patch adds the ability to execute down migrations using: + + ``` + kratos migrate sql down -e --steps {num_of_steps} + ``` + + Please read `kratos migrate sql down --help` carefully. + + Going forward, please use the following commands + + ``` + kratos migrate sql up ... + kratos migrate sql status ... + ``` + + instead of the previous, now deprecated + + ``` + kratos migrate sql ... + kratos migrate status ... + ``` + + commands. + + See https://github.com/ory-corp/cloud/issues/7350 + - Add oid as subject source for microsoft ([#4171](https://github.com/ory/kratos/issues/4171)) ([77beb4d](https://github.com/ory/kratos/commit/77beb4de5209cee0bea4b63dfec21d656cf64473)), @@ -508,6 +544,15 @@ https://github.com/ory-corp/cloud/issues/7176 - Remove more unused indices ([#4186](https://github.com/ory/kratos/issues/4186)) ([b294804](https://github.com/ory/kratos/commit/b2948044de4eee1841110162fe874055182bd2d2)) +- Support android webauthn origins + ([#4155](https://github.com/ory/kratos/issues/4155)) + ([a82d288](https://github.com/ory/kratos/commit/a82d288014411ae4eb82c718bfe825ca55b4fab0)): + + This patch adds the ability to verify Android APK origins used during + WebAuthn/Passkey exchange. + + Upgrades go-webauthn and includes fixes for Go 1.23 and workarounds for + Swagger. ### Tests From 3e87e0c4559736f9476eba943bac8d67cde91aad Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:57:23 +0100 Subject: [PATCH 100/158] feat: use one transaction for `/admin/recovery/code` (#4225) --- selfservice/strategy/code/strategy.go | 1 + .../strategy/code/strategy_recovery_admin.go | 31 ++++++++++++------- selfservice/strategy/link/strategy.go | 1 + .../strategy/link/strategy_recovery.go | 15 +++++---- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/selfservice/strategy/code/strategy.go b/selfservice/strategy/code/strategy.go index aaac228508fd..85070509402c 100644 --- a/selfservice/strategy/code/strategy.go +++ b/selfservice/strategy/code/strategy.go @@ -68,6 +68,7 @@ type ( x.WriterProvider x.LoggingProvider x.TracingProvider + x.TransactionPersistenceProvider config.Provider diff --git a/selfservice/strategy/code/strategy_recovery_admin.go b/selfservice/strategy/code/strategy_recovery_admin.go index 63aa36a90edd..d1626f8a3987 100644 --- a/selfservice/strategy/code/strategy_recovery_admin.go +++ b/selfservice/strategy/code/strategy_recovery_admin.go @@ -4,10 +4,13 @@ package code import ( + "context" "net/http" "net/url" "time" + "github.com/gobuffalo/pop/v6" + "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" @@ -184,16 +187,12 @@ func (s *Strategy) createRecoveryCodeForIdentity(w http.ResponseWriter, r *http. })). WithMetaLabel(text.NewInfoNodeLabelRecoveryCode()), ) + rawCode := GenerateCode() recoveryFlow.UI.Nodes. Append(node.NewInputField("method", s.RecoveryStrategyID(), node.CodeGroup, node.InputAttributeTypeSubmit). WithMetaLabel(text.NewInfoNodeLabelContinue())) - if err := s.deps.RecoveryFlowPersister().CreateRecoveryFlow(ctx, recoveryFlow); err != nil { - s.deps.Writer().WriteError(w, r, err) - return - } - id, err := s.deps.IdentityPool().GetIdentity(ctx, p.IdentityID, identity.ExpandDefault) if notFoundErr := sqlcon.ErrNoRows; errors.As(err, ¬FoundErr) { s.deps.Writer().WriteError(w, r, notFoundErr.WithReasonf("could not find identity")) @@ -203,14 +202,22 @@ func (s *Strategy) createRecoveryCodeForIdentity(w http.ResponseWriter, r *http. return } - rawCode := GenerateCode() + if err := s.deps.TransactionalPersisterProvider().Transaction(ctx, func(ctx context.Context, c *pop.Connection) error { + if err := s.deps.RecoveryFlowPersister().CreateRecoveryFlow(ctx, recoveryFlow); err != nil { + return err + } + + if _, err := s.deps.RecoveryCodePersister().CreateRecoveryCode(ctx, &CreateRecoveryCodeParams{ + RawCode: rawCode, + CodeType: RecoveryCodeTypeAdmin, + ExpiresIn: expiresIn, + FlowID: recoveryFlow.ID, + IdentityID: id.ID, + }); err != nil { + return err + } - if _, err := s.deps.RecoveryCodePersister().CreateRecoveryCode(ctx, &CreateRecoveryCodeParams{ - RawCode: rawCode, - CodeType: RecoveryCodeTypeAdmin, - ExpiresIn: expiresIn, - FlowID: recoveryFlow.ID, - IdentityID: id.ID, + return nil }); err != nil { s.deps.Writer().WriteError(w, r, err) return diff --git a/selfservice/strategy/link/strategy.go b/selfservice/strategy/link/strategy.go index cdf8356cc4b3..5cb78378118b 100644 --- a/selfservice/strategy/link/strategy.go +++ b/selfservice/strategy/link/strategy.go @@ -43,6 +43,7 @@ type ( x.WriterProvider x.LoggingProvider x.TracingProvider + x.TransactionPersistenceProvider config.Provider diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index 0ad04d244817..6c92082e47ef 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -10,6 +10,8 @@ import ( "net/url" "time" + "github.com/gobuffalo/pop/v6" + "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" @@ -171,11 +173,6 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. return } - if err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), req); err != nil { - s.d.Writer().WriteError(w, r, err) - return - } - id, err := s.d.IdentityPool().GetIdentity(r.Context(), p.IdentityID, identity.ExpandDefault) if errors.Is(err, sqlcon.ErrNoRows) { s.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The requested identity id does not exist.").WithWrap(err))) @@ -186,7 +183,13 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. } token := NewAdminRecoveryToken(id.ID, req.ID, expiresIn) - if err := s.d.RecoveryTokenPersister().CreateRecoveryToken(r.Context(), token); err != nil { + if err := s.d.TransactionalPersisterProvider().Transaction(r.Context(), func(ctx context.Context, c *pop.Connection) error { + if err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(ctx, req); err != nil { + return err + } + + return s.d.RecoveryTokenPersister().CreateRecoveryToken(ctx, token) + }); err != nil { s.d.Writer().WriteError(w, r, err) return } From 30485c44e61c17231e0c46b321be842b19ea5a5f Mon Sep 17 00:00:00 2001 From: Patrik Date: Wed, 27 Nov 2024 16:18:49 +0100 Subject: [PATCH 101/158] feat: cache OIDC providers (#4222) This change significantly reduces the number of requests to `/.well-known/openid-configuration` endpoints. --- go.mod | 13 +-- go.sum | 26 +++--- selfservice/strategy/oidc/provider.go | 66 +++++++-------- selfservice/strategy/oidc/provider_apple.go | 2 + selfservice/strategy/oidc/provider_auth0.go | 2 + selfservice/strategy/oidc/provider_config.go | 4 +- .../strategy/oidc/provider_dingtalk.go | 2 + selfservice/strategy/oidc/provider_discord.go | 2 + .../strategy/oidc/provider_facebook.go | 2 + .../strategy/oidc/provider_generic_oidc.go | 9 +-- selfservice/strategy/oidc/provider_github.go | 2 + selfservice/strategy/oidc/provider_gitlab.go | 2 + selfservice/strategy/oidc/provider_google.go | 2 + selfservice/strategy/oidc/provider_lark.go | 2 + .../strategy/oidc/provider_linkedin.go | 2 + .../strategy/oidc/provider_microsoft.go | 15 ++-- selfservice/strategy/oidc/provider_netid.go | 7 +- selfservice/strategy/oidc/provider_patreon.go | 2 + .../strategy/oidc/provider_salesforce.go | 2 + selfservice/strategy/oidc/provider_slack.go | 2 + selfservice/strategy/oidc/provider_spotify.go | 2 + .../strategy/oidc/provider_userinfo_test.go | 5 +- selfservice/strategy/oidc/provider_vk.go | 2 + selfservice/strategy/oidc/provider_x.go | 1 - selfservice/strategy/oidc/provider_yandex.go | 2 + selfservice/strategy/oidc/strategy.go | 80 ++++++++----------- 26 files changed, 138 insertions(+), 120 deletions(-) diff --git a/go.mod b/go.mod index 655aaf81ab54..610936b402b9 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.23 toolchain go1.23.2 replace ( + github.com/coreos/go-oidc/v3 => github.com/ory/go-oidc/v3 v3.0.0-20241127113405-e5362711266b + github.com/go-swagger/go-swagger => github.com/aeneasr/go-swagger v0.19.1-0.20241013070044-bccef3a12e26 // See https://github.com/go-swagger/go-swagger/issues/3131 // github.com/go-swagger/go-swagger => ../../go-swagger/go-swagger // https://github.com/gobuffalo/pop/pull/833 @@ -95,10 +97,10 @@ require ( go.opentelemetry.io/otel v1.32.0 go.opentelemetry.io/otel/sdk v1.32.0 go.opentelemetry.io/otel/trace v1.32.0 - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.29.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/net v0.30.0 - golang.org/x/oauth2 v0.23.0 + golang.org/x/net v0.31.0 + golang.org/x/oauth2 v0.24.0 golang.org/x/sync v0.9.0 golang.org/x/text v0.20.0 google.golang.org/grpc v1.67.1 @@ -113,9 +115,10 @@ require ( github.com/bmatcuk/doublestar v1.3.4 // indirect github.com/cortesi/moddwatch v0.1.0 // indirect github.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec // indirect + github.com/dgraph-io/ristretto/v2 v2.0.0 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/rjeczalik/notify v0.9.3 // indirect - golang.org/x/term v0.25.0 // indirect + golang.org/x/term v0.26.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect mvdan.cc/sh/v3 v3.6.0 // indirect ) @@ -152,7 +155,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-crypt/x v0.2.18 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect - github.com/go-jose/go-jose/v4 v4.0.2 // indirect + github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect diff --git a/go.sum b/go.sum index cc07fbbdba0c..2c39e978d87a 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,6 @@ github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7b github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= -github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/cortesi/modd v0.8.1 h1:0s8e10CJ6pxc6NQHYFrmUZOLP0X6v63ry+3na6Gq2Ow= github.com/cortesi/modd v0.8.1/go.mod h1:GDJFkhHnnW+SD1C+wHBlKe5Yh2IqiOb6Lu5t2/fjnS4= github.com/cortesi/moddwatch v0.1.0 h1:+TSMuplhKlKEPKsdUXNHd67aCqew+et15dJvRCxMd1M= @@ -132,6 +130,8 @@ github.com/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE github.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY= github.com/dgraph-io/ristretto v1.0.0 h1:SYG07bONKMlFDUYu5pEu3DGAh8c2OFNzKm6G9J4Si84= github.com/dgraph-io/ristretto v1.0.0/go.mod h1:jTi2FiYEhQ1NsMmA7DeBykizjOuY88NhKBkepyu1jPc= +github.com/dgraph-io/ristretto/v2 v2.0.0 h1:l0yiSOtlJvc0otkqyMaDNysg8E9/F/TYZwMbxscNOAQ= +github.com/dgraph-io/ristretto/v2 v2.0.0/go.mod h1:FVFokF2dRqXyPyeMnK1YDy8Fc6aTe0IKgbcd03CYeEk= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -185,8 +185,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= -github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= -github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -621,6 +621,8 @@ github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNG github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI= github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe h1:rvu4obdvqR0fkSIJ8IfgzKOWwZ5kOT2UNfLq81Qk7rc= github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe/go.mod h1:z4n3u6as84LbV4YmgjHhnwtccQqzf4cZlSk9f1FhygI= +github.com/ory/go-oidc/v3 v3.0.0-20241127113405-e5362711266b h1:PHfiybEhBiabSpPAD5Vq8BotzBrvCUgZN3OrAy3w5u8= +github.com/ory/go-oidc/v3 v3.0.0-20241127113405-e5362711266b/go.mod h1:Jxfv2TPRvdJuLfmkvokss8dkguhMmer2UvARU6SWy0Y= github.com/ory/graceful v0.1.4-0.20230301144740-e222150c51d0 h1:VMUeLRfQD14fOMvhpYZIIT4vtAqxYh+f3KnSqCeJ13o= github.com/ory/graceful v0.1.4-0.20230301144740-e222150c51d0/go.mod h1:hg2iCy+LCWOXahBZ+NQa4dk8J2govyQD79rrqrgMyY8= github.com/ory/herodot v0.10.3-0.20230626083119-d7e5192f0d88 h1:J0CIFKdpUeqKbVMw7pQ1qLtUnflRM1JWAcOEq7Hp4yg= @@ -864,8 +866,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4 golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -952,8 +954,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -962,8 +964,8 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1058,8 +1060,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/selfservice/strategy/oidc/provider.go b/selfservice/strategy/oidc/provider.go index 2241cb93d193..8d2e5edad189 100644 --- a/selfservice/strategy/oidc/provider.go +++ b/selfservice/strategy/oidc/provider.go @@ -20,24 +20,24 @@ import ( "github.com/ory/kratos/x" ) -type Provider interface { - Config() *Configuration -} - -type OAuth2Provider interface { - Provider - AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption - OAuth2(ctx context.Context) (*oauth2.Config, error) - Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) -} - -type OAuth1Provider interface { - Provider - OAuth1(ctx context.Context) *oauth1.Config - AuthURL(ctx context.Context, state string) (string, error) - Claims(ctx context.Context, token *oauth1.Token) (*Claims, error) - ExchangeToken(ctx context.Context, req *http.Request) (*oauth1.Token, error) -} +type ( + Provider interface { + Config() *Configuration + } + OAuth2Provider interface { + Provider + AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption + OAuth2(ctx context.Context) (*oauth2.Config, error) + Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) + } + OAuth1Provider interface { + Provider + OAuth1(ctx context.Context) *oauth1.Config + AuthURL(ctx context.Context, state string) (string, error) + Claims(ctx context.Context, token *oauth1.Token) (*Claims, error) + ExchangeToken(ctx context.Context, req *http.Request) (*oauth1.Token, error) + } +) type OAuth2TokenExchanger interface { Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) @@ -51,22 +51,22 @@ type NonceValidationSkipper interface { CanSkipNonce(*Claims) bool } -// ConvertibleBoolean is used as Apple casually sends the email_verified field as a string. type Claims struct { - Issuer string `json:"iss,omitempty"` - Subject string `json:"sub,omitempty"` - Object string `json:"oid,omitempty"` - Name string `json:"name,omitempty"` - GivenName string `json:"given_name,omitempty"` - FamilyName string `json:"family_name,omitempty"` - LastName string `json:"last_name,omitempty"` - MiddleName string `json:"middle_name,omitempty"` - Nickname string `json:"nickname,omitempty"` - PreferredUsername string `json:"preferred_username,omitempty"` - Profile string `json:"profile,omitempty"` - Picture string `json:"picture,omitempty"` - Website string `json:"website,omitempty"` - Email string `json:"email,omitempty"` + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Object string `json:"oid,omitempty"` + Name string `json:"name,omitempty"` + GivenName string `json:"given_name,omitempty"` + FamilyName string `json:"family_name,omitempty"` + LastName string `json:"last_name,omitempty"` + MiddleName string `json:"middle_name,omitempty"` + Nickname string `json:"nickname,omitempty"` + PreferredUsername string `json:"preferred_username,omitempty"` + Profile string `json:"profile,omitempty"` + Picture string `json:"picture,omitempty"` + Website string `json:"website,omitempty"` + Email string `json:"email,omitempty"` + // ConvertibleBoolean is used as Apple casually sends the email_verified field as a string. EmailVerified x.ConvertibleBoolean `json:"email_verified,omitempty"` Gender string `json:"gender,omitempty"` Birthdate string `json:"birthdate,omitempty"` diff --git a/selfservice/strategy/oidc/provider_apple.go b/selfservice/strategy/oidc/provider_apple.go index 706a7150c5e4..7706eda9d9af 100644 --- a/selfservice/strategy/oidc/provider_apple.go +++ b/selfservice/strategy/oidc/provider_apple.go @@ -25,6 +25,8 @@ type ProviderApple struct { JWKSUrl string } +var _ OAuth2Provider = (*ProviderApple)(nil) + func NewProviderApple( config *Configuration, reg Dependencies, diff --git a/selfservice/strategy/oidc/provider_auth0.go b/selfservice/strategy/oidc/provider_auth0.go index a4c9ee46e1ab..50f4c03fc45b 100644 --- a/selfservice/strategy/oidc/provider_auth0.go +++ b/selfservice/strategy/oidc/provider_auth0.go @@ -29,6 +29,8 @@ type ProviderAuth0 struct { *ProviderGenericOIDC } +var _ OAuth2Provider = (*ProviderAuth0)(nil) + func NewProviderAuth0( config *Configuration, reg Dependencies, diff --git a/selfservice/strategy/oidc/provider_config.go b/selfservice/strategy/oidc/provider_config.go index 7e2b0b19dbfb..7b580f9bc10b 100644 --- a/selfservice/strategy/oidc/provider_config.go +++ b/selfservice/strategy/oidc/provider_config.go @@ -12,7 +12,6 @@ import ( "golang.org/x/exp/maps" "github.com/ory/herodot" - "github.com/ory/x/urlx" ) @@ -181,8 +180,7 @@ var supportedProviders = map[string]func(config *Configuration, reg Dependencies } func (c ConfigurationCollection) Provider(id string, reg Dependencies) (Provider, error) { - for k := range c.Providers { - p := c.Providers[k] + for _, p := range c.Providers { if p.ID == id { if f, ok := supportedProviders[p.Provider]; ok { return f(&p, reg), nil diff --git a/selfservice/strategy/oidc/provider_dingtalk.go b/selfservice/strategy/oidc/provider_dingtalk.go index 12abffe85942..466c7d76406d 100644 --- a/selfservice/strategy/oidc/provider_dingtalk.go +++ b/selfservice/strategy/oidc/provider_dingtalk.go @@ -25,6 +25,8 @@ type ProviderDingTalk struct { reg Dependencies } +var _ OAuth2Provider = (*ProviderDingTalk)(nil) + func NewProviderDingTalk( config *Configuration, reg Dependencies, diff --git a/selfservice/strategy/oidc/provider_discord.go b/selfservice/strategy/oidc/provider_discord.go index 99bea24d5770..97c64a4b414e 100644 --- a/selfservice/strategy/oidc/provider_discord.go +++ b/selfservice/strategy/oidc/provider_discord.go @@ -19,6 +19,8 @@ import ( "github.com/ory/x/stringsx" ) +var _ OAuth2Provider = (*ProviderDiscord)(nil) + type ProviderDiscord struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/provider_facebook.go b/selfservice/strategy/oidc/provider_facebook.go index 2f7a0a58aff0..a7d2ec689eaf 100644 --- a/selfservice/strategy/oidc/provider_facebook.go +++ b/selfservice/strategy/oidc/provider_facebook.go @@ -24,6 +24,8 @@ import ( "github.com/ory/herodot" ) +var _ OAuth2Provider = (*ProviderFacebook)(nil) + type ProviderFacebook struct { *ProviderGenericOIDC } diff --git a/selfservice/strategy/oidc/provider_generic_oidc.go b/selfservice/strategy/oidc/provider_generic_oidc.go index 146505165807..3bdb8d24ec31 100644 --- a/selfservice/strategy/oidc/provider_generic_oidc.go +++ b/selfservice/strategy/oidc/provider_generic_oidc.go @@ -6,17 +6,16 @@ package oidc import ( "context" "net/url" + "slices" + gooidc "github.com/coreos/go-oidc/v3/oidc" "github.com/pkg/errors" "golang.org/x/oauth2" - gooidc "github.com/coreos/go-oidc/v3/oidc" - "github.com/ory/herodot" - "github.com/ory/x/stringslice" ) -var _ Provider = new(ProviderGenericOIDC) +var _ OAuth2Provider = (*ProviderGenericOIDC)(nil) type ProviderGenericOIDC struct { p *gooidc.Provider @@ -60,7 +59,7 @@ func (g *ProviderGenericOIDC) provider(ctx context.Context) (*gooidc.Provider, e func (g *ProviderGenericOIDC) oauth2ConfigFromEndpoint(ctx context.Context, endpoint oauth2.Endpoint) *oauth2.Config { scope := g.config.Scope - if !stringslice.Has(scope, gooidc.ScopeOpenID) { + if !slices.Contains(scope, gooidc.ScopeOpenID) { scope = append(scope, gooidc.ScopeOpenID) } diff --git a/selfservice/strategy/oidc/provider_github.go b/selfservice/strategy/oidc/provider_github.go index fe1d2bc371d1..650778cd1506 100644 --- a/selfservice/strategy/oidc/provider_github.go +++ b/selfservice/strategy/oidc/provider_github.go @@ -23,6 +23,8 @@ import ( "github.com/ory/herodot" ) +var _ OAuth2Provider = (*ProviderGitHub)(nil) + type ProviderGitHub struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/provider_gitlab.go b/selfservice/strategy/oidc/provider_gitlab.go index 9ef55b4beef7..a0cf7508c944 100644 --- a/selfservice/strategy/oidc/provider_gitlab.go +++ b/selfservice/strategy/oidc/provider_gitlab.go @@ -25,6 +25,8 @@ const ( defaultEndpoint = "https://gitlab.com" ) +var _ OAuth2Provider = (*ProviderGitLab)(nil) + type ProviderGitLab struct { *ProviderGenericOIDC } diff --git a/selfservice/strategy/oidc/provider_google.go b/selfservice/strategy/oidc/provider_google.go index e27832692faa..4e009b318380 100644 --- a/selfservice/strategy/oidc/provider_google.go +++ b/selfservice/strategy/oidc/provider_google.go @@ -12,6 +12,8 @@ import ( "github.com/ory/x/stringslice" ) +var _ OAuth2Provider = (*ProviderGoogle)(nil) + type ProviderGoogle struct { *ProviderGenericOIDC JWKSUrl string diff --git a/selfservice/strategy/oidc/provider_lark.go b/selfservice/strategy/oidc/provider_lark.go index 52902dc20e8c..d66d5c0b2230 100644 --- a/selfservice/strategy/oidc/provider_lark.go +++ b/selfservice/strategy/oidc/provider_lark.go @@ -16,6 +16,8 @@ import ( "github.com/ory/x/httpx" ) +var _ OAuth2Provider = (*ProviderLark)(nil) + type ProviderLark struct { *ProviderGenericOIDC } diff --git a/selfservice/strategy/oidc/provider_linkedin.go b/selfservice/strategy/oidc/provider_linkedin.go index 03a3db3e490d..475dd738b29f 100644 --- a/selfservice/strategy/oidc/provider_linkedin.go +++ b/selfservice/strategy/oidc/provider_linkedin.go @@ -63,6 +63,8 @@ const ( IntrospectionURL string = "https://www.linkedin.com/oauth/v2/introspectToken" ) +var _ OAuth2Provider = (*ProviderLinkedIn)(nil) + type ProviderLinkedIn struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/provider_microsoft.go b/selfservice/strategy/oidc/provider_microsoft.go index ec634ce75e3e..408a11096573 100644 --- a/selfservice/strategy/oidc/provider_microsoft.go +++ b/selfservice/strategy/oidc/provider_microsoft.go @@ -9,20 +9,19 @@ import ( "net/url" "strings" - "github.com/hashicorp/go-retryablehttp" - - "github.com/ory/x/httpx" - + gooidc "github.com/coreos/go-oidc/v3/oidc" "github.com/gofrs/uuid" "github.com/golang-jwt/jwt/v4" - - gooidc "github.com/coreos/go-oidc/v3/oidc" + "github.com/hashicorp/go-retryablehttp" "github.com/pkg/errors" "golang.org/x/oauth2" "github.com/ory/herodot" + "github.com/ory/x/httpx" ) +var _ OAuth2Provider = (*ProviderMicrosoft)(nil) + type ProviderMicrosoft struct { *ProviderGenericOIDC } @@ -40,7 +39,7 @@ func NewProviderMicrosoft( } func (m *ProviderMicrosoft) OAuth2(ctx context.Context) (*oauth2.Config, error) { - if len(strings.TrimSpace(m.config.Tenant)) == 0 { + if strings.TrimSpace(m.config.Tenant) == "" { return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("No Tenant specified for the `microsoft` oidc provider %s", m.config.ID)) } @@ -53,7 +52,7 @@ func (m *ProviderMicrosoft) OAuth2(ctx context.Context) (*oauth2.Config, error) return m.oauth2ConfigFromEndpoint(ctx, endpoint), nil } -func (m *ProviderMicrosoft) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) { +func (m *ProviderMicrosoft) Claims(ctx context.Context, exchange *oauth2.Token, _ url.Values) (*Claims, error) { raw, ok := exchange.Extra("id_token").(string) if !ok || len(raw) == 0 { return nil, errors.WithStack(ErrIDTokenMissing) diff --git a/selfservice/strategy/oidc/provider_netid.go b/selfservice/strategy/oidc/provider_netid.go index dfe83c958433..d936bf1b361c 100644 --- a/selfservice/strategy/oidc/provider_netid.go +++ b/selfservice/strategy/oidc/provider_netid.go @@ -8,11 +8,10 @@ import ( "encoding/json" "fmt" "net/url" + "slices" gooidc "github.com/coreos/go-oidc/v3/oidc" - "github.com/ory/x/stringslice" - "github.com/hashicorp/go-retryablehttp" "github.com/pkg/errors" "golang.org/x/oauth2" @@ -28,6 +27,8 @@ const ( defaultBrokerHost = "broker.netid.de" ) +var _ OAuth2Provider = (*ProviderNetID)(nil) + type ProviderNetID struct { *ProviderGenericOIDC } @@ -37,7 +38,7 @@ func NewProviderNetID( reg Dependencies, ) Provider { config.IssuerURL = fmt.Sprintf("%s://%s/", defaultBrokerScheme, defaultBrokerHost) - if !stringslice.Has(config.Scope, gooidc.ScopeOpenID) { + if !slices.Contains(config.Scope, gooidc.ScopeOpenID) { config.Scope = append(config.Scope, gooidc.ScopeOpenID) } diff --git a/selfservice/strategy/oidc/provider_patreon.go b/selfservice/strategy/oidc/provider_patreon.go index 745dc8fcc199..d89e1e2a3ebc 100644 --- a/selfservice/strategy/oidc/provider_patreon.go +++ b/selfservice/strategy/oidc/provider_patreon.go @@ -18,6 +18,8 @@ import ( "github.com/ory/herodot" ) +var _ OAuth2Provider = (*ProviderPatreon)(nil) + type ProviderPatreon struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/provider_salesforce.go b/selfservice/strategy/oidc/provider_salesforce.go index 1d028a1a8de7..04d514ccdf22 100644 --- a/selfservice/strategy/oidc/provider_salesforce.go +++ b/selfservice/strategy/oidc/provider_salesforce.go @@ -25,6 +25,8 @@ import ( "github.com/ory/herodot" ) +var _ OAuth2Provider = (*ProviderSalesforce)(nil) + type ProviderSalesforce struct { *ProviderGenericOIDC } diff --git a/selfservice/strategy/oidc/provider_slack.go b/selfservice/strategy/oidc/provider_slack.go index 7c7e26c99da4..0faed2220ae5 100644 --- a/selfservice/strategy/oidc/provider_slack.go +++ b/selfservice/strategy/oidc/provider_slack.go @@ -19,6 +19,8 @@ import ( "github.com/slack-go/slack" ) +var _ OAuth2Provider = (*ProviderSlack)(nil) + type ProviderSlack struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/provider_spotify.go b/selfservice/strategy/oidc/provider_spotify.go index 366105c94d0e..2c01d0764b3c 100644 --- a/selfservice/strategy/oidc/provider_spotify.go +++ b/selfservice/strategy/oidc/provider_spotify.go @@ -23,6 +23,8 @@ import ( "github.com/ory/herodot" ) +var _ OAuth2Provider = (*ProviderSpotify)(nil) + type ProviderSpotify struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/provider_userinfo_test.go b/selfservice/strategy/oidc/provider_userinfo_test.go index dde2507af319..9eb27914541e 100644 --- a/selfservice/strategy/oidc/provider_userinfo_test.go +++ b/selfservice/strategy/oidc/provider_userinfo_test.go @@ -349,14 +349,13 @@ func TestProviderClaimsRespectsErrorCodes(t *testing.T) { } httpmock.RegisterResponder("GET", tc.userInfoEndpoint, func(req *http.Request) (*http.Response, error) { - resp, err := httpmock.NewJsonResponse(455, map[string]interface{}{}) - return resp, err + return httpmock.NewJsonResponse(455, map[string]interface{}{}) }) _, err := tc.provider.(oidc.OAuth2Provider).Claims(ctx, token, url.Values{}) var he *herodot.DefaultError require.ErrorAs(t, err, &he) - assert.Equal(t, "OpenID Connect provider returned a 455 status code but 200 is expected.", he.Reason()) + assert.Equal(t, "OpenID Connect provider returned a 455 status code but 200 is expected.", he.Reason(), "%+v", err) }) t.Run("call is successful", func(t *testing.T) { diff --git a/selfservice/strategy/oidc/provider_vk.go b/selfservice/strategy/oidc/provider_vk.go index 2a3513b6e050..c60711504fd3 100644 --- a/selfservice/strategy/oidc/provider_vk.go +++ b/selfservice/strategy/oidc/provider_vk.go @@ -19,6 +19,8 @@ import ( "github.com/ory/herodot" ) +var _ OAuth2Provider = (*ProviderVK)(nil) + type ProviderVK struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/provider_x.go b/selfservice/strategy/oidc/provider_x.go index f58dbd48182f..ca2acb6c5e25 100644 --- a/selfservice/strategy/oidc/provider_x.go +++ b/selfservice/strategy/oidc/provider_x.go @@ -18,7 +18,6 @@ import ( "github.com/ory/herodot" ) -var _ Provider = (*ProviderX)(nil) var _ OAuth1Provider = (*ProviderX)(nil) const xUserInfoBase = "https://api.twitter.com/1.1/account/verify_credentials.json" diff --git a/selfservice/strategy/oidc/provider_yandex.go b/selfservice/strategy/oidc/provider_yandex.go index 07b30caee52b..9b11b8fbcf5e 100644 --- a/selfservice/strategy/oidc/provider_yandex.go +++ b/selfservice/strategy/oidc/provider_yandex.go @@ -17,6 +17,8 @@ import ( "github.com/ory/herodot" ) +var _ OAuth2Provider = (*ProviderYandex)(nil) + type ProviderYandex struct { config *Configuration reg Dependencies diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index f2837e769b73..d799c9190dcd 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -14,39 +14,20 @@ import ( "strings" "time" - "github.com/ory/x/sqlxx" - - "golang.org/x/exp/maps" - - "github.com/ory/x/urlx" - - "go.opentelemetry.io/otel/attribute" - "golang.org/x/oauth2" - - "github.com/ory/kratos/cipher" - oidcv1 "github.com/ory/kratos/gen/oidc/v1" - "github.com/ory/kratos/selfservice/sessiontokenexchange" - "github.com/ory/x/jsonnetsecure" - "github.com/ory/x/otelx" - - "github.com/ory/kratos/text" - - "github.com/ory/kratos/ui/container" - "github.com/ory/x/decoderx" - "github.com/ory/x/stringsx" - - "github.com/ory/kratos/ui/node" - "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" "github.com/tidwall/gjson" - - "github.com/ory/x/jsonx" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "golang.org/x/exp/maps" + "golang.org/x/oauth2" "github.com/ory/herodot" + "github.com/ory/kratos/cipher" "github.com/ory/kratos/continuity" "github.com/ory/kratos/driver/config" + oidcv1 "github.com/ory/kratos/gen/oidc/v1" "github.com/ory/kratos/identity" "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/errorx" @@ -54,10 +35,20 @@ import ( "github.com/ory/kratos/selfservice/flow/login" "github.com/ory/kratos/selfservice/flow/registration" "github.com/ory/kratos/selfservice/flow/settings" - + "github.com/ory/kratos/selfservice/sessiontokenexchange" "github.com/ory/kratos/selfservice/strategy" "github.com/ory/kratos/session" + "github.com/ory/kratos/text" + "github.com/ory/kratos/ui/container" + "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" + "github.com/ory/x/decoderx" + "github.com/ory/x/jsonnetsecure" + "github.com/ory/x/jsonx" + "github.com/ory/x/otelx" + "github.com/ory/x/sqlxx" + "github.com/ory/x/stringsx" + "github.com/ory/x/urlx" ) const ( @@ -375,7 +366,7 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt ) ctx := context.WithValue(r.Context(), httprouter.ParamsKey, ps) - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "strategy.oidc.ExchangeCode") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "strategy.oidc.HandleCallback") defer otelx.End(span, &err) r = r.WithContext(ctx) @@ -405,7 +396,7 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt var et *identity.CredentialsOIDCEncryptedTokens switch p := provider.(type) { case OAuth2Provider: - token, err := s.ExchangeCode(ctx, provider, code, PKCEVerifier(state)) + token, err := s.exchangeCode(ctx, p, code, PKCEVerifier(state)) if err != nil { s.forwardError(ctx, w, r, req, s.handleError(ctx, w, r, req, state.ProviderId, nil, err)) return @@ -489,29 +480,24 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt } } -func (s *Strategy) ExchangeCode(ctx context.Context, provider Provider, code string, opts []oauth2.AuthCodeOption) (token *oauth2.Token, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "strategy.oidc.ExchangeCode") +func (s *Strategy) exchangeCode(ctx context.Context, provider OAuth2Provider, code string, opts []oauth2.AuthCodeOption) (token *oauth2.Token, err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "strategy.oidc.exchangeCode", trace.WithAttributes( + attribute.String("provider_id", provider.Config().ID), + attribute.String("provider_label", provider.Config().Label))) defer otelx.End(span, &err) - span.SetAttributes(attribute.String("provider_id", provider.Config().ID)) - span.SetAttributes(attribute.String("provider_label", provider.Config().Label)) - switch p := provider.(type) { - case OAuth2Provider: - te, ok := provider.(OAuth2TokenExchanger) - if !ok { - te, err = p.OAuth2(ctx) - if err != nil { - return nil, err - } + te, ok := provider.(OAuth2TokenExchanger) + if !ok { + te, err = provider.OAuth2(ctx) + if err != nil { + return nil, err } - - client := s.d.HTTPClient(ctx) - ctx = context.WithValue(ctx, oauth2.HTTPClient, client.HTTPClient) - token, err = te.Exchange(ctx, code, opts...) - return token, err - default: - return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The chosen provider is not capable of exchanging an OAuth 2.0 code for an access token.")) } + + client := s.d.HTTPClient(ctx) + ctx = context.WithValue(ctx, oauth2.HTTPClient, client.HTTPClient) + token, err = te.Exchange(ctx, code, opts...) + return token, err } func (s *Strategy) populateMethod(r *http.Request, f flow.Flow, message func(provider string, providerId string) *text.Message) error { From c61132ea980fb0fe8818d1d23a54b4db08402063 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:10:08 +0000 Subject: [PATCH 102/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0dd476640b5..5694c71f6b6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -499,6 +499,12 @@ https://github.com/ory-corp/cloud/issues/7176 - Allow listing identities by organization ID ([#4115](https://github.com/ory/kratos/issues/4115)) ([b4c453b](https://github.com/ory/kratos/commit/b4c453b0472f67d0a52b345691f66aa48777a897)) +- Cache OIDC providers ([#4222](https://github.com/ory/kratos/issues/4222)) + ([30485c4](https://github.com/ory/kratos/commit/30485c44e61c17231e0c46b321be842b19ea5a5f)): + + This change significantly reduces the number of requests to + `/.well-known/openid-configuration` endpoints. + - Drop unused indices post index migration ([#4201](https://github.com/ory/kratos/issues/4201)) ([1008639](https://github.com/ory/kratos/commit/1008639428a6b72e0aa47bd13fe9c1d120aafb6e)) @@ -554,6 +560,10 @@ https://github.com/ory-corp/cloud/issues/7176 Upgrades go-webauthn and includes fixes for Go 1.23 and workarounds for Swagger. +- Use one transaction for `/admin/recovery/code` + ([#4225](https://github.com/ory/kratos/issues/4225)) + ([3e87e0c](https://github.com/ory/kratos/commit/3e87e0c4559736f9476eba943bac8d67cde91aad)) + ### Tests - Update snapshots ([#4167](https://github.com/ory/kratos/issues/4167)) From d5cfa898aaf0ae3ce3e1875128131732b824bd2b Mon Sep 17 00:00:00 2001 From: Patrik Date: Thu, 28 Nov 2024 12:23:10 +0100 Subject: [PATCH 103/158] chore: bump ory/x (#4229) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 610936b402b9..acdf909951c1 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.674 + github.com/ory/x v0.0.675 github.com/peterhellberg/link v1.2.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 2c39e978d87a..d48ddcf554f0 100644 --- a/go.sum +++ b/go.sum @@ -640,8 +640,8 @@ github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b h1:BIzoOe2/wynZBQak1p github.com/ory/pop/v6 v6.2.1-0.20241121111754-e5dfc0f3344b/go.mod h1:okVAYKGtgunD/wbW3NGhZTndJCS+6FqO+cA89rQ4doc= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/ory/x v0.0.674 h1:KPcOcjFI4zSkTwsRGErqigjcm/ax03RoExAEjeelfEY= -github.com/ory/x v0.0.674/go.mod h1:zJmnDtKje2FCP4EeFvRsKk94XXiqKCSGJMZcirAfhUs= +github.com/ory/x v0.0.675 h1:K6GpVo99BXBFv2UiwMjySNNNqCFKGswynrt7vWQJFU8= +github.com/ory/x v0.0.675/go.mod h1:zJmnDtKje2FCP4EeFvRsKk94XXiqKCSGJMZcirAfhUs= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= From f7ddaaeb4a8284b4b115bf4b25765806989da5bc Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:23:15 +0000 Subject: [PATCH 104/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5694c71f6b6a..e9185f87ffea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-27)](#2024-11-27) +- [ (2024-11-28)](#2024-11-28) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-27) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-28) ## Breaking Changes From a7cdc3a6911e265f4e78c780d8e4b8922066875c Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Fri, 29 Nov 2024 09:58:18 +0100 Subject: [PATCH 105/158] feat: emit admin recovery code event (#4230) --- .../strategy/code/strategy_recovery_admin.go | 7 ++- .../strategy/link/strategy_recovery.go | 28 ++++++---- x/events/events.go | 56 +++++++++++-------- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/selfservice/strategy/code/strategy_recovery_admin.go b/selfservice/strategy/code/strategy_recovery_admin.go index d1626f8a3987..b64eb7b66e02 100644 --- a/selfservice/strategy/code/strategy_recovery_admin.go +++ b/selfservice/strategy/code/strategy_recovery_admin.go @@ -10,10 +10,10 @@ import ( "time" "github.com/gobuffalo/pop/v6" - "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" + "go.opentelemetry.io/otel/trace" "github.com/ory/herodot" "github.com/ory/kratos/identity" @@ -23,6 +23,7 @@ import ( "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" + "github.com/ory/kratos/x/events" "github.com/ory/x/decoderx" "github.com/ory/x/sqlcon" "github.com/ory/x/urlx" @@ -223,6 +224,10 @@ func (s *Strategy) createRecoveryCodeForIdentity(w http.ResponseWriter, r *http. return } + trace.SpanFromContext(r.Context()).AddEvent( + events.NewRecoveryInitiatedByAdmin(ctx, recoveryFlow.ID, id.ID, flowType.String(), "code"), + ) + s.deps.Audit(). WithField("identity_id", id.ID). WithSensitiveField("recovery_code", rawCode). diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index 6c92082e47ef..23d40758980e 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -11,19 +11,13 @@ import ( "time" "github.com/gobuffalo/pop/v6" - "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/ory/herodot" - "github.com/ory/x/decoderx" - "github.com/ory/x/otelx" - "github.com/ory/x/sqlcon" - "github.com/ory/x/sqlxx" - "github.com/ory/x/urlx" - "github.com/ory/kratos/identity" "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" @@ -33,6 +27,12 @@ import ( "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" + "github.com/ory/kratos/x/events" + "github.com/ory/x/decoderx" + "github.com/ory/x/otelx" + "github.com/ory/x/sqlcon" + "github.com/ory/x/sqlxx" + "github.com/ory/x/urlx" ) const ( @@ -146,13 +146,15 @@ type recoveryLinkForIdentity struct { // 404: errorGeneric // default: errorGeneric func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + ctx := r.Context() + var p createRecoveryLinkForIdentityBody if err := s.dx.Decode(r, &p, decoderx.HTTPJSONDecoder()); err != nil { s.d.Writer().WriteError(w, r, err) return } - expiresIn := s.d.Config().SelfServiceLinkMethodLifespan(r.Context()) + expiresIn := s.d.Config().SelfServiceLinkMethodLifespan(ctx) if len(p.ExpiresIn) > 0 { var err error expiresIn, err = time.ParseDuration(p.ExpiresIn) @@ -173,7 +175,7 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. return } - id, err := s.d.IdentityPool().GetIdentity(r.Context(), p.IdentityID, identity.ExpandDefault) + id, err := s.d.IdentityPool().GetIdentity(ctx, p.IdentityID, identity.ExpandDefault) if errors.Is(err, sqlcon.ErrNoRows) { s.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The requested identity id does not exist.").WithWrap(err))) return @@ -183,7 +185,7 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. } token := NewAdminRecoveryToken(id.ID, req.ID, expiresIn) - if err := s.d.TransactionalPersisterProvider().Transaction(r.Context(), func(ctx context.Context, c *pop.Connection) error { + if err := s.d.TransactionalPersisterProvider().Transaction(ctx, func(ctx context.Context, c *pop.Connection) error { if err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(ctx, req); err != nil { return err } @@ -194,6 +196,10 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. return } + trace.SpanFromContext(ctx).AddEvent( + events.NewRecoveryInitiatedByAdmin(ctx, req.ID, id.ID, req.Type.String(), "link"), + ) + s.d.Audit(). WithField("identity_id", id.ID). WithSensitiveField("recovery_link_token", token). @@ -202,7 +208,7 @@ func (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http. s.d.Writer().Write(w, r, &recoveryLinkForIdentity{ ExpiresAt: req.ExpiresAt.UTC(), RecoveryLink: urlx.CopyWithQuery( - urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()), recovery.RouteSubmitFlow), + urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx), recovery.RouteSubmitFlow), url.Values{ "token": {token.Token}, "flow": {req.ID.String()}, diff --git a/x/events/events.go b/x/events/events.go index 95b0b856a4a9..ca7108e74abf 100644 --- a/x/events/events.go +++ b/x/events/events.go @@ -19,28 +19,29 @@ import ( ) const ( - SessionIssued semconv.Event = "SessionIssued" - SessionChanged semconv.Event = "SessionChanged" - SessionLifespanExtended semconv.Event = "SessionLifespanExtended" - SessionRevoked semconv.Event = "SessionRevoked" - SessionChecked semconv.Event = "SessionChecked" - SessionTokenizedAsJWT semconv.Event = "SessionTokenizedAsJWT" - RegistrationFailed semconv.Event = "RegistrationFailed" - RegistrationSucceeded semconv.Event = "RegistrationSucceeded" - LoginFailed semconv.Event = "LoginFailed" - LoginSucceeded semconv.Event = "LoginSucceeded" - SettingsFailed semconv.Event = "SettingsFailed" - SettingsSucceeded semconv.Event = "SettingsSucceeded" - RecoveryFailed semconv.Event = "RecoveryFailed" - RecoverySucceeded semconv.Event = "RecoverySucceeded" - VerificationFailed semconv.Event = "VerificationFailed" - VerificationSucceeded semconv.Event = "VerificationSucceeded" - IdentityCreated semconv.Event = "IdentityCreated" - IdentityUpdated semconv.Event = "IdentityUpdated" - IdentityDeleted semconv.Event = "IdentityDeleted" - WebhookDelivered semconv.Event = "WebhookDelivered" - WebhookSucceeded semconv.Event = "WebhookSucceeded" - WebhookFailed semconv.Event = "WebhookFailed" + SessionIssued semconv.Event = "SessionIssued" + SessionChanged semconv.Event = "SessionChanged" + SessionLifespanExtended semconv.Event = "SessionLifespanExtended" + SessionRevoked semconv.Event = "SessionRevoked" + SessionChecked semconv.Event = "SessionChecked" + SessionTokenizedAsJWT semconv.Event = "SessionTokenizedAsJWT" + RegistrationFailed semconv.Event = "RegistrationFailed" + RegistrationSucceeded semconv.Event = "RegistrationSucceeded" + LoginFailed semconv.Event = "LoginFailed" + LoginSucceeded semconv.Event = "LoginSucceeded" + SettingsFailed semconv.Event = "SettingsFailed" + SettingsSucceeded semconv.Event = "SettingsSucceeded" + RecoveryFailed semconv.Event = "RecoveryFailed" + RecoverySucceeded semconv.Event = "RecoverySucceeded" + RecoveryInitiatedByAdmin semconv.Event = "RecoveryInitiatedByAdmin" + VerificationFailed semconv.Event = "VerificationFailed" + VerificationSucceeded semconv.Event = "VerificationSucceeded" + IdentityCreated semconv.Event = "IdentityCreated" + IdentityUpdated semconv.Event = "IdentityUpdated" + IdentityDeleted semconv.Event = "IdentityDeleted" + WebhookDelivered semconv.Event = "WebhookDelivered" + WebhookSucceeded semconv.Event = "WebhookSucceeded" + WebhookFailed semconv.Event = "WebhookFailed" ) const ( @@ -223,6 +224,17 @@ func NewRecoverySucceeded(ctx context.Context, flowID, identityID uuid.UUID, flo )...) } +func NewRecoveryInitiatedByAdmin(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { + return RecoveryInitiatedByAdmin.String(), + trace.WithAttributes(append( + semconv.AttributesFromContext(ctx), + attrSelfServiceFlowType(flowType), + semconv.AttrIdentityID(identityID), + attrSelfServiceMethodUsed(method), + attrFlowID(flowID), + )...) +} + func NewSettingsSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) { return SettingsSucceeded.String(), trace.WithAttributes(append( From 85aeb5b7de3e8de87d31586ea267fa7b36ce11d9 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:58:54 +0100 Subject: [PATCH 106/158] chore(ci): adjust codecov config (#4234) --- codecov.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/codecov.yml b/codecov.yml index 920fd382283f..620228c31857 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,10 +2,9 @@ coverage: status: project: default: - target: 70% - threshold: 5% + target: 65% + threshold: 10% only_pulls: true - base: auto ignore: - "test" - "internal" From 7f5040080578e194dde3605dbb1a344fe9ff27ae Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:59:33 +0100 Subject: [PATCH 107/158] fix: send correct verification status in post-recovery hook (#4224) The verification status is now correctly being transported when executing a recovery hook. --- internal/client-go/go.sum | 1 + .../testhelpers/selfservice_verification.go | 30 +++++++++++++++-- .../strategy/code/strategy_recovery.go | 26 ++++++--------- .../strategy/code/strategy_recovery_test.go | 14 +++++++- .../strategy/link/strategy_recovery.go | 33 ++++++++----------- .../strategy/link/strategy_recovery_test.go | 12 +++++++ 6 files changed, 77 insertions(+), 39 deletions(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/testhelpers/selfservice_verification.go b/internal/testhelpers/selfservice_verification.go index fd0419d2be61..c1d8aa264687 100644 --- a/internal/testhelpers/selfservice_verification.go +++ b/internal/testhelpers/selfservice_verification.go @@ -37,7 +37,7 @@ func NewVerifyAfterHookWebHookTarget(ctx context.Context, t *testing.T, conf *co assert(t, msg) })) - + before := conf.GetProvider(ctx).Get(config.ViperKeySelfServiceVerificationAfter + ".hooks") // A hook to ensure that the verification hook is called with the correct data conf.MustSet(ctx, config.ViperKeySelfServiceVerificationAfter+".hooks", []map[string]interface{}{ { @@ -52,7 +52,33 @@ func NewVerifyAfterHookWebHookTarget(ctx context.Context, t *testing.T, conf *co t.Cleanup(ts.Close) t.Cleanup(func() { - conf.MustSet(ctx, config.ViperKeySelfServiceVerificationAfter+".hooks", []map[string]interface{}{}) + conf.MustSet(ctx, config.ViperKeySelfServiceVerificationAfter+".hooks", before) + }) +} + +func NewRecoveryAfterHookWebHookTarget(ctx context.Context, t *testing.T, conf *config.Config, assert func(t *testing.T, body []byte)) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + msg, err := io.ReadAll(r.Body) + require.NoError(t, err) + + assert(t, msg) + })) + + // A hook to ensure that the recovery hook is called with the correct data + conf.MustSet(ctx, config.ViperKeySelfServiceRecoveryAfter+".hooks", []map[string]interface{}{ + { + "hook": "web_hook", + "config": map[string]interface{}{ + "url": ts.URL, + "method": "POST", + "body": "base64://ZnVuY3Rpb24oY3R4KSB7CiAgICBpZGVudGl0eTogY3R4LmlkZW50aXR5Cn0=", + }, + }, + }) + + t.Cleanup(ts.Close) + t.Cleanup(func() { + conf.MustSet(ctx, config.ViperKeySelfServiceRecoveryAfter+".hooks", []map[string]interface{}{}) }) } diff --git a/selfservice/strategy/code/strategy_recovery.go b/selfservice/strategy/code/strategy_recovery.go index 8f0dcc0f3aa6..178a1906fdde 100644 --- a/selfservice/strategy/code/strategy_recovery.go +++ b/selfservice/strategy/code/strategy_recovery.go @@ -9,6 +9,8 @@ import ( "net/url" "time" + "github.com/ory/x/pointerx" + "github.com/gofrs/uuid" "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" @@ -430,22 +432,14 @@ func (s *Strategy) recoveryHandleFormSubmission(w http.ResponseWriter, r *http.R } func (s *Strategy) markRecoveryAddressVerified(w http.ResponseWriter, r *http.Request, f *recovery.Flow, id *identity.Identity, recoveryAddress *identity.RecoveryAddress) error { - var address *identity.VerifiableAddress - for idx := range id.VerifiableAddresses { - va := id.VerifiableAddresses[idx] - if va.Value == recoveryAddress.Value { - address = &va - break - } - } - - if address != nil && !address.Verified { // can it be that the address is nil? - address.Verified = true - verifiedAt := sqlxx.NullTime(time.Now().UTC()) - address.VerifiedAt = &verifiedAt - address.Status = identity.VerifiableAddressStatusCompleted - if err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), address); err != nil { - return s.HandleRecoveryError(w, r, f, nil, err) + for k, v := range id.VerifiableAddresses { + if v.Value == recoveryAddress.Value { + id.VerifiableAddresses[k].Verified = true + id.VerifiableAddresses[k].VerifiedAt = pointerx.Ptr(sqlxx.NullTime(time.Now().UTC())) + id.VerifiableAddresses[k].Status = identity.VerifiableAddressStatusCompleted + if err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), &id.VerifiableAddresses[k]); err != nil { + return s.HandleRecoveryError(w, r, f, nil, err) + } } } diff --git a/selfservice/strategy/code/strategy_recovery_test.go b/selfservice/strategy/code/strategy_recovery_test.go index 9b55016daebb..245c8420a03b 100644 --- a/selfservice/strategy/code/strategy_recovery_test.go +++ b/selfservice/strategy/code/strategy_recovery_test.go @@ -12,6 +12,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "sync" "testing" "time" @@ -253,6 +254,15 @@ func TestRecovery(t *testing.T) { } t.Run("type=browser", func(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1) + testhelpers.NewRecoveryAfterHookWebHookTarget(ctx, t, conf, func(t *testing.T, msg []byte) { + defer wg.Done() + assert.EqualValues(t, "recoverme1@ory.sh", gjson.GetBytes(msg, "identity.verifiable_addresses.0.value").String(), string(msg)) + assert.EqualValues(t, true, gjson.GetBytes(msg, "identity.verifiable_addresses.0.verified").Bool(), string(msg)) + assert.EqualValues(t, "completed", gjson.GetBytes(msg, "identity.verifiable_addresses.0.status").String(), string(msg)) + }) + client := testhelpers.NewClientWithCookies(t) email := "recoverme1@ory.sh" createIdentityToRecover(t, reg, email) @@ -270,6 +280,8 @@ func TestRecovery(t *testing.T) { require.NoError(t, res.Body.Close()) assert.Equal(t, "code_recovery", gjson.Get(body, "authentication_methods.0.method").String(), "%s", body) assert.Equal(t, "aal1", gjson.Get(body, "authenticator_assurance_level").String(), "%s", body) + + wg.Wait() }) t.Run("type=spa", func(t *testing.T) { @@ -990,7 +1002,7 @@ func TestRecovery(t *testing.T) { body = submitRecoveryCode(t, cl, body, RecoveryClientTypeBrowser, recoveryCode, http.StatusSeeOther) assert.NotEqual(t, gjson.Get(body, "id"), initialFlowId) - require.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1) + require.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1) // No session cookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL))) assert.NotContains(t, cookies, "ory_kratos_session") }) diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index 23d40758980e..4fbd6e15ff1d 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -30,6 +30,7 @@ import ( "github.com/ory/kratos/x/events" "github.com/ory/x/decoderx" "github.com/ory/x/otelx" + "github.com/ory/x/pointerx" "github.com/ory/x/sqlcon" "github.com/ory/x/sqlxx" "github.com/ory/x/urlx" @@ -322,16 +323,16 @@ func (s *Strategy) recoveryIssueSession(ctx context.Context, w http.ResponseWrit return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } - if err := s.d.RecoveryExecutor().PostRecoveryHook(w, r, f, sess); err != nil { + // Force load. + if err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, sess.Identity, identity.ExpandEverything); err != nil { return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } - if err := s.d.SessionManager().UpsertAndIssueCookie(r.Context(), w, r, sess); err != nil { + if err := s.d.RecoveryExecutor().PostRecoveryHook(w, r, f, sess); err != nil { return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } - // Force load. - if err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, sess.Identity, identity.ExpandEverything); err != nil { + if err := s.d.SessionManager().UpsertAndIssueCookie(r.Context(), w, r, sess); err != nil { return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } @@ -498,22 +499,14 @@ func (s *Strategy) recoveryHandleFormSubmission(w http.ResponseWriter, r *http.R } func (s *Strategy) markRecoveryAddressVerified(w http.ResponseWriter, r *http.Request, f *recovery.Flow, id *identity.Identity, recoveryAddress *identity.RecoveryAddress) error { - var address *identity.VerifiableAddress - for idx := range id.VerifiableAddresses { - va := id.VerifiableAddresses[idx] - if va.Value == recoveryAddress.Value { - address = &va - break - } - } - - if address != nil && !address.Verified { // can it be that the address is nil? - address.Verified = true - verifiedAt := sqlxx.NullTime(time.Now().UTC()) - address.VerifiedAt = &verifiedAt - address.Status = identity.VerifiableAddressStatusCompleted - if err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), address); err != nil { - return s.HandleRecoveryError(w, r, f, nil, err) + for k, v := range id.VerifiableAddresses { + if v.Value == recoveryAddress.Value { + id.VerifiableAddresses[k].Verified = true + id.VerifiableAddresses[k].VerifiedAt = pointerx.Ptr(sqlxx.NullTime(time.Now().UTC())) + id.VerifiableAddresses[k].Status = identity.VerifiableAddressStatusCompleted + if err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), &id.VerifiableAddresses[k]); err != nil { + return s.HandleRecoveryError(w, r, f, nil, err) + } } } diff --git a/selfservice/strategy/link/strategy_recovery_test.go b/selfservice/strategy/link/strategy_recovery_test.go index 531cb4e77502..f4b2ba07ee5c 100644 --- a/selfservice/strategy/link/strategy_recovery_test.go +++ b/selfservice/strategy/link/strategy_recovery_test.go @@ -11,6 +11,7 @@ import ( "net/http/httptest" "net/url" "strings" + "sync" "testing" "time" @@ -541,11 +542,22 @@ func TestRecovery(t *testing.T) { } t.Run("type=browser", func(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1) + testhelpers.NewRecoveryAfterHookWebHookTarget(ctx, t, conf, func(t *testing.T, msg []byte) { + defer wg.Done() + assert.EqualValues(t, "recoverme1@ory.sh", gjson.GetBytes(msg, "identity.verifiable_addresses.0.value").String(), string(msg)) + assert.EqualValues(t, true, gjson.GetBytes(msg, "identity.verifiable_addresses.0.verified").Bool(), string(msg)) + assert.EqualValues(t, "completed", gjson.GetBytes(msg, "identity.verifiable_addresses.0.status").String(), string(msg)) + }) + email := "recoverme1@ory.sh" createIdentityToRecover(t, reg, email) check(t, expectSuccess(t, nil, false, false, func(v url.Values) { v.Set("email", email) }), email, "") + + wg.Wait() }) t.Run("description=should return browser to return url", func(t *testing.T) { From dbae98a26b8e2a3328d8510745ddb58c18b7ad3d Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 3 Dec 2024 12:59:54 +0100 Subject: [PATCH 108/158] fix: span names (#4232) --- selfservice/strategy/code/strategy_login.go | 8 ++++---- selfservice/strategy/code/strategy_registration.go | 4 ++-- selfservice/strategy/idfirst/strategy_login.go | 2 +- selfservice/strategy/lookup/login.go | 2 +- selfservice/strategy/lookup/settings.go | 2 +- selfservice/strategy/oidc/strategy_login.go | 4 ++-- selfservice/strategy/oidc/strategy_registration.go | 2 +- selfservice/strategy/oidc/strategy_settings.go | 2 +- selfservice/strategy/passkey/passkey_login.go | 2 +- selfservice/strategy/passkey/passkey_registration.go | 11 +++++++---- selfservice/strategy/passkey/passkey_settings.go | 2 +- selfservice/strategy/password/login.go | 8 ++++---- selfservice/strategy/password/registration.go | 4 ++-- selfservice/strategy/password/settings.go | 2 +- selfservice/strategy/profile/strategy.go | 2 +- selfservice/strategy/totp/login.go | 2 +- selfservice/strategy/totp/settings.go | 2 +- selfservice/strategy/webauthn/login.go | 6 +++--- selfservice/strategy/webauthn/registration.go | 2 +- selfservice/strategy/webauthn/settings.go | 2 +- 20 files changed, 37 insertions(+), 34 deletions(-) diff --git a/selfservice/strategy/code/strategy_login.go b/selfservice/strategy/code/strategy_login.go index d568df8beec5..13e959299a5e 100644 --- a/selfservice/strategy/code/strategy_login.go +++ b/selfservice/strategy/code/strategy_login.go @@ -119,7 +119,7 @@ func (s *Strategy) HandleLoginError(r *http.Request, f *login.Flow, body *update // the identity through other credentials matching the identifier. // the fallback mechanism is used for migration purposes of old accounts that do not have a code credential. func (s *Strategy) findIdentityByIdentifier(ctx context.Context, identifier string) (id *identity.Identity, cred *identity.Credentials, isFallback bool, err error) { - ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.strategy.findIdentityByIdentifier") + ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.Strategy.findIdentityByIdentifier") defer otelx.End(span, &err) id, cred, err = s.deps.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), identifier) @@ -267,7 +267,7 @@ func (s *Strategy) findIdentifierInVerifiableAddress(i *identity.Identity, ident } func (s *Strategy) findIdentityForIdentifier(ctx context.Context, identifier string, requestedAAL identity.AuthenticatorAssuranceLevel, session *session.Session) (_ *identity.Identity, _ []Address, err error) { - ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.strategy.findIdentityForIdentifier") + ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.Strategy.findIdentityForIdentifier") span.SetAttributes( attribute.String("flow.requested_aal", string(requestedAAL)), ) @@ -379,7 +379,7 @@ func (s *Strategy) findIdentityForIdentifier(ctx context.Context, identifier str } func (s *Strategy) loginSendCode(ctx context.Context, w http.ResponseWriter, r *http.Request, f *login.Flow, p *updateLoginFlowWithCodeMethod, sess *session.Session) (err error) { - ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.strategy.loginSendCode") + ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.Strategy.loginSendCode") defer otelx.End(span, &err) p.Identifier = maybeNormalizeEmail( @@ -440,7 +440,7 @@ func maybeNormalizeEmail(input string) string { } func (s *Strategy) loginVerifyCode(ctx context.Context, f *login.Flow, p *updateLoginFlowWithCodeMethod, sess *session.Session) (_ *identity.Identity, err error) { - ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.strategy.loginVerifyCode") + ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.Strategy.loginVerifyCode") defer otelx.End(span, &err) // we are in the second submission state of the flow diff --git a/selfservice/strategy/code/strategy_registration.go b/selfservice/strategy/code/strategy_registration.go index da53cdfb2cd9..734d5540cdf1 100644 --- a/selfservice/strategy/code/strategy_registration.go +++ b/selfservice/strategy/code/strategy_registration.go @@ -164,7 +164,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat } func (s *Strategy) registrationSendEmail(ctx context.Context, w http.ResponseWriter, r *http.Request, f *registration.Flow, p *updateRegistrationFlowWithCodeMethod, i *identity.Identity) (err error) { - ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.strategy.registrationSendEmail") + ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.Strategy.registrationSendEmail") defer otelx.End(span, &err) if len(p.Traits) == 0 { @@ -223,7 +223,7 @@ func (s *Strategy) registrationSendEmail(ctx context.Context, w http.ResponseWri } func (s *Strategy) registrationVerifyCode(ctx context.Context, f *registration.Flow, p *updateRegistrationFlowWithCodeMethod, i *identity.Identity) (err error) { - ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.strategy.registrationVerifyCode") + ctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.code.Strategy.registrationVerifyCode") defer otelx.End(span, &err) if len(p.Code) == 0 { diff --git a/selfservice/strategy/idfirst/strategy_login.go b/selfservice/strategy/idfirst/strategy_login.go index 0cc7b274b30e..1f745a2aba07 100644 --- a/selfservice/strategy/idfirst/strategy_login.go +++ b/selfservice/strategy/idfirst/strategy_login.go @@ -43,7 +43,7 @@ func (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, payload upda } func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, _ *session.Session) (_ *identity.Identity, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.link.strategy.Login") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.idfirst.Strategy.Login") defer otelx.End(span, &err) if !s.d.Config().SelfServiceLoginFlowIdentifierFirstEnabled(ctx) { diff --git a/selfservice/strategy/lookup/login.go b/selfservice/strategy/lookup/login.go index 2441824204b5..2668774a62d2 100644 --- a/selfservice/strategy/lookup/login.go +++ b/selfservice/strategy/lookup/login.go @@ -93,7 +93,7 @@ type updateLoginFlowWithLookupSecretMethod struct { } func (s *Strategy) Login(_ http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (i *identity.Identity, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.lookup.strategy.Login") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.lookup.Strategy.Login") defer otelx.End(span, &err) if err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2); err != nil { diff --git a/selfservice/strategy/lookup/settings.go b/selfservice/strategy/lookup/settings.go index 183f770bde03..08ed97f85453 100644 --- a/selfservice/strategy/lookup/settings.go +++ b/selfservice/strategy/lookup/settings.go @@ -102,7 +102,7 @@ func (p *updateSettingsFlowWithLookupMethod) SetFlowID(rid uuid.UUID) { } func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.lookup.strategy.Settings") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.lookup.Strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithLookupMethod diff --git a/selfservice/strategy/oidc/strategy_login.go b/selfservice/strategy/oidc/strategy_login.go index 07be40194d40..773a500d59a3 100644 --- a/selfservice/strategy/oidc/strategy_login.go +++ b/selfservice/strategy/oidc/strategy_login.go @@ -99,7 +99,7 @@ type UpdateLoginFlowWithOidcMethod struct { } func (s *Strategy) processLogin(ctx context.Context, w http.ResponseWriter, r *http.Request, loginFlow *login.Flow, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider, container *AuthCodeContainer) (_ *registration.Flow, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.strategy.processLogin") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.processLogin") defer otelx.End(span, &err) i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeOIDC, identity.OIDCUniqueID(provider.Config().ID, claims.Subject)) @@ -338,7 +338,7 @@ func (s *Strategy) PopulateLoginMethodSecondFactorRefresh(r *http.Request, sr *l } func (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(r *http.Request, f *login.Flow, mods ...login.FormHydratorModifier) (err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.oidc.strategy.PopulateLoginMethodIdentifierFirstCredentials") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.oidc.Strategy.PopulateLoginMethodIdentifierFirstCredentials") defer otelx.End(span, &err) conf, err := s.Config(ctx) diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index 82737df36a9d..cf7dd35bbba6 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -284,7 +284,7 @@ func (s *Strategy) registrationToLogin(ctx context.Context, w http.ResponseWrite } func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWriter, r *http.Request, rf *registration.Flow, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider, container *AuthCodeContainer) (_ *login.Flow, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.strategy.processRegistration") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.processRegistration") defer otelx.End(span, &err) if _, _, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeOIDC, identity.OIDCUniqueID(provider.Config().ID, claims.Subject)); err == nil { diff --git a/selfservice/strategy/oidc/strategy_settings.go b/selfservice/strategy/oidc/strategy_settings.go index 7f2c6d42f5fa..dcc49f405be2 100644 --- a/selfservice/strategy/oidc/strategy_settings.go +++ b/selfservice/strategy/oidc/strategy_settings.go @@ -256,7 +256,7 @@ func (p *updateSettingsFlowWithOidcMethod) SetFlowID(rid uuid.UUID) { } func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.strategy.Settings") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithOidcMethod diff --git a/selfservice/strategy/passkey/passkey_login.go b/selfservice/strategy/passkey/passkey_login.go index 5fffcdaac3c3..9a062af1d697 100644 --- a/selfservice/strategy/passkey/passkey_login.go +++ b/selfservice/strategy/passkey/passkey_login.go @@ -150,7 +150,7 @@ type updateLoginFlowWithPasskeyMethod struct { } func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, _ *session.Session) (i *identity.Identity, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.passkey.strategy.Login") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.passkey.Strategy.Login") defer otelx.End(span, &err) if f.Type != flow.TypeBrowser { diff --git a/selfservice/strategy/passkey/passkey_registration.go b/selfservice/strategy/passkey/passkey_registration.go index fe3ec305e8b1..1b3a2edbc21c 100644 --- a/selfservice/strategy/passkey/passkey_registration.go +++ b/selfservice/strategy/passkey/passkey_registration.go @@ -101,7 +101,7 @@ func (s *Strategy) decode(r *http.Request) (*updateRegistrationFlowWithPasskeyMe } func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, regFlow *registration.Flow, ident *identity.Identity) (err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.passkey.strategy.Register") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.passkey.Strategy.Register") defer otelx.End(span, &err) if regFlow.Type != flow.TypeBrowser { @@ -273,7 +273,8 @@ func (s *Strategy) PopulateRegistrationMethod(r *http.Request, regFlow *registra Name: node.PasskeyCreateData, Type: node.InputAttributeTypeHidden, FieldValue: string(injectWebAuthnOptions), - }}) + }, + }) regFlow.UI.Nodes.Upsert(&node.Node{ Type: node.Input, @@ -282,7 +283,8 @@ func (s *Strategy) PopulateRegistrationMethod(r *http.Request, regFlow *registra Attributes: &node.InputAttributes{ Name: node.PasskeyRegister, Type: node.InputAttributeTypeHidden, - }}) + }, + }) regFlow.UI.Nodes.Append(&node.Node{ Type: node.Input, @@ -293,7 +295,8 @@ func (s *Strategy) PopulateRegistrationMethod(r *http.Request, regFlow *registra Type: node.InputAttributeTypeButton, OnClick: js.WebAuthnTriggersPasskeyRegistration.String() + "()", // defined in webauthn.js OnClickTrigger: js.WebAuthnTriggersPasskeyRegistration, - }}) + }, + }) // Passkey nodes end diff --git a/selfservice/strategy/passkey/passkey_settings.go b/selfservice/strategy/passkey/passkey_settings.go index f698a292930c..0af4a4c2a214 100644 --- a/selfservice/strategy/passkey/passkey_settings.go +++ b/selfservice/strategy/passkey/passkey_settings.go @@ -163,7 +163,7 @@ func (s *Strategy) identityListWebAuthn(id *identity.Identity) (*identity.Creden } func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.passkey.strategy.Settings") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.passkey.Strategy.Settings") defer otelx.End(span, &err) if f.Type != flow.TypeBrowser { diff --git a/selfservice/strategy/password/login.go b/selfservice/strategy/password/login.go index fb7ae70a1a30..cc4e658f863d 100644 --- a/selfservice/strategy/password/login.go +++ b/selfservice/strategy/password/login.go @@ -52,7 +52,7 @@ func (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, payload upda } func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, _ *session.Session) (i *identity.Identity, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.password.strategy.Login") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.password.Strategy.Login") defer otelx.End(span, &err) if err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil { @@ -126,7 +126,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } func (s *Strategy) migratePasswordHash(ctx context.Context, identifier uuid.UUID, password []byte) (err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.strategy.migratePasswordHash") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.Strategy.migratePasswordHash") defer otelx.End(span, &err) hpw, err := s.d.Hasher(ctx).Generate(ctx, password) @@ -156,7 +156,7 @@ func (s *Strategy) migratePasswordHash(ctx context.Context, identifier uuid.UUID func (s *Strategy) PopulateLoginMethodFirstFactorRefresh(r *http.Request, sr *login.Flow) (err error) { ctx := r.Context() - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.strategy.PopulateLoginMethodFirstFactorRefresh") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.Strategy.PopulateLoginMethodFirstFactorRefresh") defer otelx.End(span, &err) identifier, id, _ := flowhelpers.GuessForcedLoginIdentifier(r, s.d, sr, s.ID()) @@ -214,7 +214,7 @@ func (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, sr *login.Flo } func (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(r *http.Request, sr *login.Flow, opts ...login.FormHydratorModifier) (err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.password.strategy.PopulateLoginMethodIdentifierFirstCredentials") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.password.Strategy.PopulateLoginMethodIdentifierFirstCredentials") defer otelx.End(span, &err) o := login.NewFormHydratorOptions(opts) diff --git a/selfservice/strategy/password/registration.go b/selfservice/strategy/password/registration.go index 26f6a3f4c6a6..b99d0543a980 100644 --- a/selfservice/strategy/password/registration.go +++ b/selfservice/strategy/password/registration.go @@ -78,7 +78,7 @@ func (s *Strategy) decode(p *UpdateRegistrationFlowWithPasswordMethod, r *http.R } func (s *Strategy) Register(_ http.ResponseWriter, r *http.Request, f *registration.Flow, i *identity.Identity) (err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.password.strategy.Register") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.password.Strategy.Register") defer otelx.End(span, &err) if err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.ID().String(), s.d); err != nil { @@ -148,7 +148,7 @@ func (s *Strategy) Register(_ http.ResponseWriter, r *http.Request, f *registrat } func (s *Strategy) validateCredentials(ctx context.Context, i *identity.Identity, pw string) (err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.strategy.validateCredentials") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.Strategy.validateCredentials") defer otelx.End(span, &err) if err := s.d.IdentityValidator().Validate(ctx, i); err != nil { diff --git a/selfservice/strategy/password/settings.go b/selfservice/strategy/password/settings.go index ebe85e262849..183f06eb70b2 100644 --- a/selfservice/strategy/password/settings.go +++ b/selfservice/strategy/password/settings.go @@ -76,7 +76,7 @@ func (p *updateSettingsFlowWithPasswordMethod) SetFlowID(rid uuid.UUID) { } func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.strategy.Settings") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.password.Strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithPasswordMethod diff --git a/selfservice/strategy/profile/strategy.go b/selfservice/strategy/profile/strategy.go index bae200463565..0347d3160cb8 100644 --- a/selfservice/strategy/profile/strategy.go +++ b/selfservice/strategy/profile/strategy.go @@ -116,7 +116,7 @@ func (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, } func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.profile.strategy.Settings") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.profile.Strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithProfileMethod diff --git a/selfservice/strategy/totp/login.go b/selfservice/strategy/totp/login.go index d17bc1fd7e86..a05443206cf6 100644 --- a/selfservice/strategy/totp/login.go +++ b/selfservice/strategy/totp/login.go @@ -95,7 +95,7 @@ type updateLoginFlowWithTotpMethod struct { } func (s *Strategy) Login(_ http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (i *identity.Identity, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.totp.strategy.Login") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.totp.Strategy.Login") defer otelx.End(span, &err) if err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2); err != nil { diff --git a/selfservice/strategy/totp/settings.go b/selfservice/strategy/totp/settings.go index 65953f741c61..993e1adf8033 100644 --- a/selfservice/strategy/totp/settings.go +++ b/selfservice/strategy/totp/settings.go @@ -86,7 +86,7 @@ func (p *updateSettingsFlowWithTotpMethod) SetFlowID(rid uuid.UUID) { } func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.strategy.Settings") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.totp.Strategy.Settings") defer otelx.End(span, &err) var p updateSettingsFlowWithTotpMethod diff --git a/selfservice/strategy/webauthn/login.go b/selfservice/strategy/webauthn/login.go index 97fdd1190ab2..c225368fa29e 100644 --- a/selfservice/strategy/webauthn/login.go +++ b/selfservice/strategy/webauthn/login.go @@ -151,7 +151,7 @@ type updateLoginFlowWithWebAuthnMethod struct { } func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (i *identity.Identity, err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.webauthn.strategy.Login") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.webauthn.Strategy.Login") defer otelx.End(span, &err) if f.Type != flow.TypeBrowser { @@ -193,7 +193,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } func (s *Strategy) loginPasswordless(ctx context.Context, w http.ResponseWriter, r *http.Request, f *login.Flow, p *updateLoginFlowWithWebAuthnMethod) (i *identity.Identity, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.strategy.loginPasswordless") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.Strategy.loginPasswordless") defer otelx.End(span, &err) if err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil { @@ -250,7 +250,7 @@ func (s *Strategy) loginPasswordless(ctx context.Context, w http.ResponseWriter, } func (s *Strategy) loginAuthenticate(ctx context.Context, r *http.Request, f *login.Flow, identityID uuid.UUID, p *updateLoginFlowWithWebAuthnMethod, aal identity.AuthenticatorAssuranceLevel) (_ *identity.Identity, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.strategy.loginAuthenticate") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.Strategy.loginAuthenticate") defer otelx.End(span, &err) i, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, identityID) diff --git a/selfservice/strategy/webauthn/registration.go b/selfservice/strategy/webauthn/registration.go index b97613b161cb..fcba84ddcd42 100644 --- a/selfservice/strategy/webauthn/registration.go +++ b/selfservice/strategy/webauthn/registration.go @@ -95,7 +95,7 @@ func (s *Strategy) decode(p *updateRegistrationFlowWithWebAuthnMethod, r *http.R } func (s *Strategy) Register(_ http.ResponseWriter, r *http.Request, regFlow *registration.Flow, i *identity.Identity) (err error) { - ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.webauthn.strategy.Register") + ctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), "selfservice.strategy.webauthn.Strategy.Register") defer otelx.End(span, &err) if regFlow.Type != flow.TypeBrowser || !s.d.Config().WebAuthnForPasswordless(ctx) { diff --git a/selfservice/strategy/webauthn/settings.go b/selfservice/strategy/webauthn/settings.go index b9900927653d..b488c136ac51 100644 --- a/selfservice/strategy/webauthn/settings.go +++ b/selfservice/strategy/webauthn/settings.go @@ -104,7 +104,7 @@ func (p *updateSettingsFlowWithWebAuthnMethod) SetFlowID(rid uuid.UUID) { } func (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) { - ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.strategy.Settings") + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.webauthn.Strategy.Settings") defer otelx.End(span, &err) if f.Type != flow.TypeBrowser { From 7294145d8599d5dc8c8f7144be685ac8e984f3a5 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:02:03 +0000 Subject: [PATCH 109/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 816ea446abece89eb2f5347feed64af73bec5f06 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:49:33 +0000 Subject: [PATCH 110/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9185f87ffea..e968e250d9b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-11-28)](#2024-11-28) +- [ (2024-12-03)](#2024-12-03) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-11-28) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-03) ## Breaking Changes @@ -397,6 +397,15 @@ https://github.com/ory-corp/cloud/issues/7176 - **sdk:** Remove incorrect attributes ([#4163](https://github.com/ory/kratos/issues/4163)) ([88c68aa](https://github.com/ory/kratos/commit/88c68aa07281a638c9897e76d300d1095b17601d)) +- Send correct verification status in post-recovery hook + ([#4224](https://github.com/ory/kratos/issues/4224)) + ([7f50400](https://github.com/ory/kratos/commit/7f5040080578e194dde3605dbb1a344fe9ff27ae)): + + The verification status is now correctly being transported when executing a + recovery hook. + +- Span names ([#4232](https://github.com/ory/kratos/issues/4232)) + ([dbae98a](https://github.com/ory/kratos/commit/dbae98a26b8e2a3328d8510745ddb58c18b7ad3d)) - Truncate updated at ([#4149](https://github.com/ory/kratos/issues/4149)) ([2f8aaee](https://github.com/ory/kratos/commit/2f8aaee0716835caaba0dff9b6cc457c2cdff5d4)) - Use context for readiness probes @@ -508,6 +517,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Drop unused indices post index migration ([#4201](https://github.com/ory/kratos/issues/4201)) ([1008639](https://github.com/ory/kratos/commit/1008639428a6b72e0aa47bd13fe9c1d120aafb6e)) +- Emit admin recovery code event + ([#4230](https://github.com/ory/kratos/issues/4230)) + ([a7cdc3a](https://github.com/ory/kratos/commit/a7cdc3a6911e265f4e78c780d8e4b8922066875c)) - Fast add credential type lookups ([#4177](https://github.com/ory/kratos/issues/4177)) ([eeb1355](https://github.com/ory/kratos/commit/eeb13552118504f17b48f2c7e002e777f5ee73f4)) From 39057879821b387b49f5d4f7cb19b9e02ec924a7 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Wed, 4 Dec 2024 10:51:47 +0100 Subject: [PATCH 111/158] feat: gracefully handle failing password rehashing during login (#4235) This fixes an issue where we would successfully import long passwords (>72 chars), but fail when the user attempts to login with the correct password because we can't rehash it. In this case, we simply issue a warning to the logs, keep the old hash intact, and continue logging in the user. --- hash/hash_comparator.go | 5 +- selfservice/strategy/password/login.go | 2 +- selfservice/strategy/password/login_test.go | 64 ++++++++++++++++++++- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/hash/hash_comparator.go b/hash/hash_comparator.go index ca23fc4abfd4..4c6007ec94ff 100644 --- a/hash/hash_comparator.go +++ b/hash/hash_comparator.go @@ -551,10 +551,11 @@ func compareCryptHelper(password []byte, hash string) error { return errors.WithStack(ErrMismatchedHashAndPassword) } +var regexSSHA = regexp.MustCompile(`\{([^}]*)\}`) + // decodeSSHAHash decodes SSHA[1|256|512] encoded password hash in usual {SSHA...} format. func decodeSSHAHash(encodedHash string) (hasher string, salt, hash []byte, err error) { - re := regexp.MustCompile(`\{([^}]*)\}`) - match := re.FindStringSubmatch(string(encodedHash)) + match := regexSSHA.FindStringSubmatch(string(encodedHash)) var index_of_salt_begin int var index_of_hash_begin int diff --git a/selfservice/strategy/password/login.go b/selfservice/strategy/password/login.go index cc4e658f863d..92eda3390076 100644 --- a/selfservice/strategy/password/login.go +++ b/selfservice/strategy/password/login.go @@ -112,7 +112,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, if !s.d.Hasher(ctx).Understands([]byte(o.HashedPassword)) { if err := s.migratePasswordHash(ctx, i.ID, []byte(p.Password)); err != nil { - return nil, s.handleLoginError(r, f, p, err) + s.d.Logger().Warnf("Unable to migrate password hash for identity %s: %s Keeping existing password hash and continuing.", i.ID, err) } } } diff --git a/selfservice/strategy/password/login_test.go b/selfservice/strategy/password/login_test.go index c955bf7d8a20..79f82b9c45b2 100644 --- a/selfservice/strategy/password/login_test.go +++ b/selfservice/strategy/password/login_test.go @@ -6,13 +6,16 @@ package password_test import ( "bytes" "context" + "crypto/sha256" _ "embed" + "encoding/base64" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "net/url" + "slices" "strings" "testing" "time" @@ -21,6 +24,7 @@ import ( configtesthelpers "github.com/ory/kratos/driver/config/testhelpers" + "github.com/ory/x/randx" "github.com/ory/x/snapshotx" "github.com/ory/kratos/driver" @@ -903,6 +907,63 @@ func TestCompleteLogin(t *testing.T) { assert.Equal(t, identifier, gjson.Get(body, "identity.traits.email").String(), "%s", body) }) + t.Run("suite=password rehashing degrades gracefully during login", func(t *testing.T) { + identifier := x.NewUUID().String() + "@google.com" + // pwd := "Kd9hUV4Xkcq87VSca6A4fq1iBijrMScBFhkpIPEwBtvTDsBwfqJCqXPPr4TkhOhsd9wFGeB3MzS4bJuesLCAjJc5s1GKJ51zW7F" + pwd := randx.MustString(100, randx.AlphaNum) // longer than bcrypt max length + require.Greater(t, len(pwd), 72) // bcrypt max length + salt := randx.MustString(32, randx.AlphaNum) + sha := sha256.Sum256([]byte(pwd + salt)) + hashed := "{SSHA256}" + base64.StdEncoding.EncodeToString(slices.Concat(sha[:], []byte(salt))) + iId := x.NewUUID() + require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &identity.Identity{ + ID: iId, + SchemaID: "migration", + Traits: identity.Traits(fmt.Sprintf(`{"email":%q}`, identifier)), + Credentials: map[identity.CredentialsType]identity.Credentials{ + identity.CredentialsTypePassword: { + Type: identity.CredentialsTypePassword, + Identifiers: []string{identifier}, + Config: sqlxx.JSONRawMessage(`{"hashed_password":"` + hashed + `"}`), + }, + }, + VerifiableAddresses: []identity.VerifiableAddress{ + { + ID: x.NewUUID(), + Value: identifier, + Verified: true, + CreatedAt: time.Now(), + IdentityID: iId, + }, + }, + })) + + values := func(v url.Values) { + v.Set("identifier", identifier) + v.Set("method", identity.CredentialsTypePassword.String()) + v.Set("password", pwd) + } + + browserClient := testhelpers.NewClientWithCookies(t) + + body := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values, + false, false, http.StatusOK, redirTS.URL) + + assert.Equal(t, identifier, gjson.Get(body, "identity.traits.email").String(), "%s", body) + + // check that the password hash algorithm is unchanged + _, c, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypePassword, identifier) + require.NoError(t, err) + var o identity.CredentialsPassword + require.NoError(t, json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&o)) + assert.Equal(t, hashed, o.HashedPassword) + + // login still works + body = testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values, + false, true, http.StatusOK, redirTS.URL) + assert.Equal(t, identifier, gjson.Get(body, "identity.traits.email").String(), "%s", body) + }) + t.Run("suite=password migration hook", func(t *testing.T) { ctx := context.Background() @@ -948,7 +1009,8 @@ func TestCompleteLogin(t *testing.T) { require.NoError(t, reg.Config().Set(ctx, config.ViperKeyPasswordMigrationHook, map[string]any{ "config": map[string]any{"url": ts.URL}, - "enabled": true})) + "enabled": true, + })) for _, tc := range []struct { name string From 9d3afa7d378234fc4a8c9b2f74960bc65fe573eb Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:43:25 +0000 Subject: [PATCH 112/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e968e250d9b7..d258cdd832e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-03)](#2024-12-03) +- [ (2024-12-04)](#2024-12-04) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-03) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-04) ## Breaking Changes @@ -523,6 +523,15 @@ https://github.com/ory-corp/cloud/issues/7176 - Fast add credential type lookups ([#4177](https://github.com/ory/kratos/issues/4177)) ([eeb1355](https://github.com/ory/kratos/commit/eeb13552118504f17b48f2c7e002e777f5ee73f4)) +- Gracefully handle failing password rehashing during login + ([#4235](https://github.com/ory/kratos/issues/4235)) + ([3905787](https://github.com/ory/kratos/commit/39057879821b387b49f5d4f7cb19b9e02ec924a7)): + + This fixes an issue where we would successfully import long passwords (>72 + chars), but fail when the user attempts to login with the correct password + because we can't rehash it. In this case, we simply issue a warning to the + logs, keep the old hash intact, and continue logging in the user. + - Improve QueryForCredentials ([#4181](https://github.com/ory/kratos/issues/4181)) ([ca0d6a7](https://github.com/ory/kratos/commit/ca0d6a7ea717495429b8bac7fd843ac69c1ebf16)) From 8cbb5bd91540145a8de79e1c8f07457fdf512bd5 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:36:42 +0000 Subject: [PATCH 113/158] chore: update repository templates to https://github.com/ory/meta/commit/1af2225678e6ed0f1947b17a07626774bff38667 --- SECURITY.md | 77 ++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 026e3afb70f8..6104514805c4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,51 +3,54 @@ # Ory Security Policy -## Overview +This policy outlines Ory's security commitments and practices for users across +different licensing and deployment models. -This security policy outlines the security support commitments for different -types of Ory users. +To learn more about Ory's security service level agreements (SLAs) and +processes, please [contact us](https://www.ory.sh/contact/). -[Get in touch](https://www.ory.sh/contact/) to learn more about Ory's security -SLAs and process. - -## Apache 2.0 License Users +## Ory Network Users -- **Security SLA:** No security Service Level Agreement (SLA) is provided. -- **Release Schedule:** Releases are planned every 3 to 6 months. These releases - will contain all security fixes implemented up to that point. -- **Version Support:** Security patches are only provided for the current - release version. +- **Security SLA:** Ory addresses vulnerabilities in the Ory Network according + to the following guidelines: + - Critical: Typically addressed within 14 days. + - High: Typically addressed within 30 days. + - Medium: Typically addressed within 90 days. + - Low: Typically addressed within 180 days. + - Informational: Addressed as necessary. + These timelines are targets and may vary based on specific circumstances. +- **Release Schedule:** Updates are deployed to the Ory Network as + vulnerabilities are resolved. +- **Version Support:** The Ory Network always runs the latest version, ensuring + up-to-date security fixes. ## Ory Enterprise License Customers -- **Security SLA:** The following timelines apply for security vulnerabilities - based on their severity: - - Critical: Resolved within 14 days. - - High: Resolved within 30 days. - - Medium: Resolved within 90 days. - - Low: Resolved within 180 days. - - Informational: Addressed as needed. -- **Release Schedule:** Updates are provided as soon as vulnerabilities are - resolved, adhering to the above SLA. -- **Version Support:** Depending on the Ory Enterprise License agreement - multiple versions can be supported. +- **Security SLA:** Ory addresses vulnerabilities based on their severity: + - Critical: Typically addressed within 14 days. + - High: Typically addressed within 30 days. + - Medium: Typically addressed within 90 days. + - Low: Typically addressed within 180 days. + - Informational: Addressed as necessary. + These timelines are targets and may vary based on specific circumstances. +- **Release Schedule:** Updates are made available as vulnerabilities are + resolved. Ory works closely with enterprise customers to ensure timely updates + that align with their operational needs. +- **Version Support:** Ory may provide security support for multiple versions, + depending on the terms of the enterprise agreement. -## Ory Network Users +## Apache 2.0 License Users -- **Security SLA:** The following timelines apply for security vulnerabilities - based on their severity: - - Critical: Resolved within 14 days. - - High: Resolved within 30 days. - - Medium: Resolved within 90 days. - - Low: Resolved within 180 days. - - Informational: Addressed as needed. -- **Release Schedule:** Updates are automatically deployed to Ory Network as - soon as vulnerabilities are resolved, adhering to the above SLA. -- **Version Support:** Ory Network always runs the most current version. +- **Security SLA:** Ory does not provide a formal SLA for security issues under + the Apache 2.0 License. +- **Release Schedule:** Releases prioritize new functionality and include fixes + for known security vulnerabilities at the time of release. While major + releases typically occur one to two times per year, Ory does not guarantee a + fixed release schedule. +- **Version Support:** Security patches are only provided for the latest release + version. ## Reporting a Vulnerability -Please head over to our -[security policy](https://www.ory.sh/docs/ecosystem/security) to learn more -about reporting security vulnerabilities. +For details on how to report security vulnerabilities, visit our +[security policy documentation](https://www.ory.sh/docs/ecosystem/security). From 3dd9dec0f640669b9a57e88af8d2f65e2630d4f4 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Thu, 12 Dec 2024 17:36:12 +0100 Subject: [PATCH 114/158] chore: refactor parameter parsing in ListIdentities and disallow combining filters --- identity/handler.go | 127 +++++++++++++++------------- identity/handler_test.go | 35 +++++++- internal/client-go/api_identity.go | 4 +- internal/httpclient/api_identity.go | 4 +- spec/api.json | 2 +- spec/swagger.json | 2 +- 6 files changed, 107 insertions(+), 67 deletions(-) diff --git a/identity/handler.go b/identity/handler.go index 7560724899db..98b73665d553 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -119,10 +119,7 @@ func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) { // Paginated Identity List Response // // swagger:response listIdentities -// -//nolint:deadcode,unused -//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions -type listIdentitiesResponse struct { +type _ struct { migrationpagination.ResponseHeaderAnnotation // List of identities @@ -133,11 +130,10 @@ type listIdentitiesResponse struct { // Paginated List Identity Parameters // -// swagger:parameters listIdentities +// Note: Filters cannot be combined. // -//nolint:deadcode,unused -//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions -type listIdentitiesParameters struct { +// swagger:parameters listIdentities +type _ struct { migrationpagination.RequestParameters // List of ids used to filter identities. @@ -183,11 +179,73 @@ type listIdentitiesParameters struct { crdbx.ConsistencyRequestParameters } +func parseListIdentitiesParameters(r *http.Request) (params ListIdentityParameters, err error) { + query := r.URL.Query() + var requestedFilters int + + params.Expand = ExpandDefault + + if ids := query["ids"]; len(ids) > 0 { + requestedFilters++ + for _, v := range ids { + id, err := uuid.FromString(v) + if err != nil { + return params, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `ids`.", v)) + } + params.IdsFilter = append(params.IdsFilter, id) + } + } + if len(params.IdsFilter) > 500 { + return params, errors.WithStack(herodot.ErrBadRequest.WithReason("The number of ids to filter must not exceed 500.")) + } + + if orgID := query.Get("organization_id"); orgID != "" { + requestedFilters++ + params.OrganizationID, err = uuid.FromString(orgID) + if err != nil { + return params, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `organization_id`.", orgID)) + } + } + + if identifier := query.Get("credentials_identifier"); identifier != "" { + requestedFilters++ + params.Expand = ExpandEverything + params.CredentialsIdentifier = identifier + } + + if identifier := query.Get("credentials_identifier_similar"); identifier != "" { + requestedFilters++ + params.Expand = ExpandEverything + params.CredentialsIdentifierSimilar = identifier + } + + for _, v := range query["include_credential"] { + params.Expand = ExpandEverything + tc, ok := ParseCredentialsType(v) + if !ok { + return params, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid value `%s` for parameter `include_credential`.", v)) + } + params.DeclassifyCredentials = append(params.DeclassifyCredentials, tc) + } + + if requestedFilters > 1 { + return params, errors.WithStack(herodot.ErrBadRequest.WithReason("You cannot combine multiple filters in this API")) + } + + params.KeySetPagination, params.PagePagination, err = x.ParseKeysetOrPagePagination(r) + if err != nil { + return params, err + } + params.ConsistencyLevel = crdbx.ConsistencyLevelFromRequest(r) + + return params, nil +} + // swagger:route GET /admin/identities identity listIdentities // // # List Identities // -// Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. +// Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. // // Produces: // - application/json @@ -201,54 +259,7 @@ type listIdentitiesParameters struct { // 200: listIdentities // default: errorGeneric func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - includeCredentials := r.URL.Query()["include_credential"] - var err error - var declassify []CredentialsType - for _, v := range includeCredentials { - tc, ok := ParseCredentialsType(v) - if ok { - declassify = append(declassify, tc) - } else { - h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid value `%s` for parameter `include_credential`.", declassify))) - return - } - } - - var orgId uuid.UUID - if orgIdStr := r.URL.Query().Get("organization_id"); orgIdStr != "" { - orgId, err = uuid.FromString(r.URL.Query().Get("organization_id")) - if err != nil { - h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `organization_id`.", r.URL.Query().Get("organization_id")))) - return - } - } - var idsFilter []uuid.UUID - for _, v := range r.URL.Query()["ids"] { - id, err := uuid.FromString(v) - if err != nil { - h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid UUID value `%s` for parameter `ids`.", v))) - return - } - idsFilter = append(idsFilter, id) - } - - params := ListIdentityParameters{ - Expand: ExpandDefault, - IdsFilter: idsFilter, - CredentialsIdentifier: r.URL.Query().Get("credentials_identifier"), - CredentialsIdentifierSimilar: r.URL.Query().Get("preview_credentials_identifier_similar"), - OrganizationID: orgId, - ConsistencyLevel: crdbx.ConsistencyLevelFromRequest(r), - DeclassifyCredentials: declassify, - } - if params.CredentialsIdentifier != "" && params.CredentialsIdentifierSimilar != "" { - h.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithReason("Cannot pass both credentials_identifier and preview_credentials_identifier_similar.")) - return - } - if params.CredentialsIdentifier != "" || params.CredentialsIdentifierSimilar != "" || len(params.DeclassifyCredentials) > 0 { - params.Expand = ExpandEverything - } - params.KeySetPagination, params.PagePagination, err = x.ParseKeysetOrPagePagination(r) + params, err := parseListIdentitiesParameters(r) if err != nil { h.r.Writer().WriteError(w, r, err) return @@ -271,7 +282,7 @@ func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Para } u := *r.URL pagepagination.PaginationHeader(w, &u, total, params.PagePagination.Page, params.PagePagination.ItemsPerPage) - } else { + } else if nextPage != nil { u := *r.URL keysetpagination.Header(w, &u, nextPage) } diff --git a/identity/handler_test.go b/identity/handler_test.go index e3362a6ecf94..d55448cdce3b 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -369,21 +369,50 @@ func TestHandler(t *testing.T) { id := x.ParseUUID(res.Get("id").String()) ids = append(ids, id) } - require.Equal(t, len(ids), identitiesAmount) + require.Len(t, ids, identitiesAmount) }) t.Run("case=list few identities", func(t *testing.T) { - url := "/identities?ids=" + ids[0].String() + url := "/identities?ids=" + ids[0].String() + "&ids=" + ids[0].String() // duplicate ID is deduplicated in result for i := 1; i < listAmount; i++ { url += "&ids=" + ids[i].String() } res := get(t, adminTS, url, http.StatusOK) identities := res.Array() - require.Equal(t, len(identities), listAmount) + require.Len(t, identities, listAmount) }) }) + t.Run("case=list identities by ID is capped at 500", func(t *testing.T) { + url := "/identities?ids=" + x.NewUUID().String() + for i := 0; i < 501; i++ { + url += "&ids=" + x.NewUUID().String() + } + res := get(t, adminTS, url, http.StatusBadRequest) + assert.Contains(t, res.Get("error.reason").String(), "must not exceed 500") + }) + + t.Run("case=list identities cannot combine filters", func(t *testing.T) { + filters := []string{ + "ids=" + x.NewUUID().String(), + "credentials_identifier=foo@bar.com", + "credentials_identifier_similar=bar.com", + "organization_id=" + x.NewUUID().String(), + } + for i := range filters { + for j := range filters { + if i == j { + continue // OK to use the same filter multiple times. Behavior varies by filter, though. + } + + url := "/identities?" + filters[i] + "&" + filters[j] + res := get(t, adminTS, url, http.StatusBadRequest) + assert.Contains(t, res.Get("error.reason").String(), "cannot combine multiple filters") + } + } + }) + t.Run("case=malformed ids should return an error", func(t *testing.T) { res := get(t, adminTS, "/identities?ids=not-a-uuid", http.StatusBadRequest) assert.Contains(t, res.Get("error.reason").String(), "Invalid UUID value `not-a-uuid` for parameter `ids`.", "%s", res.Raw) diff --git a/internal/client-go/api_identity.go b/internal/client-go/api_identity.go index 9e4aec1b6c58..b7dd012adf83 100644 --- a/internal/client-go/api_identity.go +++ b/internal/client-go/api_identity.go @@ -227,7 +227,7 @@ type IdentityAPI interface { /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ @@ -2137,7 +2137,7 @@ func (r IdentityAPIApiListIdentitiesRequest) Execute() ([]Identity, *http.Respon /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ diff --git a/internal/httpclient/api_identity.go b/internal/httpclient/api_identity.go index 9e4aec1b6c58..b7dd012adf83 100644 --- a/internal/httpclient/api_identity.go +++ b/internal/httpclient/api_identity.go @@ -227,7 +227,7 @@ type IdentityAPI interface { /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ @@ -2137,7 +2137,7 @@ func (r IdentityAPIApiListIdentitiesRequest) Execute() ([]Identity, *http.Respon /* * ListIdentities List Identities - * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. + * Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return IdentityAPIApiListIdentitiesRequest */ diff --git a/spec/api.json b/spec/api.json index 907845a46f7c..87650d834bb7 100644 --- a/spec/api.json +++ b/spec/api.json @@ -3930,7 +3930,7 @@ }, "/admin/identities": { "get": { - "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system.", + "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.", "operationId": "listIdentities", "parameters": [ { diff --git a/spec/swagger.json b/spec/swagger.json index 031ad06841ba..9071e0d298bf 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -171,7 +171,7 @@ "oryAccessToken": [] } ], - "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system.", + "description": "Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.", "produces": [ "application/json" ], From d03d37d5c9736adc0893477ada8d52ab661772a8 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Fri, 13 Dec 2024 00:37:45 +0100 Subject: [PATCH 115/158] chore: bump golang.org/x/crypto --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index acdf909951c1..fb7135cdfd55 100644 --- a/go.mod +++ b/go.mod @@ -97,12 +97,12 @@ require ( go.opentelemetry.io/otel v1.32.0 go.opentelemetry.io/otel/sdk v1.32.0 go.opentelemetry.io/otel/trace v1.32.0 - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/net v0.31.0 golang.org/x/oauth2 v0.24.0 - golang.org/x/sync v0.9.0 - golang.org/x/text v0.20.0 + golang.org/x/sync v0.10.0 + golang.org/x/text v0.21.0 google.golang.org/grpc v1.67.1 ) @@ -118,7 +118,7 @@ require ( github.com/dgraph-io/ristretto/v2 v2.0.0 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/rjeczalik/notify v0.9.3 // indirect - golang.org/x/term v0.26.0 // indirect + golang.org/x/term v0.27.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect mvdan.cc/sh/v3 v3.6.0 // indirect ) @@ -313,7 +313,7 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sys v0.27.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/tools v0.23.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect diff --git a/go.sum b/go.sum index d48ddcf554f0..c6d4e25622eb 100644 --- a/go.sum +++ b/go.sum @@ -866,8 +866,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4 golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -981,8 +981,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1047,8 +1047,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1060,8 +1060,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1074,8 +1074,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From c17fb30d95ea9946a87eb8ed485ddb0f1ea83eac Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:19:27 +0000 Subject: [PATCH 116/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d258cdd832e0..587ab44449be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-04)](#2024-12-04) +- [ (2024-12-16)](#2024-12-16) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-04) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-16) ## Breaking Changes From 5ee54eda909638fa10c543f156042a217b34cba6 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 16 Dec 2024 17:14:36 +0100 Subject: [PATCH 117/158] fix: preview_credentials_identifier_similar (#4246) --- identity/handler.go | 2 +- identity/handler_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/identity/handler.go b/identity/handler.go index 98b73665d553..84bc886322fd 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -213,7 +213,7 @@ func parseListIdentitiesParameters(r *http.Request) (params ListIdentityParamete params.CredentialsIdentifier = identifier } - if identifier := query.Get("credentials_identifier_similar"); identifier != "" { + if identifier := query.Get("preview_credentials_identifier_similar"); identifier != "" { requestedFilters++ params.Expand = ExpandEverything params.CredentialsIdentifierSimilar = identifier diff --git a/identity/handler_test.go b/identity/handler_test.go index d55448cdce3b..66f4936961f3 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -397,7 +397,7 @@ func TestHandler(t *testing.T) { filters := []string{ "ids=" + x.NewUUID().String(), "credentials_identifier=foo@bar.com", - "credentials_identifier_similar=bar.com", + "preview_credentials_identifier_similar=bar.com", "organization_id=" + x.NewUUID().String(), } for i := range filters { From 0d1d00345e0f5b23a6f8b3f9f39db560b96fcb6e Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:04:02 +0000 Subject: [PATCH 118/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 587ab44449be..5decb8cba192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -391,6 +391,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Pass on correct context during verification ([#4151](https://github.com/ory/kratos/issues/4151)) ([7e0b500](https://github.com/ory/kratos/commit/7e0b500aada9c1931c759a43db7360e85afb57e3)) +- Preview_credentials_identifier_similar + ([#4246](https://github.com/ory/kratos/issues/4246)) + ([5ee54ed](https://github.com/ory/kratos/commit/5ee54eda909638fa10c543f156042a217b34cba6)) - Registration post persist hooks should not be cancelable ([#4148](https://github.com/ory/kratos/issues/4148)) ([18056a0](https://github.com/ory/kratos/commit/18056a0f1cfdf42769e5a974b2526ccf5c608cc2)) From 25429fa15268eacebf8a14139035bbc8f8b7661f Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Wed, 18 Dec 2024 18:51:15 +0100 Subject: [PATCH 119/158] chore: upgrade lib phone numbers to v1.4.1 (#4250) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fb7135cdfd55..a74ddb8ae8a5 100644 --- a/go.mod +++ b/go.mod @@ -257,7 +257,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/term v0.5.0 // indirect - github.com/nyaruka/phonenumbers v1.3.6 + github.com/nyaruka/phonenumbers v1.4.1 github.com/ogier/pflag v0.0.1 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect diff --git a/go.sum b/go.sum index c6d4e25622eb..6ced4bdca17e 100644 --- a/go.sum +++ b/go.sum @@ -594,8 +594,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nyaruka/phonenumbers v1.3.6 h1:33owXWp4d1U+Tyaj9fpci6PbvaQZcXBUO2FybeKeLwQ= -github.com/nyaruka/phonenumbers v1.3.6/go.mod h1:Ut+eFwikULbmCenH6InMKL9csUNLyxHuBLyfkpum11s= +github.com/nyaruka/phonenumbers v1.4.1 h1:dNsiYGirahC2lMRz3p2dxmmyLbzD3arCgmj/hPEVRPY= +github.com/nyaruka/phonenumbers v1.4.1/go.mod h1:gv+CtldaFz+G3vHHnasBSirAi3O2XLqZzVWz4V1pl2E= github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= From 6fea496e9f1e7a90db1e3519ed870ca64d505fdb Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:42:13 +0000 Subject: [PATCH 120/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5decb8cba192..19be2af35139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-16)](#2024-12-16) +- [ (2024-12-18)](#2024-12-18) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-16) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-18) ## Breaking Changes From f18d1b24539f7d8dcf9c27986af861d0f8cb9683 Mon Sep 17 00:00:00 2001 From: Henning Perl Date: Thu, 19 Dec 2024 12:58:41 +0100 Subject: [PATCH 121/158] feat: jackson provider (#4242) This adds a jackson provider to Kratos. --- driver/config/config.go | 5 ++ embedx/embedx.go | 6 +- identity/credentials.go | 4 +- internal/client-go/go.sum | 1 + .../sql/identity/persister_identity.go | 2 +- selfservice/flow/login/handler.go | 2 +- selfservice/flow/registration/handler.go | 4 +- selfservice/flow/registration/handler_test.go | 2 +- selfservice/strategy/oidc/provider_config.go | 1 + selfservice/strategy/oidc/provider_jackson.go | 57 +++++++++++++++++++ .../strategy/oidc/provider_jackson_test.go | 36 ++++++++++++ selfservice/strategy/oidc/strategy.go | 53 +++++++++++++---- selfservice/strategy/oidc/strategy_login.go | 4 +- .../strategy/oidc/strategy_registration.go | 4 +- 14 files changed, 156 insertions(+), 25 deletions(-) create mode 100644 selfservice/strategy/oidc/provider_jackson.go create mode 100644 selfservice/strategy/oidc/provider_jackson_test.go diff --git a/driver/config/config.go b/driver/config/config.go index b1e16e393f13..366f9e37150d 100644 --- a/driver/config/config.go +++ b/driver/config/config.go @@ -193,6 +193,7 @@ const ( ViperKeyIgnoreNetworkErrors = "selfservice.methods.password.config.ignore_network_errors" ViperKeyTOTPIssuer = "selfservice.methods.totp.config.issuer" ViperKeyOIDCBaseRedirectURL = "selfservice.methods.oidc.config.base_redirect_uri" + ViperKeySAMLBaseRedirectURL = "selfservice.methods.saml.config.base_redirect_uri" ViperKeyWebAuthnRPDisplayName = "selfservice.methods.webauthn.config.rp.display_name" ViperKeyWebAuthnRPID = "selfservice.methods.webauthn.config.rp.id" ViperKeyWebAuthnRPOrigin = "selfservice.methods.webauthn.config.rp.origin" @@ -616,6 +617,10 @@ func (p *Config) OIDCRedirectURIBase(ctx context.Context) *url.URL { return p.GetProvider(ctx).URIF(ViperKeyOIDCBaseRedirectURL, p.SelfPublicURL(ctx)) } +func (p *Config) SAMLRedirectURIBase(ctx context.Context) *url.URL { + return p.GetProvider(ctx).URIF(ViperKeySAMLBaseRedirectURL, p.SelfPublicURL(ctx)) +} + func (p *Config) IdentityTraitsSchemas(ctx context.Context) (ss Schemas, err error) { if err = p.GetProvider(ctx).Koanf.Unmarshal(ViperKeyIdentitySchemas, &ss); err != nil { return ss, nil diff --git a/embedx/embedx.go b/embedx/embedx.go index b91d86b8f692..5212337f4ea2 100644 --- a/embedx/embedx.go +++ b/embedx/embedx.go @@ -5,15 +5,13 @@ package embedx import ( "bytes" + _ "embed" "io" "github.com/pkg/errors" - - "github.com/ory/x/otelx" - "github.com/tidwall/gjson" - _ "embed" + "github.com/ory/x/otelx" ) //go:embed config.schema.json diff --git a/identity/credentials.go b/identity/credentials.go index 9fc2d93851bb..9f3865006f96 100644 --- a/identity/credentials.go +++ b/identity/credentials.go @@ -89,6 +89,7 @@ const ( CredentialsTypeCodeAuth CredentialsType = "code" CredentialsTypePasskey CredentialsType = "passkey" CredentialsTypeProfile CredentialsType = "profile" + CredentialsTypeSAML CredentialsType = "saml" ) func (c CredentialsType) String() string { @@ -99,7 +100,7 @@ func (c CredentialsType) ToUiNodeGroup() node.UiNodeGroup { switch c { case CredentialsTypePassword: return node.PasswordGroup - case CredentialsTypeOIDC: + case CredentialsTypeOIDC, CredentialsTypeSAML: return node.OpenIDConnectGroup case CredentialsTypeTOTP: return node.TOTPGroup @@ -138,6 +139,7 @@ func ParseCredentialsType(in string) (CredentialsType, bool) { for _, t := range []CredentialsType{ CredentialsTypePassword, CredentialsTypeOIDC, + CredentialsTypeSAML, CredentialsTypeTOTP, CredentialsTypeLookup, CredentialsTypeWebAuthn, diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index c966c8ddfd0d..6cc3f5911d11 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index 8d5a08f04415..990f97a550ba 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -1337,7 +1337,7 @@ func FindIdentityCredentialsTypeByName(con *pop.Connection, ct identity.Credenti } if !found { - return uuid.Nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The SQL adapter failed to return the appropriate credentials_type for nane %s. This is a bug in the code.", ct)) + return uuid.Nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The SQL adapter failed to return the appropriate credentials_type for name %q. This is a bug in the code.", ct)) } return result, nil diff --git a/selfservice/flow/login/handler.go b/selfservice/flow/login/handler.go index 267b33217216..f98ba66cd3fb 100644 --- a/selfservice/flow/login/handler.go +++ b/selfservice/flow/login/handler.go @@ -233,7 +233,7 @@ preLoginHook: // We only apply the filter on AAL1, because the OIDC strategy can only satsify // AAL1. strategyFilters = []StrategyFilter{func(s Strategy) bool { - return s.ID() == identity.CredentialsTypeOIDC + return s.ID() == identity.CredentialsTypeOIDC || s.ID() == identity.CredentialsTypeSAML }} } } diff --git a/selfservice/flow/registration/handler.go b/selfservice/flow/registration/handler.go index de52abefd5a7..c3f46d3a8397 100644 --- a/selfservice/flow/registration/handler.go +++ b/selfservice/flow/registration/handler.go @@ -141,7 +141,9 @@ func (h *Handler) NewRegistrationFlow(w http.ResponseWriter, r *http.Request, ft h.d.Logger().WithError(err).Warnf("ignoring invalid UUID %q in query parameter `organization`", rawOrg) } else { f.OrganizationID = uuid.NullUUID{UUID: orgID, Valid: true} - strategyFilters = []StrategyFilter{func(s Strategy) bool { return s.ID() == identity.CredentialsTypeOIDC }} + strategyFilters = []StrategyFilter{func(s Strategy) bool { + return s.ID() == identity.CredentialsTypeOIDC || s.ID() == identity.CredentialsTypeSAML + }} } } for _, s := range h.d.RegistrationStrategies(r.Context(), strategyFilters...) { diff --git a/selfservice/flow/registration/handler_test.go b/selfservice/flow/registration/handler_test.go index a9e7b842718c..c2767bdd4192 100644 --- a/selfservice/flow/registration/handler_test.go +++ b/selfservice/flow/registration/handler_test.go @@ -426,7 +426,7 @@ func TestOIDCStrategyOrder(t *testing.T) { // reorder the strategies reg.WithSelfserviceStrategies(t, []any{ - oidc.NewStrategy(reg), + oidc.NewStrategy(reg, oidc.ForCredentialType(identity.CredentialsTypeOIDC)), password.NewStrategy(reg), }) diff --git a/selfservice/strategy/oidc/provider_config.go b/selfservice/strategy/oidc/provider_config.go index 7b580f9bc10b..e866aea17f84 100644 --- a/selfservice/strategy/oidc/provider_config.go +++ b/selfservice/strategy/oidc/provider_config.go @@ -177,6 +177,7 @@ var supportedProviders = map[string]func(config *Configuration, reg Dependencies "patreon": NewProviderPatreon, "lark": NewProviderLark, "x": NewProviderX, + "jackson": NewProviderJackson, } func (c ConfigurationCollection) Provider(id string, reg Dependencies) (Provider, error) { diff --git a/selfservice/strategy/oidc/provider_jackson.go b/selfservice/strategy/oidc/provider_jackson.go new file mode 100644 index 000000000000..f83a88306e62 --- /dev/null +++ b/selfservice/strategy/oidc/provider_jackson.go @@ -0,0 +1,57 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package oidc + +import ( + "context" + "strings" + + "github.com/coreos/go-oidc/v3/oidc" + "golang.org/x/oauth2" + + "github.com/ory/x/urlx" +) + +type ProviderJackson struct { + *ProviderGenericOIDC +} + +func NewProviderJackson( + config *Configuration, + reg Dependencies, +) Provider { + return &ProviderJackson{ + ProviderGenericOIDC: &ProviderGenericOIDC{ + config: config, + reg: reg, + }, + } +} + +func (j *ProviderJackson) setProvider(ctx context.Context) { + if j.ProviderGenericOIDC.p == nil { + internalHost := strings.TrimSuffix(j.config.TokenURL, "/api/oauth/token") + config := oidc.ProviderConfig{ + IssuerURL: j.config.IssuerURL, + AuthURL: j.config.AuthURL, + TokenURL: j.config.TokenURL, + DeviceAuthURL: "", + UserInfoURL: internalHost + "/api/oauth/userinfo", + JWKSURL: internalHost + "/oauth/jwks", + Algorithms: []string{"RS256"}, + } + j.ProviderGenericOIDC.p = config.NewProvider(j.withHTTPClientContext(ctx)) + } +} + +func (j *ProviderJackson) OAuth2(ctx context.Context) (*oauth2.Config, error) { + j.setProvider(ctx) + endpoint := j.ProviderGenericOIDC.p.Endpoint() + config := j.oauth2ConfigFromEndpoint(ctx, endpoint) + config.RedirectURL = urlx.AppendPaths( + j.reg.Config().SAMLRedirectURIBase(ctx), + "/self-service/methods/saml/callback/"+j.config.ID).String() + + return config, nil +} diff --git a/selfservice/strategy/oidc/provider_jackson_test.go b/selfservice/strategy/oidc/provider_jackson_test.go new file mode 100644 index 000000000000..4c8bfa0bbf55 --- /dev/null +++ b/selfservice/strategy/oidc/provider_jackson_test.go @@ -0,0 +1,36 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package oidc_test + +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ory/kratos/internal" + "github.com/ory/kratos/selfservice/strategy/oidc" +) + +func TestProviderJackson(t *testing.T) { + _, reg := internal.NewVeryFastRegistryWithoutDB(t) + + j := oidc.NewProviderJackson(&oidc.Configuration{ + Provider: "jackson", + IssuerURL: "https://www.jackson.com/oauth", + AuthURL: "https://www.jackson.com/oauth/auth", + TokenURL: "https://www.jackson.com/api/oauth/token", + Mapper: "file://./stub/hydra.schema.json", + Scope: []string{"email", "profile"}, + ID: "some-id", + }, reg) + assert.NotNil(t, j) + + c, err := j.(oidc.OAuth2Provider).OAuth2(context.Background()) + require.NoError(t, err) + + assert.True(t, strings.HasSuffix(c.RedirectURL, "/self-service/methods/saml/callback/some-id")) +} diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index d799c9190dcd..b49883757f5d 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -44,7 +44,6 @@ import ( "github.com/ory/kratos/x" "github.com/ory/x/decoderx" "github.com/ory/x/jsonnetsecure" - "github.com/ory/x/jsonx" "github.com/ory/x/otelx" "github.com/ory/x/sqlxx" "github.com/ory/x/stringsx" @@ -119,9 +118,12 @@ func isForced(req interface{}) bool { // Strategy implements selfservice.LoginStrategy, selfservice.RegistrationStrategy and selfservice.SettingsStrategy. // It supports login, registration and settings via OpenID Providers. type Strategy struct { - d Dependencies - validator *schema.Validator - dec *decoderx.HTTP + d Dependencies + validator *schema.Validator + dec *decoderx.HTTP + credType identity.CredentialsType + handleUnknownProviderError func(err error) error + handleMethodNotAllowedError func(err error) error } type AuthCodeContainer struct { @@ -203,15 +205,42 @@ func (s *Strategy) redirectToGET(w http.ResponseWriter, r *http.Request, _ httpr http.Redirect(w, r, dest.String(), http.StatusFound) } -func NewStrategy(d any) *Strategy { - return &Strategy{ - d: d.(Dependencies), - validator: schema.NewValidator(), +type NewStrategyOpt func(s *Strategy) + +// ForCredentialType overrides the credentials type for this strategy. +func ForCredentialType(ct identity.CredentialsType) NewStrategyOpt { + return func(s *Strategy) { s.credType = ct } +} + +// WithUnknownProviderHandler overrides the error returned when the provider +// cannot be found. +func WithUnknownProviderHandler(handler func(error) error) NewStrategyOpt { + return func(s *Strategy) { s.handleUnknownProviderError = handler } +} + +// WithHandleMethodNotAllowedError overrides the error returned when method is +// not allowed. +func WithHandleMethodNotAllowedError(handler func(error) error) NewStrategyOpt { + return func(s *Strategy) { s.handleMethodNotAllowedError = handler } +} + +func NewStrategy(d any, opts ...NewStrategyOpt) *Strategy { + s := &Strategy{ + d: d.(Dependencies), + validator: schema.NewValidator(), + credType: identity.CredentialsTypeOIDC, + handleUnknownProviderError: func(err error) error { return err }, + handleMethodNotAllowedError: func(err error) error { return err }, + } + for _, opt := range opts { + opt(s) } + + return s } func (s *Strategy) ID() identity.CredentialsType { - return identity.CredentialsTypeOIDC + return s.credType } func (s *Strategy) validateFlow(ctx context.Context, r *http.Request, rid uuid.UUID) (flow.Flow, error) { @@ -516,8 +545,8 @@ func (s *Strategy) Config(ctx context.Context) (*ConfigurationCollection, error) var c ConfigurationCollection conf := s.d.Config().SelfServiceStrategy(ctx, string(s.ID())).Config - if err := jsonx. - NewStrictDecoder(bytes.NewBuffer(conf)). + if err := json. + NewDecoder(bytes.NewBuffer(conf)). Decode(&c); err != nil { s.d.Logger().WithError(err).WithField("config", conf) return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to decode OpenID Connect Provider configuration: %s", err)) @@ -530,7 +559,7 @@ func (s *Strategy) provider(ctx context.Context, id string) (Provider, error) { if c, err := s.Config(ctx); err != nil { return nil, err } else if provider, err := c.Provider(id, s.d); err != nil { - return nil, err + return nil, s.handleUnknownProviderError(err) } else { return provider, nil } diff --git a/selfservice/strategy/oidc/strategy_login.go b/selfservice/strategy/oidc/strategy_login.go index 773a500d59a3..392009ec2241 100644 --- a/selfservice/strategy/oidc/strategy_login.go +++ b/selfservice/strategy/oidc/strategy_login.go @@ -102,7 +102,7 @@ func (s *Strategy) processLogin(ctx context.Context, w http.ResponseWriter, r *h ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.processLogin") defer otelx.End(span, &err) - i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeOIDC, identity.OIDCUniqueID(provider.Config().ID, claims.Subject)) + i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), identity.OIDCUniqueID(provider.Config().ID, claims.Subject)) if err != nil { if errors.Is(err, sqlcon.ErrNoRows) { // If no account was found we're "manually" creating a new registration flow and redirecting the browser @@ -218,7 +218,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, } if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil { - return nil, s.handleError(ctx, w, r, f, pid, nil, err) + return nil, s.handleError(ctx, w, r, f, pid, nil, s.handleMethodNotAllowedError(err)) } provider, err := s.provider(ctx, pid) diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index cf7dd35bbba6..5ed061119e7b 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -181,7 +181,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat } if err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil { - return s.handleError(ctx, w, r, f, pid, nil, err) + return s.handleError(ctx, w, r, f, pid, nil, s.handleMethodNotAllowedError(err)) } provider, err := s.provider(ctx, pid) @@ -347,7 +347,7 @@ func (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWrite } i.SetCredentials(s.ID(), *creds) - if err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, identity.CredentialsTypeOIDC, provider.Config().ID, provider.Config().OrganizationID, rf, i); err != nil { + if err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, s.ID(), provider.Config().ID, provider.Config().OrganizationID, rf, i); err != nil { return nil, s.handleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err) } From 32853ddbfb1b8b8c3908c3c47be3ec267265b621 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:00:15 +0000 Subject: [PATCH 122/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- internal/client-go/api_identity.go | 4 +-- internal/client-go/go.sum | 1 - .../client-go/model_identity_credentials.go | 2 +- internal/client-go/model_login_flow.go | 2 +- internal/client-go/model_registration_flow.go | 2 +- internal/httpclient/api_identity.go | 4 +-- .../httpclient/model_identity_credentials.go | 2 +- internal/httpclient/model_login_flow.go | 2 +- .../httpclient/model_registration_flow.go | 2 +- spec/api.json | 21 +++++++++------ spec/swagger.json | 26 ++++++++++++------- 11 files changed, 39 insertions(+), 29 deletions(-) diff --git a/internal/client-go/api_identity.go b/internal/client-go/api_identity.go index b7dd012adf83..2daa8d8d4971 100644 --- a/internal/client-go/api_identity.go +++ b/internal/client-go/api_identity.go @@ -114,7 +114,7 @@ type IdentityAPI interface { You cannot delete password or code auth credentials through this API. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @param id ID is the identity's ID. - * @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + * @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode * @return IdentityAPIApiDeleteIdentityCredentialsRequest */ DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest @@ -1090,7 +1090,7 @@ func (r IdentityAPIApiDeleteIdentityCredentialsRequest) Execute() (*http.Respons You cannot delete password or code auth credentials through this API. - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID is the identity's ID. - - @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + - @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode - @return IdentityAPIApiDeleteIdentityCredentialsRequest */ func (a *IdentityAPIService) DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest { diff --git a/internal/client-go/go.sum b/internal/client-go/go.sum index 6cc3f5911d11..c966c8ddfd0d 100644 --- a/internal/client-go/go.sum +++ b/internal/client-go/go.sum @@ -4,7 +4,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/client-go/model_identity_credentials.go b/internal/client-go/model_identity_credentials.go index 7ee96800df4b..de087e64e09f 100644 --- a/internal/client-go/model_identity_credentials.go +++ b/internal/client-go/model_identity_credentials.go @@ -23,7 +23,7 @@ type IdentityCredentials struct { CreatedAt *time.Time `json:"created_at,omitempty"` // Identifiers represents a list of unique identifiers this credential type matches. Identifiers []string `json:"identifiers,omitempty"` - // Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + // Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode Type *string `json:"type,omitempty"` // UpdatedAt is a helper struct field for gobuffalo.pop. UpdatedAt *time.Time `json:"updated_at,omitempty"` diff --git a/internal/client-go/model_login_flow.go b/internal/client-go/model_login_flow.go index 2794adee0b83..5fc35379ea48 100644 --- a/internal/client-go/model_login_flow.go +++ b/internal/client-go/model_login_flow.go @@ -18,7 +18,7 @@ import ( // LoginFlow This object represents a login flow. A login flow is initiated at the \"Initiate Login API / Browser Flow\" endpoint by a client. Once a login flow is completed successfully, a session cookie or session token will be issued. type LoginFlow struct { - // The active login method If set contains the login method used. If the flow is new, it is unset. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + // The active login method If set contains the login method used. If the flow is new, it is unset. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode Active *string `json:"active,omitempty"` // CreatedAt is a helper struct field for gobuffalo.pop. CreatedAt *time.Time `json:"created_at,omitempty"` diff --git a/internal/client-go/model_registration_flow.go b/internal/client-go/model_registration_flow.go index c0ba64843d3f..4eb2d78f6052 100644 --- a/internal/client-go/model_registration_flow.go +++ b/internal/client-go/model_registration_flow.go @@ -18,7 +18,7 @@ import ( // RegistrationFlow struct for RegistrationFlow type RegistrationFlow struct { - // Active, if set, contains the registration method that is being used. It is initially not set. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + // Active, if set, contains the registration method that is being used. It is initially not set. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode Active *string `json:"active,omitempty"` // ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated. ExpiresAt time.Time `json:"expires_at"` diff --git a/internal/httpclient/api_identity.go b/internal/httpclient/api_identity.go index b7dd012adf83..2daa8d8d4971 100644 --- a/internal/httpclient/api_identity.go +++ b/internal/httpclient/api_identity.go @@ -114,7 +114,7 @@ type IdentityAPI interface { You cannot delete password or code auth credentials through this API. * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @param id ID is the identity's ID. - * @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + * @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode * @return IdentityAPIApiDeleteIdentityCredentialsRequest */ DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest @@ -1090,7 +1090,7 @@ func (r IdentityAPIApiDeleteIdentityCredentialsRequest) Execute() (*http.Respons You cannot delete password or code auth credentials through this API. - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID is the identity's ID. - - @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + - @param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode - @return IdentityAPIApiDeleteIdentityCredentialsRequest */ func (a *IdentityAPIService) DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIApiDeleteIdentityCredentialsRequest { diff --git a/internal/httpclient/model_identity_credentials.go b/internal/httpclient/model_identity_credentials.go index 7ee96800df4b..de087e64e09f 100644 --- a/internal/httpclient/model_identity_credentials.go +++ b/internal/httpclient/model_identity_credentials.go @@ -23,7 +23,7 @@ type IdentityCredentials struct { CreatedAt *time.Time `json:"created_at,omitempty"` // Identifiers represents a list of unique identifiers this credential type matches. Identifiers []string `json:"identifiers,omitempty"` - // Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + // Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode Type *string `json:"type,omitempty"` // UpdatedAt is a helper struct field for gobuffalo.pop. UpdatedAt *time.Time `json:"updated_at,omitempty"` diff --git a/internal/httpclient/model_login_flow.go b/internal/httpclient/model_login_flow.go index 2794adee0b83..5fc35379ea48 100644 --- a/internal/httpclient/model_login_flow.go +++ b/internal/httpclient/model_login_flow.go @@ -18,7 +18,7 @@ import ( // LoginFlow This object represents a login flow. A login flow is initiated at the \"Initiate Login API / Browser Flow\" endpoint by a client. Once a login flow is completed successfully, a session cookie or session token will be issued. type LoginFlow struct { - // The active login method If set contains the login method used. If the flow is new, it is unset. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + // The active login method If set contains the login method used. If the flow is new, it is unset. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode Active *string `json:"active,omitempty"` // CreatedAt is a helper struct field for gobuffalo.pop. CreatedAt *time.Time `json:"created_at,omitempty"` diff --git a/internal/httpclient/model_registration_flow.go b/internal/httpclient/model_registration_flow.go index c0ba64843d3f..4eb2d78f6052 100644 --- a/internal/httpclient/model_registration_flow.go +++ b/internal/httpclient/model_registration_flow.go @@ -18,7 +18,7 @@ import ( // RegistrationFlow struct for RegistrationFlow type RegistrationFlow struct { - // Active, if set, contains the registration method that is being used. It is initially not set. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode + // Active, if set, contains the registration method that is being used. It is initially not set. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode Active *string `json:"active,omitempty"` // ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated. ExpiresAt time.Time `json:"expires_at"` diff --git a/spec/api.json b/spec/api.json index 87650d834bb7..5c3cac8a2696 100644 --- a/spec/api.json +++ b/spec/api.json @@ -1030,7 +1030,7 @@ "type": "array" }, "type": { - "description": "Type discriminates between different types of credentials.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "Type discriminates between different types of credentials.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "enum": [ "password", "oidc", @@ -1040,11 +1040,12 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, "updated_at": { "description": "UpdatedAt is a helper struct field for gobuffalo.pop.", @@ -1328,7 +1329,7 @@ "description": "This object represents a login flow. A login flow is initiated at the \"Initiate Login API / Browser Flow\"\nendpoint by a client.\n\nOnce a login flow is completed successfully, a session cookie or session token will be issued.", "properties": { "active": { - "description": "The active login method\n\nIf set contains the login method used. If the flow is new, it is unset.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "The active login method\n\nIf set contains the login method used. If the flow is new, it is unset.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "enum": [ "password", "oidc", @@ -1338,11 +1339,12 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, "created_at": { "description": "CreatedAt is a helper struct field for gobuffalo.pop.", @@ -1783,7 +1785,7 @@ "registrationFlow": { "properties": { "active": { - "description": "Active, if set, contains the registration method that is being used. It is initially\nnot set.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "Active, if set, contains the registration method that is being used. It is initially\nnot set.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "enum": [ "password", "oidc", @@ -1793,11 +1795,12 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, "expires_at": { "description": "ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\na new flow has to be initiated.", @@ -4271,6 +4274,7 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], @@ -4510,7 +4514,7 @@ } }, { - "description": "Type is the type of credentials to delete.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "Type is the type of credentials to delete.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "in": "path", "name": "type", "required": true, @@ -4524,12 +4528,13 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string" }, - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, { "description": "Identifier is the identifier of the OIDC credential to delete.\nFind the identifier by calling the `GET /admin/identities/{id}?include_credential=oidc` endpoint.", diff --git a/spec/swagger.json b/spec/swagger.json index 9071e0d298bf..35296b3acb48 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -441,6 +441,7 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], @@ -702,12 +703,13 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], "type": "string", - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", - "description": "Type is the type of credentials to delete.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "Type is the type of credentials to delete.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "name": "type", "in": "path", "required": true @@ -4181,7 +4183,7 @@ } }, "type": { - "description": "Type discriminates between different types of credentials.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "Type discriminates between different types of credentials.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "type": "string", "enum": [ "password", @@ -4192,10 +4194,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, "updated_at": { "description": "UpdatedAt is a helper struct field for gobuffalo.pop.", @@ -4490,7 +4493,7 @@ ], "properties": { "active": { - "description": "The active login method\n\nIf set contains the login method used. If the flow is new, it is unset.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "The active login method\n\nIf set contains the login method used. If the flow is new, it is unset.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "type": "string", "enum": [ "password", @@ -4501,10 +4504,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, "created_at": { "description": "CreatedAt is a helper struct field for gobuffalo.pop.", @@ -4916,7 +4920,7 @@ ], "properties": { "active": { - "description": "Active, if set, contains the registration method that is being used. It is initially\nnot set.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "Active, if set, contains the registration method that is being used. It is initially\nnot set.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "type": "string", "enum": [ "password", @@ -4927,10 +4931,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, "expires_at": { "description": "ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\na new flow has to be initiated.", @@ -5078,7 +5083,7 @@ "format": "date-time" }, "method": { - "description": "The method used in this authenticator.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", + "description": "The method used in this authenticator.\npassword CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode", "type": "string", "enum": [ "password", @@ -5089,10 +5094,11 @@ "code", "passkey", "profile", + "saml", "link_recovery", "code_recovery" ], - "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" + "x-go-enum-desc": "password CredentialsTypePassword\noidc CredentialsTypeOIDC\ntotp CredentialsTypeTOTP\nlookup_secret CredentialsTypeLookup\nwebauthn CredentialsTypeWebAuthn\ncode CredentialsTypeCodeAuth\npasskey CredentialsTypePasskey\nprofile CredentialsTypeProfile\nsaml CredentialsTypeSAML\nlink_recovery CredentialsTypeRecoveryLink CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow). It is not used within the credentials object itself.\ncode_recovery CredentialsTypeRecoveryCode" }, "organization": { "description": "The Organization id used for authentication", From a893cd8254b820c439f96b5616adf3b1a0f97759 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:50:45 +0000 Subject: [PATCH 123/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19be2af35139..fb5bef4b824e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-18)](#2024-12-18) +- [ (2024-12-19)](#2024-12-19) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-18) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-19) ## Breaking Changes @@ -541,6 +541,11 @@ https://github.com/ory-corp/cloud/issues/7176 - Improve secondary indices for self service tables ([#4179](https://github.com/ory/kratos/issues/4179)) ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253)) +- Jackson provider ([#4242](https://github.com/ory/kratos/issues/4242)) + ([f18d1b2](https://github.com/ory/kratos/commit/f18d1b24539f7d8dcf9c27986af861d0f8cb9683)): + + This adds a jackson provider to Kratos. + - Load session only once when middleware is used ([#4187](https://github.com/ory/kratos/issues/4187)) ([234b6f2](https://github.com/ory/kratos/commit/234b6f2f6435c62b7e161c032b888c4e2b3328d4)) From 0bce294f78367cdcde07945f1fb67acb2e92d622 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Fri, 20 Dec 2024 09:53:41 +0100 Subject: [PATCH 124/158] chore: update docs on ListIdentities (#4248) --- identity/handler.go | 12 ++++++++---- spec/api.json | 4 ++-- spec/swagger.json | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/identity/handler.go b/identity/handler.go index 84bc886322fd..6d458636854d 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -136,8 +136,13 @@ type _ struct { type _ struct { migrationpagination.RequestParameters - // List of ids used to filter identities. - // If this list is empty, then no filter will be applied. + // Retrieve multiple identities by their IDs. + // + // This parameter has the following limitations: + // + // - Duplicate or non-existent IDs are ignored. + // - The order of returned IDs may be different from the request. + // - This filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500). // // required: false // in: query @@ -169,9 +174,8 @@ type _ struct { // in: query DeclassifyCredentials []string `json:"include_credential"` - // OrganizationID is the organization id to filter identities by. + // List identities that belong to a specific organization. // - // If `ids` is set, this parameter is ignored. // required: false // in: query OrganizationID string `json:"organization_id"` diff --git a/spec/api.json b/spec/api.json index 5c3cac8a2696..39c8f6c1d969 100644 --- a/spec/api.json +++ b/spec/api.json @@ -3994,7 +3994,7 @@ "x-go-enum-desc": " ConsistencyLevelUnset ConsistencyLevelUnset is the unset / default consistency level.\nstrong ConsistencyLevelStrong ConsistencyLevelStrong is the strong consistency level.\neventual ConsistencyLevelEventual ConsistencyLevelEventual is the eventual consistency level using follower read timestamps." }, { - "description": "List of ids used to filter identities.\nIf this list is empty, then no filter will be applied.", + "description": "Retrieve multiple identities by their IDs.\nThe order of the IDs in the response may be different from the request.\nDuplicate or non-existent IDs are ignored.\nMax. no. of ids: 500\nThis filter does not support pagination. To retrieve more than 500\nidentities by ID, split your request into multiple calls client-side.", "in": "query", "name": "ids", "schema": { @@ -4032,7 +4032,7 @@ } }, { - "description": "OrganizationID is the organization id to filter identities by.\n\nIf `ids` is set, this parameter is ignored.", + "description": "List identities that belong to a specific organization.", "in": "query", "name": "organization_id", "schema": { diff --git a/spec/swagger.json b/spec/swagger.json index 35296b3acb48..81fdc20ab8e2 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -237,7 +237,7 @@ "items": { "type": "string" }, - "description": "List of ids used to filter identities.\nIf this list is empty, then no filter will be applied.", + "description": "Retrieve multiple identities by their IDs.\nThe order of the IDs in the response may be different from the request.\nDuplicate or non-existent IDs are ignored.\nMax. no. of ids: 500\nThis filter does not support pagination. To retrieve more than 500\nidentities by ID, split your request into multiple calls client-side.", "name": "ids", "in": "query" }, @@ -264,7 +264,7 @@ }, { "type": "string", - "description": "OrganizationID is the organization id to filter identities by.\n\nIf `ids` is set, this parameter is ignored.", + "description": "List identities that belong to a specific organization.", "name": "organization_id", "in": "query" } From d9f6f75b6a43aad996f6390f73616a2cf596c6e4 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:54:37 +0100 Subject: [PATCH 125/158] fix: cancel conditional passkey before trying again (#4247) --- ...inMethodIdentifierFirstIdentification.json | 2 +- .../strategy/idfirst/strategy_login.go | 4 +-- ...sswordless-case=passkey_button_exists.json | 2 +- ...resh_passwordless_credentials-browser.json | 2 +- ...=refresh_passwordless_credentials-spa.json | 2 +- ...device_is_shown_which_can_be_unlinked.json | 2 +- ...-case=one_activation_element_is_shown.json | 2 +- ...method=PopulateLoginMethodFirstFactor.json | 2 +- ...PopulateLoginMethodFirstFactorRefresh.json | 2 +- ...inMethodIdentifierFirstIdentification.json | 2 +- ...on-case=passkey_button_exists-browser.json | 2 +- ...ration-case=passkey_button_exists-spa.json | 2 +- ...oad_is_set_when_identity_has_webauthn.json | 2 +- ...ebauthn_login_is_invalid-type=browser.json | 2 +- ...if_webauthn_login_is_invalid-type=spa.json | 2 +- ...false-case=mfa_v0_credentials-browser.json | 2 +- ...led=false-case=mfa_v0_credentials-spa.json | 2 +- ...false-case=mfa_v1_credentials-browser.json | 2 +- ...led=false-case=mfa_v1_credentials-spa.json | 2 +- ...case=passwordless_credentials-browser.json | 2 +- ...rue-case=passwordless_credentials-spa.json | 2 +- ...device_is_shown_which_can_be_unlinked.json | 2 +- ...-case=one_activation_element_is_shown.json | 2 +- ..._enabled_and_user_has_mfa_credentials.json | 2 +- ...and_user_has_passwordless_credentials.json | 2 +- ...inMethodSecondFactor-case=mfa_enabled.json | 2 +- ...n-case=webauthn_button_exists-browser.json | 2 +- ...ation-case=webauthn_button_exists-spa.json | 2 +- x/webauthnx/js/webauthn.js | 28 ++++++++++++++----- 29 files changed, 50 insertions(+), 36 deletions(-) diff --git a/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json b/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json index 73e408add0a0..4517b39e7474 100644 --- a/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json +++ b/selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json @@ -20,7 +20,7 @@ "type": "text", "value": "", "required": true, - "autocomplete": "username email", + "autocomplete": "username webauthn", "disabled": false, "node_type": "input" }, diff --git a/selfservice/strategy/idfirst/strategy_login.go b/selfservice/strategy/idfirst/strategy_login.go index 1f745a2aba07..edf83c487442 100644 --- a/selfservice/strategy/idfirst/strategy_login.go +++ b/selfservice/strategy/idfirst/strategy_login.go @@ -136,7 +136,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, if !ok { continue } - attrs.Autocomplete = "username email" + attrs.Autocomplete = "username webauthn" attrs.Type = node.InputAttributeTypeHidden f.UI.Nodes[k].Attributes = attrs @@ -186,7 +186,7 @@ func (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Requ } f.UI.SetNode(node.NewInputField("identifier", "", s.NodeGroup(), node.InputAttributeTypeText, node.WithInputAttributes(func(a *node.InputAttributes) { - a.Autocomplete = "username email" + a.Autocomplete = "username webauthn" a.Required = true })).WithMetaLabel(identifierLabel)) f.UI.GetNodes().Append(node.NewInputField("method", s.ID(), s.NodeGroup(), node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelContinue())) diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json index 39b1e8a8ca59..ce6f722551f9 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json @@ -38,7 +38,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json index 269754d1dbd0..46a7896863a1 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json index 269754d1dbd0..46a7896863a1 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json @@ -30,7 +30,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index 354fdfab6feb..65ade1871cfe 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -110,7 +110,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index 3065bddabb0f..293a8752d52b 100644 --- a/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -62,7 +62,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json index 9ea8913db0aa..7465a9a5ae82 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json index 0d33b6d7d9fb..c586635ce49a 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json @@ -18,7 +18,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json index 911497b207da..e65526e82d48 100644 --- a/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json +++ b/selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json @@ -52,7 +52,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json index c0d75cd7cd1d..2068eb38ef1c 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json index c0d75cd7cd1d..2068eb38ef1c 100644 --- a/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json +++ b/selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json index 4d8766c503b9..7f1f252857ec 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json @@ -42,7 +42,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json index d26936d42077..d3e3d320af14 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json index d26936d42077..d3e3d320af14 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json @@ -37,7 +37,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "node_type": "script" }, diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json index a17789700612..2df3a118d304 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json @@ -43,7 +43,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json index 1d38764e30a6..c335744d6532 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json @@ -116,7 +116,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json index 628b00fd8b5f..ff26034abc11 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json +++ b/selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json @@ -68,7 +68,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json index bd8b5253db96..9f84956e3f6c 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json index bd8b5253db96..9f84956e3f6c 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json index bd8b5253db96..9f84956e3f6c 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json +++ b/selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json @@ -31,7 +31,7 @@ "async": true, "referrerpolicy": "no-referrer", "crossorigin": "anonymous", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "type": "text/javascript", "id": "webauthn_script", "node_type": "script" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json index 4d51e6ea1536..733a28311ebd 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json index 4d51e6ea1536..733a28311ebd 100644 --- a/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json +++ b/selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json @@ -94,7 +94,7 @@ "async": true, "crossorigin": "anonymous", "id": "webauthn_script", - "integrity": "sha512-3Z1fDRo1yulzEUWcPb/35UhuKYyNgM/z70Pnidfr1pQGtRZz2xaFinaEyIiolwRTx+0B43ATFQcMsyaDJxC0tA==", + "integrity": "sha512-GJndj+bkFBMHiun3qBMmFh5eeGodY/eSh8tg50xHcNEdOBCIKnlofYd2slaBTtVpyI4opfkMc/zw+nwBjGdAbw==", "node_type": "script", "referrerpolicy": "no-referrer", "type": "text/javascript" diff --git a/x/webauthnx/js/webauthn.js b/x/webauthnx/js/webauthn.js index 4bc0d4427aa9..790896c1926b 100644 --- a/x/webauthnx/js/webauthn.js +++ b/x/webauthnx/js/webauthn.js @@ -143,8 +143,8 @@ const identifierEl = document.getElementsByName("identifier")[0] if (!dataEl || !resultEl || !identifierEl) { - console.debug( - "__oryPasskeyLoginAutocompleteInit: mandatory fields not found", + console.error( + "Unable to initialize WebAuthn / Passkey autocomplete because one or more required form fields are missing.", ) return } @@ -154,9 +154,10 @@ !window.PublicKeyCredential.isConditionalMediationAvailable || window.Cypress // Cypress auto-fills the autocomplete, which we don't want ) { - console.log("This browser does not support WebAuthn!") + console.log("This browser does not support Passkey / WebAuthn!") return } + const isCMA = await PublicKeyCredential.isConditionalMediationAvailable() if (!isCMA) { console.log( @@ -172,6 +173,14 @@ } opt.publicKey.challenge = __oryWebAuthnBufferDecode(opt.publicKey.challenge) + // If this is set we already have a request ongoing which we need to abort. + if (window.abortPasskeyConditionalUI) { + window.abortPasskeyConditionalUI.abort( + "Canceling Passkey autocomplete to complete trigger-based passkey login.", + ) + window.abortPasskeyConditionalUI = undefined + } + // Allow aborting through a global variable window.abortPasskeyConditionalUI = new AbortController() @@ -182,7 +191,6 @@ signal: abortPasskeyConditionalUI.signal, }) .then(function (credential) { - console.trace(credential) resultEl.value = JSON.stringify({ id: credential.id, rawId: __oryWebAuthnBufferEncode(credential.rawId), @@ -214,7 +222,9 @@ const resultEl = document.getElementsByName("passkey_login")[0] if (!dataEl || !resultEl) { - console.debug("__oryPasskeyLogin: mandatory fields not found") + console.error( + "Unable to initialize WebAuthn / Passkey autocomplete because one or more required form fields are missing.", + ) return } if (!window.PublicKeyCredential) { @@ -239,10 +249,12 @@ ) } - window.abortPasskeyConditionalUI && + if (window.abortPasskeyConditionalUI) { window.abortPasskeyConditionalUI.abort( "Canceling Passkey autocomplete to complete trigger-based passkey login.", ) + window.abortPasskeyConditionalUI = undefined + } navigator.credentials .get({ @@ -279,7 +291,9 @@ } console.trace(err) - window.abortPasskeyConditionalUI && __oryPasskeyLoginAutocompleteInit() + + // Try re-initializing autocomplete + return __oryPasskeyLoginAutocompleteInit() }) } From 45538f1111622cfa06f2d658a1d6e5db9cb0a974 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:56:27 +0000 Subject: [PATCH 126/158] autogen(openapi): regenerate swagger spec and internal client [skip ci] --- spec/api.json | 2 +- spec/swagger.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/api.json b/spec/api.json index 39c8f6c1d969..7cdd1a4482bc 100644 --- a/spec/api.json +++ b/spec/api.json @@ -3994,7 +3994,7 @@ "x-go-enum-desc": " ConsistencyLevelUnset ConsistencyLevelUnset is the unset / default consistency level.\nstrong ConsistencyLevelStrong ConsistencyLevelStrong is the strong consistency level.\neventual ConsistencyLevelEventual ConsistencyLevelEventual is the eventual consistency level using follower read timestamps." }, { - "description": "Retrieve multiple identities by their IDs.\nThe order of the IDs in the response may be different from the request.\nDuplicate or non-existent IDs are ignored.\nMax. no. of ids: 500\nThis filter does not support pagination. To retrieve more than 500\nidentities by ID, split your request into multiple calls client-side.", + "description": "Retrieve multiple identities by their IDs.\n\nThis parameter has the following limitations:\n\nDuplicate or non-existent IDs are ignored.\nThe order of returned IDs may be different from the request.\nThis filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500).", "in": "query", "name": "ids", "schema": { diff --git a/spec/swagger.json b/spec/swagger.json index 81fdc20ab8e2..7fbee9f76ae6 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -237,7 +237,7 @@ "items": { "type": "string" }, - "description": "Retrieve multiple identities by their IDs.\nThe order of the IDs in the response may be different from the request.\nDuplicate or non-existent IDs are ignored.\nMax. no. of ids: 500\nThis filter does not support pagination. To retrieve more than 500\nidentities by ID, split your request into multiple calls client-side.", + "description": "Retrieve multiple identities by their IDs.\n\nThis parameter has the following limitations:\n\nDuplicate or non-existent IDs are ignored.\nThe order of returned IDs may be different from the request.\nThis filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500).", "name": "ids", "in": "query" }, From deb3661d8c17862e83bb079c3c5a321767eb78b0 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:47:46 +0000 Subject: [PATCH 127/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb5bef4b824e..05ec8cd20fa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-19)](#2024-12-19) +- [ (2024-12-20)](#2024-12-20) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-19) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-20) ## Breaking Changes @@ -366,6 +366,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Add missing autocomplete attributes to identifier_first strategy ([#4215](https://github.com/ory/kratos/issues/4215)) ([e1f29c2](https://github.com/ory/kratos/commit/e1f29c2d3524f9444ec067c52d2c9f1d44fa6539)) +- Cancel conditional passkey before trying again + ([#4247](https://github.com/ory/kratos/issues/4247)) + ([d9f6f75](https://github.com/ory/kratos/commit/d9f6f75b6a43aad996f6390f73616a2cf596c6e4)) - Do not roll back transaction on partial identity insert error ([#4211](https://github.com/ory/kratos/issues/4211)) ([82660f0](https://github.com/ory/kratos/commit/82660f04e2f33d0aa86fccee42c90773a901d400)) From 4ca4d79cff5185caad27eddee7e6f8d0e58463ba Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 23 Dec 2024 13:37:22 +0100 Subject: [PATCH 128/158] feat: rework the OTP code submit count mechanism (#4251) * feat: rework the OTP code submit count mechanism Unlike what the previous comment suggested, incrementing and checking the submit count inside the database transaction is not actually optimal peformance- or security-wise. We now check atomically increment and check the submit count as the first part of the operation, and abort as early as possible if we detect brute-forcing. This prevents a situation where the check works only on certain transaction isolation levels. * chore: bump dependencies --- .github/workflows/ci.yaml | 9 ++ go.mod | 11 ++- go.sum | 24 ++--- identity/extension_verification.go | 6 +- persistence/sql/persister_code.go | 97 +++++++++++++------ persistence/sql/persister_login_code.go | 2 +- persistence/sql/persister_recovery_code.go | 2 +- .../sql/persister_registration_code.go | 2 +- .../sql/persister_verification_code.go | 2 +- selfservice/hook/web_hook.go | 2 +- selfservice/hook/web_hook_integration_test.go | 2 +- selfservice/strategy/code/test/persistence.go | 27 +++++- selfservice/strategy/oidc/provider_config.go | 2 +- selfservice/strategy/oidc/strategy.go | 4 +- test/e2e/package-lock.json | 52 +++++----- test/e2e/package.json | 2 +- x/webauthnx/aaguid/aaguid.go | 2 +- 17 files changed, 156 insertions(+), 92 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9fc74bd6397f..fd01491e2a9a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -85,6 +85,9 @@ jobs: uses: sonatype-nexus-community/nancy-github-action@v1.0.2 with: nancyVersion: v1.0.42 + - run: | + sudo apt-get update + name: apt-get update - run: npm install name: Install node deps - name: Run golangci-lint @@ -158,6 +161,9 @@ jobs: - uses: ory/ci/checkout@master with: fetch-depth: 2 + - run: | + sudo apt-get update + name: apt-get update - run: | npm ci cd test/e2e; npm ci @@ -261,6 +267,9 @@ jobs: - uses: ory/ci/checkout@master with: fetch-depth: 2 + - run: | + sudo apt-get update + name: apt-get update - run: | npm ci cd test/e2e; npm ci diff --git a/go.mod b/go.mod index a74ddb8ae8a5..deba6fbdf766 100644 --- a/go.mod +++ b/go.mod @@ -98,8 +98,8 @@ require ( go.opentelemetry.io/otel/sdk v1.32.0 go.opentelemetry.io/otel/trace v1.32.0 golang.org/x/crypto v0.31.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/net v0.31.0 + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect + golang.org/x/net v0.33.0 golang.org/x/oauth2 v0.24.0 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 @@ -119,6 +119,7 @@ require ( github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/rjeczalik/notify v0.9.3 // indirect golang.org/x/term v0.27.0 // indirect + golang.org/x/time v0.8.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect mvdan.cc/sh/v3 v3.6.0 // indirect ) @@ -312,10 +313,10 @@ require ( go.opentelemetry.io/otel/metric v1.32.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/mod v0.19.0 // indirect + golang.org/x/mod v0.22.0 // indirect golang.org/x/sys v0.28.0 // indirect - golang.org/x/tools v0.23.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + golang.org/x/tools v0.28.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/protobuf v1.35.1 diff --git a/go.sum b/go.sum index 6ced4bdca17e..5f0133b6a64f 100644 --- a/go.sum +++ b/go.sum @@ -878,8 +878,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -904,8 +904,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -954,8 +954,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1079,8 +1079,8 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1128,14 +1128,14 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/identity/extension_verification.go b/identity/extension_verification.go index 3b3f92581c37..677a49934bdd 100644 --- a/identity/extension_verification.go +++ b/identity/extension_verification.go @@ -5,12 +5,12 @@ package identity import ( "fmt" + "maps" + "slices" "strings" "sync" "time" - "golang.org/x/exp/maps" - "github.com/ory/jsonschema/v3" "github.com/ory/kratos/schema" ) @@ -60,7 +60,7 @@ func (r *SchemaExtensionVerification) Run(ctx jsonschema.ValidationContext, s sc formatString = "email" formatter, ok := jsonschema.Formats[formatString] if !ok { - supportedKeys := maps.Keys(jsonschema.Formats) + supportedKeys := slices.Collect(maps.Keys(jsonschema.Formats)) return ctx.Error("format", "format %q is not supported. Supported formats are [%s]", formatString, strings.Join(supportedKeys, ", ")) } diff --git a/persistence/sql/persister_code.go b/persistence/sql/persister_code.go index ece7dea75ec3..8b859918e389 100644 --- a/persistence/sql/persister_code.go +++ b/persistence/sql/persister_code.go @@ -12,6 +12,8 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" "github.com/pkg/errors" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/selfservice/strategy/code" "github.com/ory/x/otelx" @@ -41,7 +43,7 @@ func useOneTimeCode[P any, U interface { *P oneTimeCodeProvider }](ctx context.Context, p *Persister, flowID uuid.UUID, userProvidedCode string, flowTableName string, foreignKeyName string, opts ...codeOption, -) (_ U, err error) { +) (target U, err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.useOneTimeCode") defer otelx.End(span, &err) @@ -50,33 +52,21 @@ func useOneTimeCode[P any, U interface { opt(o) } - var target U - nid := p.NetworkID(ctx) - if err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { - //#nosec G201 -- TableName is static - if err := tx.RawQuery(fmt.Sprintf("UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ?", flowTableName), flowID, nid).Exec(); err != nil { - return err - } - - var submitCount int - // Because MySQL does not support "RETURNING" clauses, but we need the updated `submit_count` later on. - //#nosec G201 -- TableName is static - if err := sqlcon.HandleError(tx.RawQuery(fmt.Sprintf("SELECT submit_count FROM %s WHERE id = ? AND nid = ?", flowTableName), flowID, nid).First(&submitCount)); err != nil { - if errors.Is(err, sqlcon.ErrNoRows) { - // Return no error, as that would roll back the transaction - return nil - } - return err - } + // Before we do anything else, increment the submit count and check if we're + // being brute-forced. This is a separate statement/transaction to the rest + // of the operations so that it is correct for all transaction isolation + // levels. + submitCount, err := incrementOTPCodeSubmitCount(ctx, p, flowID, flowTableName) + if err != nil { + return nil, err + } + if submitCount > 5 { + return nil, errors.WithStack(code.ErrCodeSubmittedTooOften) + } - // This check prevents parallel brute force attacks by checking the submit count inside this database - // transaction. If the flow has been submitted more than 5 times, the transaction is aborted (regardless of - // whether the code was correct or not) and we thus give no indication whether the supplied code was correct or - // not. For more explanation see [this comment](https://github.com/ory/kratos/pull/2645#discussion_r984732899). - if submitCount > 5 { - return errors.WithStack(code.ErrCodeSubmittedTooOften) - } + nid := p.NetworkID(ctx) + if err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { var codes []U codesQuery := tx.Where(fmt.Sprintf("nid = ? AND %s = ?", foreignKeyName), nid, flowID) if o.IdentityID != nil { @@ -85,10 +75,8 @@ func useOneTimeCode[P any, U interface { if err := sqlcon.HandleError(codesQuery.All(&codes)); err != nil { if errors.Is(err, sqlcon.ErrNoRows) { - // Return no error, as that would roll back the transaction and reset the submit count. - return nil + return errors.WithStack(code.ErrCodeNotFound) } - return err } @@ -107,7 +95,7 @@ func useOneTimeCode[P any, U interface { } if target.Validate() != nil { - // Return no error, as that would roll back the transaction + // Return no error, as that would roll back the transaction. We re-validate the code after the transaction. return nil } @@ -123,3 +111,52 @@ func useOneTimeCode[P any, U interface { return target, nil } + +func incrementOTPCodeSubmitCount(ctx context.Context, p *Persister, flowID uuid.UUID, flowTableName string) (submitCount int, err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.incrementOTPCodeSubmitCount", + trace.WithAttributes(attribute.Stringer("flow_id", flowID), attribute.String("flow_table_name", flowTableName))) + defer otelx.End(span, &err) + defer func() { + span.SetAttributes(attribute.Int("submit_count", submitCount)) + }() + + nid := p.NetworkID(ctx) + + // The branch below is a marginal performance optimization for databases + // supporting RETURNING (one query instead of two). There is no real + // security difference here, but there is an observable difference in + // behavior. + // + // Databases supporting RETURNING will perform the increment+select + // atomically. That means that always exactly 5 attempts will be allowed for + // each flow, no matter how many concurrent attempts are made. + // + // Databases without support for RETURNING (MySQL) will perform the UPDATE + // and SELECT in two queries, which are not atomic. The effect is that there + // will still never be more than 5 attempts for each flow, but there may be + // fewer before we reject. Under normal operation, this is never a problem + // because a human will never submit their code as quickly as would be + // required to trigger this race condition. + // + // In a very strict sense of the word, the MySQL implementation is even more + // secure than the RETURNING implementation. But we're ok either way :) + if p.c.Dialect.Name() == "mysql" { + //#nosec G201 -- TableName is static + qUpdate := fmt.Sprintf("UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ?", flowTableName) + if err := p.GetConnection(ctx).RawQuery(qUpdate, flowID, nid).Exec(); err != nil { + return 0, sqlcon.HandleError(err) + } + //#nosec G201 -- TableName is static + qSelect := fmt.Sprintf("SELECT submit_count FROM %s WHERE id = ? AND nid = ?", flowTableName) + err = sqlcon.HandleError(p.GetConnection(ctx).RawQuery(qSelect, flowID, nid).First(&submitCount)) + } else { + //#nosec G201 -- TableName is static + q := fmt.Sprintf("UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ? RETURNING submit_count", flowTableName) + err = sqlcon.HandleError(p.Connection(ctx).RawQuery(q, flowID, nid).First(&submitCount)) + } + if errors.Is(err, sqlcon.ErrNoRows) { + return 0, errors.WithStack(code.ErrCodeNotFound) + } + + return submitCount, err +} diff --git a/persistence/sql/persister_login_code.go b/persistence/sql/persister_login_code.go index 808e65b9d2a4..deee50f02f59 100644 --- a/persistence/sql/persister_login_code.go +++ b/persistence/sql/persister_login_code.go @@ -43,7 +43,7 @@ func (p *Persister) UseLoginCode(ctx context.Context, flowID uuid.UUID, identity ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseLoginCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.LoginCode, *code.LoginCode](ctx, p, flowID, userProvidedCode, new(login.Flow).TableName(ctx), "selfservice_login_flow_id", withCheckIdentityID(identityID)) + codeRow, err := useOneTimeCode[code.LoginCode](ctx, p, flowID, userProvidedCode, new(login.Flow).TableName(ctx), "selfservice_login_flow_id", withCheckIdentityID(identityID)) if err != nil { return nil, err } diff --git a/persistence/sql/persister_recovery_code.go b/persistence/sql/persister_recovery_code.go index 9dc4dd26bb83..7b27aff12e84 100644 --- a/persistence/sql/persister_recovery_code.go +++ b/persistence/sql/persister_recovery_code.go @@ -58,7 +58,7 @@ func (p *Persister) UseRecoveryCode(ctx context.Context, flowID uuid.UUID, userP ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseRecoveryCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.RecoveryCode, *code.RecoveryCode](ctx, p, flowID, userProvidedCode, new(recovery.Flow).TableName(ctx), "selfservice_recovery_flow_id") + codeRow, err := useOneTimeCode[code.RecoveryCode](ctx, p, flowID, userProvidedCode, new(recovery.Flow).TableName(ctx), "selfservice_recovery_flow_id") if err != nil { return nil, err } diff --git a/persistence/sql/persister_registration_code.go b/persistence/sql/persister_registration_code.go index 095cb45156ba..3ef33048a60e 100644 --- a/persistence/sql/persister_registration_code.go +++ b/persistence/sql/persister_registration_code.go @@ -44,7 +44,7 @@ func (p *Persister) UseRegistrationCode(ctx context.Context, flowID uuid.UUID, u ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseRegistrationCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.RegistrationCode, *code.RegistrationCode](ctx, p, flowID, userProvidedCode, new(registration.Flow).TableName(ctx), "selfservice_registration_flow_id") + codeRow, err := useOneTimeCode[code.RegistrationCode](ctx, p, flowID, userProvidedCode, new(registration.Flow).TableName(ctx), "selfservice_registration_flow_id") if err != nil { return nil, err } diff --git a/persistence/sql/persister_verification_code.go b/persistence/sql/persister_verification_code.go index 3c3fc6d9bed5..1186712cdac9 100644 --- a/persistence/sql/persister_verification_code.go +++ b/persistence/sql/persister_verification_code.go @@ -55,7 +55,7 @@ func (p *Persister) UseVerificationCode(ctx context.Context, flowID uuid.UUID, u ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UseVerificationCode") defer otelx.End(span, &err) - codeRow, err := useOneTimeCode[code.VerificationCode, *code.VerificationCode](ctx, p, flowID, userProvidedCode, new(verification.Flow).TableName(ctx), "selfservice_verification_flow_id") + codeRow, err := useOneTimeCode[code.VerificationCode](ctx, p, flowID, userProvidedCode, new(verification.Flow).TableName(ctx), "selfservice_verification_flow_id") if err != nil { return nil, err } diff --git a/selfservice/hook/web_hook.go b/selfservice/hook/web_hook.go index 6be52d2ca8d0..d756338b13e6 100644 --- a/selfservice/hook/web_hook.go +++ b/selfservice/hook/web_hook.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "net/http" "net/textproto" "time" @@ -21,7 +22,6 @@ import ( "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.11.0" "go.opentelemetry.io/otel/trace" - "golang.org/x/exp/maps" grpccodes "google.golang.org/grpc/codes" "github.com/ory/herodot" diff --git a/selfservice/hook/web_hook_integration_test.go b/selfservice/hook/web_hook_integration_test.go index 0dff20cbb5d0..700835309345 100644 --- a/selfservice/hook/web_hook_integration_test.go +++ b/selfservice/hook/web_hook_integration_test.go @@ -14,6 +14,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "slices" "strconv" "sync" "testing" @@ -27,7 +28,6 @@ import ( "go.opentelemetry.io/otel/attribute" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" - "golang.org/x/exp/slices" "github.com/ory/kratos/driver/config" "github.com/ory/kratos/identity" diff --git a/selfservice/strategy/code/test/persistence.go b/selfservice/strategy/code/test/persistence.go index d7600e9fbd9a..975e63eb6ecc 100644 --- a/selfservice/strategy/code/test/persistence.go +++ b/selfservice/strategy/code/test/persistence.go @@ -5,6 +5,9 @@ package code import ( "context" + "errors" + "sync" + "sync/atomic" "testing" "time" @@ -113,13 +116,27 @@ func TestPersister(ctx context.Context, p interface { _, err := p.CreateRecoveryCode(ctx, dto) require.NoError(t, err) - for i := 1; i <= 5; i++ { - _, err = p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") - require.Error(t, err) + var tooOften, wrongCode int32 + var wg sync.WaitGroup + for range 50 { + wg.Add(1) + go func() { + defer wg.Done() + _, err := p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") + if !assert.Error(t, err) { + return + } + if errors.Is(err, code.ErrCodeSubmittedTooOften) { + atomic.AddInt32(&tooOften, 1) + } else { + atomic.AddInt32(&wrongCode, 1) + } + }() } + wg.Wait() - _, err = p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") - require.ErrorIs(t, err, code.ErrCodeSubmittedTooOften) + require.EqualValues(t, 50, wrongCode+tooOften, "all 50 attempts made") + require.LessOrEqual(t, wrongCode, int32(5), "max. 5 attempts have gone past the duplication check") // Submit again, just to be sure _, err = p.UseRecoveryCode(ctx, f.ID, "i-do-not-exist") diff --git a/selfservice/strategy/oidc/provider_config.go b/selfservice/strategy/oidc/provider_config.go index e866aea17f84..92b16fdf5f42 100644 --- a/selfservice/strategy/oidc/provider_config.go +++ b/selfservice/strategy/oidc/provider_config.go @@ -5,11 +5,11 @@ package oidc import ( "encoding/json" + "maps" "net/url" "strings" "github.com/pkg/errors" - "golang.org/x/exp/maps" "github.com/ory/herodot" "github.com/ory/x/urlx" diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index b49883757f5d..06af7be1ce58 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "encoding/json" + "maps" "net/http" "net/url" "path/filepath" @@ -20,7 +21,6 @@ import ( "github.com/tidwall/gjson" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "golang.org/x/exp/maps" "golang.org/x/oauth2" "github.com/ory/herodot" @@ -461,7 +461,7 @@ func (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request, ps htt return } - span.SetAttributes(attribute.StringSlice("claims", maps.Keys(claims.RawClaims))) + span.SetAttributes(attribute.StringSlice("claims", slices.Collect(maps.Keys(claims.RawClaims)))) switch a := req.(type) { case *login.Flow: diff --git a/test/e2e/package-lock.json b/test/e2e/package-lock.json index 7dd8c6416648..ec5331d9daef 100644 --- a/test/e2e/package-lock.json +++ b/test/e2e/package-lock.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@ory/kratos-client": "1.2.0", - "@playwright/test": "1.44.1", + "@playwright/test": "1.48.0", "@types/async-retry": "1.4.5", "@types/node": "16.9.6", "@types/yamljs": "0.2.31", @@ -195,19 +195,19 @@ } }, "node_modules/@playwright/test": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", - "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz", + "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.44.1" + "playwright": "1.48.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@sideway/address": { @@ -2455,35 +2455,35 @@ } }, "node_modules/playwright": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", - "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz", + "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.44.1" + "playwright-core": "1.48.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", - "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz", + "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==", "dev": true, "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/prettier": { @@ -3346,12 +3346,12 @@ } }, "@playwright/test": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", - "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz", + "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==", "dev": true, "requires": { - "playwright": "1.44.1" + "playwright": "1.48.0" } }, "@sideway/address": { @@ -5093,19 +5093,19 @@ "dev": true }, "playwright": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", - "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz", + "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.44.1" + "playwright-core": "1.48.0" } }, "playwright-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", - "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz", + "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==", "dev": true }, "prettier": { diff --git a/test/e2e/package.json b/test/e2e/package.json index a33ac330bf00..1f4c5cf2ea60 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@ory/kratos-client": "1.2.0", - "@playwright/test": "1.44.1", + "@playwright/test": "1.48.0", "@types/async-retry": "1.4.5", "@types/node": "16.9.6", "@types/yamljs": "0.2.31", diff --git a/x/webauthnx/aaguid/aaguid.go b/x/webauthnx/aaguid/aaguid.go index 376303848bc9..b3875fdf1117 100644 --- a/x/webauthnx/aaguid/aaguid.go +++ b/x/webauthnx/aaguid/aaguid.go @@ -8,9 +8,9 @@ package aaguid import ( _ "embed" "encoding/json" + "maps" "github.com/gofrs/uuid" - "golang.org/x/exp/maps" ) var ( From 85a7071d20d0f072316c74bee82c76ee690276f8 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 26 Nov 2024 13:56:07 +0100 Subject: [PATCH 129/158] feat: improved tracing for courier --- courier/courier_dispatcher.go | 16 +++++++++++++++- courier/smtp_channel.go | 36 +++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/courier/courier_dispatcher.go b/courier/courier_dispatcher.go index 75745ef9a954..8d7a5773c5ab 100644 --- a/courier/courier_dispatcher.go +++ b/courier/courier_dispatcher.go @@ -7,6 +7,10 @@ import ( "context" "github.com/pkg/errors" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + "github.com/ory/x/otelx" ) func (c *courier) channels(ctx context.Context, id string) (Channel, error) { @@ -36,7 +40,16 @@ func (c *courier) channels(ctx context.Context, id string) (Channel, error) { return nil, errors.Errorf("no courier channels configured") } -func (c *courier) DispatchMessage(ctx context.Context, msg Message) error { +func (c *courier) DispatchMessage(ctx context.Context, msg Message) (err error) { + ctx, span := c.deps.Tracer(ctx).Tracer().Start(ctx, "courier.DispatchMessage", trace.WithAttributes( + attribute.Stringer("message.id", msg.ID), + attribute.Stringer("message.nid", msg.NID), + attribute.Stringer("message.type", msg.Type), + attribute.String("message.template_type", string(msg.TemplateType)), + attribute.Int("message.send_count", msg.SendCount), + )) + defer otelx.End(span, &err) + logger := c.deps.Logger(). WithField("message_id", msg.ID). WithField("message_nid", msg.NID). @@ -56,6 +69,7 @@ func (c *courier) DispatchMessage(ctx context.Context, msg Message) error { return err } + span.SetAttributes(attribute.String("channel.id", channel.ID())) logger = logger. WithField("channel", channel.ID()) diff --git a/courier/smtp_channel.go b/courier/smtp_channel.go index 15a685bcd7ad..3527b2b7d680 100644 --- a/courier/smtp_channel.go +++ b/courier/smtp_channel.go @@ -5,15 +5,19 @@ package courier import ( "context" - "fmt" + "net" "net/textproto" + "strconv" "github.com/pkg/errors" + semconv "go.opentelemetry.io/otel/semconv/v1.20.0" + "go.opentelemetry.io/otel/trace" "github.com/ory/herodot" "github.com/ory/kratos/courier/template" "github.com/ory/kratos/driver/config" "github.com/ory/mail/v3" + "github.com/ory/x/otelx" ) type ( @@ -47,7 +51,10 @@ func (c *SMTPChannel) ID() string { return "email" } -func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) error { +func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) (err error) { + ctx, span := c.d.Tracer(ctx).Tracer().Start(ctx, "courier.SMTPChannel.Dispatch") + defer otelx.End(span, &err) + if c.smtpClient.Host == "" { return errors.WithStack(herodot.ErrInternalServerError.WithErrorf("Courier tried to deliver an email but %s is not set!", config.ViperKeyCourierSMTPURL)) } @@ -87,7 +94,7 @@ func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) error { gm.SetBody("text/plain", msg.Body) logger := c.d.Logger(). - WithField("smtp_server", fmt.Sprintf("%s:%d", c.smtpClient.Host, c.smtpClient.Port)). + WithField("smtp_server", net.JoinHostPort(c.smtpClient.Host, strconv.Itoa(c.smtpClient.Port))). WithField("smtp_ssl_enabled", c.smtpClient.SSL). WithField("message_from", cfg.FromAddress). WithField("message_id", msg.ID). @@ -107,7 +114,28 @@ func (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) error { gm.AddAlternative("text/html", htmlBody) } - if err := errors.WithStack(c.smtpClient.DialAndSend(ctx, gm)); err != nil { + dialCtx, dialSpan := c.d.Tracer(ctx).Tracer().Start(ctx, "courier.SMTPChannel.Dispatch.Dial", trace.WithAttributes( + semconv.NetPeerName(c.smtpClient.Host), + semconv.NetPeerPort(c.smtpClient.Port), + semconv.NetProtocolName("smtp"), + )) + snd, err := c.smtpClient.Dial(dialCtx) + otelx.End(dialSpan, &err) + + if err != nil { + logger. + WithError(err). + Error("Unable to dial SMTP connection.") + return errors.WithStack(herodot.ErrInternalServerError. + WithError(err.Error()).WithReason("failed to send email via smtp")) + } + defer snd.Close() + + sendCtx, sendSpan := c.d.Tracer(ctx).Tracer().Start(ctx, "courier.SMTPChannel.Dispatch.Send") + err = mail.Send(sendCtx, snd, gm) + otelx.End(sendSpan, &err) + + if err != nil { logger. WithError(err). Error("Unable to send email using SMTP connection.") From 5c3310d9a3f492dc14ea36f7ed8c41d1a8d0e61e Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:13:24 +0000 Subject: [PATCH 130/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05ec8cd20fa1..68bc9d1f3aee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-20)](#2024-12-20) +- [ (2024-12-23)](#2024-12-23) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-20) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-23) ## Breaking Changes @@ -544,6 +544,8 @@ https://github.com/ory-corp/cloud/issues/7176 - Improve secondary indices for self service tables ([#4179](https://github.com/ory/kratos/issues/4179)) ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253)) +- Improved tracing for courier + ([85a7071](https://github.com/ory/kratos/commit/85a7071d20d0f072316c74bee82c76ee690276f8)) - Jackson provider ([#4242](https://github.com/ory/kratos/issues/4242)) ([f18d1b2](https://github.com/ory/kratos/commit/f18d1b24539f7d8dcf9c27986af861d0f8cb9683)): @@ -582,6 +584,23 @@ https://github.com/ory-corp/cloud/issues/7176 - Remove more unused indices ([#4186](https://github.com/ory/kratos/issues/4186)) ([b294804](https://github.com/ory/kratos/commit/b2948044de4eee1841110162fe874055182bd2d2)) +- Rework the OTP code submit count mechanism + ([#4251](https://github.com/ory/kratos/issues/4251)) + ([4ca4d79](https://github.com/ory/kratos/commit/4ca4d79cff5185caad27eddee7e6f8d0e58463ba)): + + - feat: rework the OTP code submit count mechanism + + Unlike what the previous comment suggested, incrementing and checking the + submit count inside the database transaction is not actually optimal + peformance- or security-wise. + + We now check atomically increment and check the submit count as the first part + of the operation, and abort as early as possible if we detect brute-forcing. + This prevents a situation where the check works only on certain transaction + isolation levels. + + - chore: bump dependencies + - Support android webauthn origins ([#4155](https://github.com/ory/kratos/issues/4155)) ([a82d288](https://github.com/ory/kratos/commit/a82d288014411ae4eb82c718bfe825ca55b4fab0)): From c4b3dd6a35953dcba9ce2d74bce190d5c42a3793 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Fri, 27 Dec 2024 13:02:55 +0000 Subject: [PATCH 131/158] chore: update repository templates to https://github.com/ory/meta/commit/000f213efcd4e98ac3462086c47de58005c4b697 --- .github/ISSUE_TEMPLATE/BUG-REPORT.yml | 66 ++++++++++------------ .github/ISSUE_TEMPLATE/DESIGN-DOC.yml | 48 +++++++--------- .github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml | 48 +++++++--------- .github/ISSUE_TEMPLATE/config.yml | 6 +- .github/workflows/cve-scan.yaml | 45 ++++++++++++--- 5 files changed, 111 insertions(+), 102 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml index b3db2a2d3de2..4048324eb8a7 100644 --- a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -1,48 +1,43 @@ # AUTO-GENERATED, DO NOT EDIT! # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/BUG-REPORT.yml -description: 'Create a bug report' +description: "Create a bug report" labels: - bug -name: 'Bug Report' +name: "Bug Report" body: - attributes: value: "Thank you for taking the time to fill out this bug report!\n" type: markdown - attributes: - label: 'Preflight checklist' + label: "Preflight checklist" options: - - label: - 'I could not find a solution in the existing issues, docs, nor - discussions.' + - label: "I could not find a solution in the existing issues, docs, nor + discussions." required: true - - label: - "I agree to follow this project's [Code of + - label: "I agree to follow this project's [Code of Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)." required: true - - label: - "I have read and am following this repository's [Contribution + - label: "I have read and am following this repository's [Contribution Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - - label: - 'I have joined the [Ory Community Slack](https://slack.ory.sh).' - - label: - 'I am signed up to the [Ory Security Patch - Newsletter](https://www.ory.sh/l/sign-up-newsletter).' + - label: "I have joined the [Ory Community Slack](https://slack.ory.sh)." + - label: "I am signed up to the [Ory Security Patch + Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes - attributes: description: - 'Enter the slug or API URL of the affected Ory Network project. Leave - empty when you are self-hosting.' - label: 'Ory Network Project' - placeholder: 'https://.projects.oryapis.com' + "Enter the slug or API URL of the affected Ory Network project. Leave + empty when you are self-hosting." + label: "Ory Network Project" + placeholder: "https://.projects.oryapis.com" id: ory-network-project type: input - attributes: - description: 'A clear and concise description of what the bug is.' - label: 'Describe the bug' - placeholder: 'Tell us what you see!' + description: "A clear and concise description of what the bug is." + label: "Describe the bug" + placeholder: "Tell us what you see!" id: describe-bug type: textarea validations: @@ -56,17 +51,16 @@ body: 1. Run `docker run ....` 2. Make API Request to with `curl ...` 3. Request fails with response: `{"some": "error"}` - label: 'Reproducing the bug' + label: "Reproducing the bug" id: reproduce-bug type: textarea validations: required: true - attributes: - description: - 'Please copy and paste any relevant log output. This will be + description: "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. Please - redact any sensitive information' - label: 'Relevant log output' + redact any sensitive information" + label: "Relevant log output" render: shell placeholder: | log=error .... @@ -74,10 +68,10 @@ body: type: textarea - attributes: description: - 'Please copy and paste any relevant configuration. This will be + "Please copy and paste any relevant configuration. This will be automatically formatted into code, so no need for backticks. Please - redact any sensitive information!' - label: 'Relevant configuration' + redact any sensitive information!" + label: "Relevant configuration" render: yml placeholder: | server: @@ -86,14 +80,14 @@ body: id: config type: textarea - attributes: - description: 'What version of our software are you running?' + description: "What version of our software are you running?" label: Version id: version type: input validations: required: true - attributes: - label: 'On which operating system are you observing this issue?' + label: "On which operating system are you observing this issue?" options: - Ory Network - macOS @@ -104,19 +98,19 @@ body: id: operating-system type: dropdown - attributes: - label: 'In which environment are you deploying?' + label: "In which environment are you deploying?" options: - Ory Network - Docker - - 'Docker Compose' - - 'Kubernetes with Helm' + - "Docker Compose" + - "Kubernetes with Helm" - Kubernetes - Binary - Other id: deployment type: dropdown - attributes: - description: 'Add any other context about the problem here.' + description: "Add any other context about the problem here." label: Additional Context id: additional type: textarea diff --git a/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml b/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml index a6a86f36dcc7..b5741119698b 100644 --- a/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml +++ b/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml @@ -1,11 +1,10 @@ # AUTO-GENERATED, DO NOT EDIT! # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml -description: - 'A design document is needed for non-trivial changes to the code base.' +description: "A design document is needed for non-trivial changes to the code base." labels: - rfc -name: 'Design Document' +name: "Design Document" body: - attributes: value: | @@ -21,39 +20,34 @@ body: after code reviews, and your pull requests will be merged faster. type: markdown - attributes: - label: 'Preflight checklist' + label: "Preflight checklist" options: - - label: - 'I could not find a solution in the existing issues, docs, nor - discussions.' + - label: "I could not find a solution in the existing issues, docs, nor + discussions." required: true - - label: - "I agree to follow this project's [Code of + - label: "I agree to follow this project's [Code of Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)." required: true - - label: - "I have read and am following this repository's [Contribution + - label: "I have read and am following this repository's [Contribution Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - - label: - 'I have joined the [Ory Community Slack](https://slack.ory.sh).' - - label: - 'I am signed up to the [Ory Security Patch - Newsletter](https://www.ory.sh/l/sign-up-newsletter).' + - label: "I have joined the [Ory Community Slack](https://slack.ory.sh)." + - label: "I am signed up to the [Ory Security Patch + Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes - attributes: description: - 'Enter the slug or API URL of the affected Ory Network project. Leave - empty when you are self-hosting.' - label: 'Ory Network Project' - placeholder: 'https://.projects.oryapis.com' + "Enter the slug or API URL of the affected Ory Network project. Leave + empty when you are self-hosting." + label: "Ory Network Project" + placeholder: "https://.projects.oryapis.com" id: ory-network-project type: input - attributes: description: | This section gives the reader a very rough overview of the landscape in which the new system is being built and what is actually being built. This isn’t a requirements doc. Keep it succinct! The goal is that readers are brought up to speed but some previous knowledge can be assumed and detailed info can be linked to. This section should be entirely focused on objective background facts. - label: 'Context and scope' + label: "Context and scope" id: scope type: textarea validations: @@ -62,7 +56,7 @@ body: - attributes: description: | A short list of bullet points of what the goals of the system are, and, sometimes more importantly, what non-goals are. Note, that non-goals aren’t negated goals like “The system shouldn’t crash”, but rather things that could reasonably be goals, but are explicitly chosen not to be goals. A good example would be “ACID compliance”; when designing a database, you’d certainly want to know whether that is a goal or non-goal. And if it is a non-goal you might still select a solution that provides it, if it doesn’t introduce trade-offs that prevent achieving the goals. - label: 'Goals and non-goals' + label: "Goals and non-goals" id: goals type: textarea validations: @@ -74,7 +68,7 @@ body: The design doc is the place to write down the trade-offs you made in designing your software. Focus on those trade-offs to produce a useful document with long-term value. That is, given the context (facts), goals and non-goals (requirements), the design doc is the place to suggest solutions and show why a particular solution best satisfies those goals. The point of writing a document over a more formal medium is to provide the flexibility to express the problem at hand in an appropriate manner. Because of this, there is no explicit guidance on how to actually describe the design. - label: 'The design' + label: "The design" id: design type: textarea validations: @@ -83,21 +77,21 @@ body: - attributes: description: | If the system under design exposes an API, then sketching out that API is usually a good idea. In most cases, however, one should withstand the temptation to copy-paste formal interface or data definitions into the doc as these are often verbose, contain unnecessary detail and quickly get out of date. Instead, focus on the parts that are relevant to the design and its trade-offs. - label: 'APIs' + label: "APIs" id: apis type: textarea - attributes: description: | Systems that store data should likely discuss how and in what rough form this happens. Similar to the advice on APIs, and for the same reasons, copy-pasting complete schema definitions should be avoided. Instead, focus on the parts that are relevant to the design and its trade-offs. - label: 'Data storage' + label: "Data storage" id: persistence type: textarea - attributes: description: | Design docs should rarely contain code, or pseudo-code except in situations where novel algorithms are described. As appropriate, link to prototypes that show the feasibility of the design. - label: 'Code and pseudo-code' + label: "Code and pseudo-code" id: pseudocode type: textarea @@ -110,7 +104,7 @@ body: On the other end are systems where the possible solutions are very well defined, but it isn't at all obvious how they could even be combined to achieve the goals. This may be a legacy system that is difficult to change and wasn't designed to do what you want it to do or a library design that needs to operate within the constraints of the host programming language. In this situation, you may be able to enumerate all the things you can do relatively easily, but you need to creatively put those things together to achieve the goals. There may be multiple solutions, and none of them are great, and hence such a document should focus on selecting the best way given all identified trade-offs. - label: 'Degree of constraint' + label: "Degree of constraint" id: constrait type: textarea diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml index 7c023c2f48b3..7152fbdde4cf 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml @@ -1,11 +1,10 @@ # AUTO-GENERATED, DO NOT EDIT! # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml -description: - 'Suggest an idea for this project without a plan for implementation' +description: "Suggest an idea for this project without a plan for implementation" labels: - feat -name: 'Feature Request' +name: "Feature Request" body: - attributes: value: | @@ -14,39 +13,33 @@ body: If you already have a plan to implement a feature or a change, please create a [design document](https://github.com/aeneasr/gh-template-test/issues/new?assignees=&labels=rfc&template=DESIGN-DOC.yml) instead if the change is non-trivial! type: markdown - attributes: - label: 'Preflight checklist' + label: "Preflight checklist" options: - - label: - 'I could not find a solution in the existing issues, docs, nor - discussions.' + - label: "I could not find a solution in the existing issues, docs, nor + discussions." required: true - - label: - "I agree to follow this project's [Code of + - label: "I agree to follow this project's [Code of Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)." required: true - - label: - "I have read and am following this repository's [Contribution + - label: "I have read and am following this repository's [Contribution Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - - label: - 'I have joined the [Ory Community Slack](https://slack.ory.sh).' - - label: - 'I am signed up to the [Ory Security Patch - Newsletter](https://www.ory.sh/l/sign-up-newsletter).' + - label: "I have joined the [Ory Community Slack](https://slack.ory.sh)." + - label: "I am signed up to the [Ory Security Patch + Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes - attributes: description: - 'Enter the slug or API URL of the affected Ory Network project. Leave - empty when you are self-hosting.' - label: 'Ory Network Project' - placeholder: 'https://.projects.oryapis.com' + "Enter the slug or API URL of the affected Ory Network project. Leave + empty when you are self-hosting." + label: "Ory Network Project" + placeholder: "https://.projects.oryapis.com" id: ory-network-project type: input - attributes: - description: - 'Is your feature request related to a problem? Please describe.' - label: 'Describe your problem' + description: "Is your feature request related to a problem? Please describe." + label: "Describe your problem" placeholder: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]" @@ -59,28 +52,27 @@ body: Describe the solution you'd like placeholder: | A clear and concise description of what you want to happen. - label: 'Describe your ideal solution' + label: "Describe your ideal solution" id: solution type: textarea validations: required: true - attributes: description: "Describe alternatives you've considered" - label: 'Workarounds or alternatives' + label: "Workarounds or alternatives" id: alternatives type: textarea validations: required: true - attributes: - description: 'What version of our software are you running?' + description: "What version of our software are you running?" label: Version id: version type: input validations: required: true - attributes: - description: - 'Add any other context or screenshots about the feature request here.' + description: "Add any other context or screenshots about the feature request here." label: Additional Context id: additional type: textarea diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index abb0b696c9d9..ef4c482ae405 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -5,10 +5,8 @@ blank_issues_enabled: false contact_links: - name: Ory Kratos Forum url: https://github.com/ory/kratos/discussions - about: - Please ask and answer questions here, show your implementations and + about: Please ask and answer questions here, show your implementations and discuss ideas. - name: Ory Chat url: https://www.ory.sh/chat - about: - Hang out with other Ory community members to ask and answer questions. + about: Hang out with other Ory community members to ask and answer questions. diff --git a/.github/workflows/cve-scan.yaml b/.github/workflows/cve-scan.yaml index 28e88e24fd28..b8aa0197182a 100644 --- a/.github/workflows/cve-scan.yaml +++ b/.github/workflows/cve-scan.yaml @@ -1,3 +1,6 @@ +# AUTO-GENERATED, DO NOT EDIT! +# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/server/.github/workflows/cve-scan.yaml + name: Docker Image Scanners on: workflow_dispatch: @@ -24,7 +27,28 @@ jobs: id: vars shell: bash run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> "${GITHUB_ENV}" + # Store values in local variables + SHA_SHORT=$(git rev-parse --short HEAD) + REPO_NAME=${{ github.event.repository.name }} + + # Append -sqlite to SHA_SHORT if repo is hydra + if [ "${REPO_NAME}" = "hydra" ]; then + echo "Repo is hydra, appending -sqlite to SHA_SHORT" + IMAGE_NAME="oryd/${REPO_NAME}:${SHA_SHORT}-sqlite" + else + echo "Repo is not hydra, using default IMAGE_NAME" + IMAGE_NAME="oryd/${REPO_NAME}:${SHA_SHORT}" + fi + + # Output values for debugging + echo "Values to be set:" + echo "SHA_SHORT: ${SHA_SHORT}" + echo "REPO_NAME: ${REPO_NAME}" + echo "IMAGE_NAME: ${IMAGE_NAME}" + + # Set GitHub Environment variables + echo "SHA_SHORT=${SHA_SHORT}" >> "${GITHUB_ENV}" + echo "IMAGE_NAME=${IMAGE_NAME}" >> "${GITHUB_ENV}" - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx @@ -34,7 +58,6 @@ jobs: run: | IMAGE_TAG="${{ env.SHA_SHORT }}" make docker - # Add GitHub authentication for Trivy - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: @@ -42,7 +65,6 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # Configure Trivy - name: Configure Trivy run: | mkdir -p $HOME/.cache/trivy @@ -53,7 +75,7 @@ jobs: uses: anchore/scan-action@v5 id: grype-scan with: - image: oryd/kratos:${{ env.SHA_SHORT }} + image: ${{ env.IMAGE_NAME }} fail-build: true severity-cutoff: high add-cpes-if-none: true @@ -69,12 +91,20 @@ jobs: uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.grype-scan.outputs.sarif }} - + - name: Kubescape scanner + uses: kubescape/github-action@main + id: kubescape + with: + image: ${{ env.IMAGE_NAME }} + verbose: true + format: pretty-printer + # can't whitelist CVE yet: https://github.com/kubescape/kubescape/pull/1568 + severityThreshold: critical - name: Trivy Scanner uses: aquasecurity/trivy-action@master if: ${{ always() }} with: - image-ref: oryd/kratos:${{ env.SHA_SHORT }} + image-ref: ${{ env.IMAGE_NAME }} format: "table" exit-code: "42" ignore-unfixed: true @@ -84,12 +114,13 @@ jobs: env: TRIVY_SKIP_JAVA_DB_UPDATE: "true" TRIVY_DISABLE_VEX_NOTICE: "true" + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db - name: Dockle Linter uses: erzz/dockle-action@v1 if: ${{ always() }} with: - image: oryd/kratos:${{ env.SHA_SHORT }} + image: ${{ env.IMAGE_NAME }} exit-code: 42 failure-threshold: high - name: Hadolint From 74ae377f5d67d0c21af30e8ecb8beece22e50a32 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 27 Dec 2024 14:01:58 +0000 Subject: [PATCH 132/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68bc9d1f3aee..c7f68260ed27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-23)](#2024-12-23) +- [ (2024-12-27)](#2024-12-27) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-23) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-27) ## Breaking Changes From fee7cae56857983de8edfd001f5924780fc8864a Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:16:20 +0100 Subject: [PATCH 133/158] chore: upgrade playwright (#4255) --- test/e2e/package-lock.json | 62 ++++++++++++++++++-------------------- test/e2e/package.json | 2 +- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/test/e2e/package-lock.json b/test/e2e/package-lock.json index ec5331d9daef..631f255818ff 100644 --- a/test/e2e/package-lock.json +++ b/test/e2e/package-lock.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@ory/kratos-client": "1.2.0", - "@playwright/test": "1.48.0", + "@playwright/test": "1.49.1", "@types/async-retry": "1.4.5", "@types/node": "16.9.6", "@types/yamljs": "0.2.31", @@ -195,13 +195,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz", - "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "playwright": "1.48.0" + "playwright": "1.49.1" }, "bin": { "playwright": "cli.js" @@ -967,9 +966,9 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -1471,7 +1470,6 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -2455,13 +2453,12 @@ } }, "node_modules/playwright": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz", - "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0" + "playwright-core": "1.49.1" }, "bin": { "playwright": "cli.js" @@ -2474,11 +2471,10 @@ } }, "node_modules/playwright-core": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz", - "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", "dev": true, - "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, @@ -3346,12 +3342,12 @@ } }, "@playwright/test": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz", - "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", "dev": true, "requires": { - "playwright": "1.48.0" + "playwright": "1.49.1" } }, "@sideway/address": { @@ -3961,9 +3957,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -5093,19 +5089,19 @@ "dev": true }, "playwright": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz", - "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.48.0" + "playwright-core": "1.49.1" } }, "playwright-core": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz", - "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", "dev": true }, "prettier": { diff --git a/test/e2e/package.json b/test/e2e/package.json index 1f4c5cf2ea60..001f4d5a4ab3 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@ory/kratos-client": "1.2.0", - "@playwright/test": "1.48.0", + "@playwright/test": "1.49.1", "@types/async-retry": "1.4.5", "@types/node": "16.9.6", "@types/yamljs": "0.2.31", From 73d287e5b9a050beb01be3e06903c66c3d48e555 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:21:36 +0100 Subject: [PATCH 134/158] chore: update codeowners (#4256) --- .github/CODEOWNERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d142185008ff..ef90d000d7f4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1 @@ -* @aeneasr @zepatrik @hperl - -/docs/ @ory/documenters +* @aeneasr @ory/product-development From 2a4b8c77f16caa8e5dbc13aede1558e3d7057cf8 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:19:29 +0000 Subject: [PATCH 135/158] chore: update repository templates to https://github.com/ory/meta/commit/7ba40649aea8bbadc478c01f97ff6231fb492fb8 --- .github/workflows/licenses.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 9d1589506da2..e1d172f2f1f1 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -1,3 +1,6 @@ +# AUTO-GENERATED, DO NOT EDIT! +# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/licenses.yml + name: Licenses on: @@ -11,11 +14,14 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v2 - with: - go-version: "1.23" - - uses: actions/setup-node@v2 + - name: Install script + uses: ory/ci/licenses/setup@master with: - node-version: "18" - - run: make licenses + token: ${{ secrets.ORY_BOT_PAT || secrets.GITHUB_TOKEN }} + - name: Check licenses + uses: ory/ci/licenses/check@master + - name: Write licenses + uses: ory/ci/licenses/write@master + if: + ${{ github.ref == 'refs/heads/main' || github.ref == + 'refs/heads/master' }} From 241111b21f5d96b26ff8bc8106dc8a527c68063b Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:20:45 +0100 Subject: [PATCH 136/158] fix(sdk): add missing captcha group (#4254) --- internal/client-go/model_ui_node.go | 2 +- internal/httpclient/model_ui_node.go | 2 +- spec/api.json | 7 ++++--- spec/swagger.json | 7 ++++--- ui/node/node.go | 1 + 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/internal/client-go/model_ui_node.go b/internal/client-go/model_ui_node.go index 3582d9e85f67..84b728514114 100644 --- a/internal/client-go/model_ui_node.go +++ b/internal/client-go/model_ui_node.go @@ -18,7 +18,7 @@ import ( // UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `` tag, or an `` but also `some plain text`. type UiNode struct { Attributes UiNodeAttributes `json:"attributes"` - // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup + // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup Group string `json:"group"` Messages []UiText `json:"messages"` Meta UiNodeMeta `json:"meta"` diff --git a/internal/httpclient/model_ui_node.go b/internal/httpclient/model_ui_node.go index 3582d9e85f67..84b728514114 100644 --- a/internal/httpclient/model_ui_node.go +++ b/internal/httpclient/model_ui_node.go @@ -18,7 +18,7 @@ import ( // UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `` tag, or an `` but also `some plain text`. type UiNode struct { Attributes UiNodeAttributes `json:"attributes"` - // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup + // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup Group string `json:"group"` Messages []UiText `json:"messages"` Meta UiNodeMeta `json:"meta"` diff --git a/spec/api.json b/spec/api.json index 7cdd1a4482bc..6f31f07b7172 100644 --- a/spec/api.json +++ b/spec/api.json @@ -2231,7 +2231,7 @@ "$ref": "#/components/schemas/uiNodeAttributes" }, "group": { - "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup", + "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup", "enum": [ "default", "password", @@ -2243,10 +2243,11 @@ "lookup_secret", "webauthn", "passkey", - "identifier_first" + "identifier_first", + "captcha" ], "type": "string", - "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup" + "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup" }, "messages": { "$ref": "#/components/schemas/uiTexts" diff --git a/spec/swagger.json b/spec/swagger.json index 7fbee9f76ae6..38c8d2d8555e 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -5349,7 +5349,7 @@ "$ref": "#/definitions/uiNodeAttributes" }, "group": { - "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup", + "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup", "type": "string", "enum": [ "default", @@ -5362,9 +5362,10 @@ "lookup_secret", "webauthn", "passkey", - "identifier_first" + "identifier_first", + "captcha" ], - "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup" + "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup" }, "messages": { "$ref": "#/definitions/uiTexts" diff --git a/ui/node/node.go b/ui/node/node.go index 66e1b72fa8b6..7d9137db20ff 100644 --- a/ui/node/node.go +++ b/ui/node/node.go @@ -50,6 +50,7 @@ const ( WebAuthnGroup UiNodeGroup = "webauthn" PasskeyGroup UiNodeGroup = "passkey" IdentifierFirstGroup UiNodeGroup = "identifier_first" + CaptchaGroup UiNodeGroup = "captcha" // Available in OEL ) func (g UiNodeGroup) String() string { From 38641c87e54e0a87e9121bb56a45a0b45b91b194 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:51:41 +0000 Subject: [PATCH 137/158] chore: update repository templates to https://github.com/ory/meta/commit/cbb120bd7c046b6af46b2148f5cfc1b3d03a1dd2 --- .github/workflows/licenses.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index e1d172f2f1f1..7d0eb3acde3a 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -11,7 +11,8 @@ on: - master jobs: - check: + licenses: + name: License compliance runs-on: ubuntu-latest steps: - name: Install script From 587655590cbb1e6a2846f282c046153264ff8427 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:01:37 +0000 Subject: [PATCH 138/158] autogen: update license overview --- .reports/dep-licenses.csv | 532 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 .reports/dep-licenses.csv diff --git a/.reports/dep-licenses.csv b/.reports/dep-licenses.csv new file mode 100644 index 000000000000..efcfe9f0d706 --- /dev/null +++ b/.reports/dep-licenses.csv @@ -0,0 +1,532 @@ +"code.dny.dev/ssrf","MIT" +"dario.cat/mergo","BSD-3-Clause" +"filippo.io/edwards25519","BSD-3-Clause" +"github.com/Masterminds/goutils","Apache-2.0" +"github.com/Masterminds/semver/v3","MIT" +"github.com/Masterminds/sprig/v3","MIT" +"github.com/Nvveen/Gotty","BSD-2-Clause" +"github.com/arbovm/levenshtein","BSD-3-Clause" +"github.com/asaskevich/govalidator","MIT" +"github.com/avast/retry-go/v4","MIT" +"github.com/aymerick/douceur","MIT" +"github.com/beorn7/perks/quantile","MIT" +"github.com/boombuler/barcode","MIT" +"github.com/bwmarrin/discordgo","BSD-3-Clause" +"github.com/cenkalti/backoff","MIT" +"github.com/cenkalti/backoff/v4","MIT" +"github.com/cespare/xxhash/v2","MIT" +"github.com/cockroachdb/cockroach-go/v2/crdb","Apache-2.0" +"github.com/containerd/continuity/pathdriver","Apache-2.0" +"github.com/coreos/go-oidc/v3/oidc","Apache-2.0" +"github.com/davecgh/go-spew/spew","ISC" +"github.com/dghubble/oauth1","MIT" +"github.com/dgraph-io/ristretto","Apache-2.0" +"github.com/dgraph-io/ristretto/v2","Apache-2.0" +"github.com/dgraph-io/ristretto/v2/z","MIT" +"github.com/dgraph-io/ristretto/z","MIT" +"github.com/docker/cli","Apache-2.0" +"github.com/docker/docker","Apache-2.0" +"github.com/docker/go-connections/nat","Apache-2.0" +"github.com/docker/go-units","Apache-2.0" +"github.com/dustin/go-humanize","MIT" +"github.com/evanphx/json-patch/v5","BSD-3-Clause" +"github.com/fatih/color","MIT" +"github.com/fatih/structs","MIT" +"github.com/felixge/fgprof","MIT" +"github.com/felixge/httpsnoop","MIT" +"github.com/fsnotify/fsnotify","BSD-3-Clause" +"github.com/fxamacker/cbor/v2","MIT" +"github.com/gabriel-vasile/mimetype","MIT" +"github.com/go-crypt/crypt","MIT" +"github.com/go-crypt/x","BSD-3-Clause" +"github.com/go-faker/faker/v4/pkg/slice","MIT" +"github.com/go-jose/go-jose/v3","Apache-2.0" +"github.com/go-jose/go-jose/v3/json","BSD-3-Clause" +"github.com/go-jose/go-jose/v4","Apache-2.0" +"github.com/go-jose/go-jose/v4/json","BSD-3-Clause" +"github.com/go-logr/logr","Apache-2.0" +"github.com/go-logr/stdr","Apache-2.0" +"github.com/go-openapi/errors","Apache-2.0" +"github.com/go-openapi/jsonpointer","Apache-2.0" +"github.com/go-openapi/strfmt","Apache-2.0" +"github.com/go-openapi/swag","Apache-2.0" +"github.com/go-playground/locales","MIT" +"github.com/go-playground/universal-translator","MIT" +"github.com/go-playground/validator/v10","MIT" +"github.com/go-sql-driver/mysql","MPL-2.0" +"github.com/go-webauthn/webauthn","BSD-3-Clause" +"github.com/go-webauthn/x/revoke","BSD-2-Clause" +"github.com/gobuffalo/envy","MIT" +"github.com/gobuffalo/fizz","MIT" +"github.com/gobuffalo/flect","MIT" +"github.com/gobuffalo/github_flavored_markdown","MIT" +"github.com/gobuffalo/github_flavored_markdown/internal/russross/blackfriday","BSD-2-Clause" +"github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/sanitized_anchor_name","MIT" +"github.com/gobuffalo/helpers","MIT" +"github.com/gobuffalo/nulls","MIT" +"github.com/gobuffalo/plush/v4","MIT" +"github.com/gobuffalo/pop/v6","MIT" +"github.com/gobuffalo/tags/v3","MIT" +"github.com/gobuffalo/validate/v3","MIT" +"github.com/gobwas/glob","MIT" +"github.com/goccy/go-yaml","MIT" +"github.com/gofrs/uuid","MIT" +"github.com/gogo/protobuf","BSD-3-Clause" +"github.com/golang-jwt/jwt/v4","MIT" +"github.com/golang-jwt/jwt/v5","MIT" +"github.com/golang/gddo/httputil","BSD-3-Clause" +"github.com/golang/protobuf","BSD-3-Clause" +"github.com/google/go-github/v38/github","BSD-3-Clause" +"github.com/google/go-jsonnet","Apache-2.0" +"github.com/google/go-querystring/query","BSD-3-Clause" +"github.com/google/go-tpm","Apache-2.0" +"github.com/google/pprof/profile","Apache-2.0" +"github.com/google/shlex","Apache-2.0" +"github.com/google/uuid","BSD-3-Clause" +"github.com/gorilla/css/scanner","BSD-3-Clause" +"github.com/gorilla/securecookie","BSD-3-Clause" +"github.com/gorilla/sessions","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"github.com/grpc-ecosystem/go-grpc-prometheus","Apache-2.0" +"github.com/grpc-ecosystem/grpc-gateway/v2","BSD-3-Clause" +"github.com/gtank/cryptopasta","CC0-1.0" +"github.com/hashicorp/go-cleanhttp","MPL-2.0" +"github.com/hashicorp/go-retryablehttp","MPL-2.0" +"github.com/hashicorp/golang-lru/v2","MPL-2.0" +"github.com/hashicorp/golang-lru/v2/simplelru","BSD-3-Clause" +"github.com/huandu/xstrings","MIT" +"github.com/imdario/mergo","BSD-3-Clause" +"github.com/inhies/go-bytesize","BSD-3-Clause" +"github.com/jackc/chunkreader/v2","MIT" +"github.com/jackc/pgconn","MIT" +"github.com/jackc/pgio","MIT" +"github.com/jackc/pgpassfile","MIT" +"github.com/jackc/pgproto3/v2","MIT" +"github.com/jackc/pgservicefile","MIT" +"github.com/jackc/pgx/v5","MIT" +"github.com/jackc/puddle/v2","MIT" +"github.com/jmoiron/sqlx","MIT" +"github.com/joho/godotenv","MIT" +"github.com/josharian/intern","MIT" +"github.com/julienschmidt/httprouter","BSD-3-Clause" +"github.com/kballard/go-shellquote","MIT" +"github.com/knadh/koanf/maps","MIT" +"github.com/knadh/koanf/parsers/json","MIT" +"github.com/knadh/koanf/parsers/toml","MIT" +"github.com/knadh/koanf/parsers/yaml","MIT" +"github.com/knadh/koanf/providers/posflag","MIT" +"github.com/knadh/koanf/v2","MIT" +"github.com/leodido/go-urn","MIT" +"github.com/lestrrat-go/backoff/v2","MIT" +"github.com/lestrrat-go/blackmagic","MIT" +"github.com/lestrrat-go/httpcc","MIT" +"github.com/lestrrat-go/iter","MIT" +"github.com/lestrrat-go/jwx","MIT" +"github.com/lestrrat-go/option","MIT" +"github.com/lib/pq","MIT" +"github.com/luna-duclos/instrumentedsql","MIT" +"github.com/mailru/easyjson","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"github.com/matttproud/golang_protobuf_extensions/pbutil","Apache-2.0" +"github.com/microcosm-cc/bluemonday","BSD-3-Clause" +"github.com/mitchellh/copystructure","MIT" +"github.com/mitchellh/mapstructure","MIT" +"github.com/mitchellh/reflectwalk","MIT" +"github.com/moby/docker-image-spec/specs-go/v1","Apache-2.0" +"github.com/moby/term","Apache-2.0" +"github.com/mohae/deepcopy","MIT" +"github.com/montanaflynn/stats","MIT" +"github.com/nyaruka/phonenumbers","MIT" +"github.com/oklog/ulid","Apache-2.0" +"github.com/opencontainers/go-digest","Apache-2.0" +"github.com/opencontainers/image-spec/specs-go","Apache-2.0" +"github.com/opencontainers/runc/libcontainer/user","Apache-2.0" +"github.com/openzipkin/zipkin-go/model","Apache-2.0" +"github.com/ory/analytics-go/v5","MIT" +"github.com/ory/dockertest/v3","Apache-2.0" +"github.com/ory/dockertest/v3/docker","BSD-2-Clause" +"github.com/ory/graceful","Apache-2.0" +"github.com/ory/herodot","Apache-2.0" +"github.com/ory/hydra-client-go/v2","Apache-2.0" +"github.com/ory/jsonschema/v3","BSD-3-Clause" +"github.com/ory/kratos","Apache-2.0" +"github.com/ory/mail/v3","MIT" +"github.com/ory/nosurf","MIT" +"github.com/ory/x","Apache-2.0" +"github.com/ory/x/reqlog","MIT" +"github.com/pelletier/go-toml","MIT" +"github.com/pelletier/go-toml","Apache-2.0" +"github.com/peterhellberg/link","MIT" +"github.com/phayes/freeport","BSD-3-Clause" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/pkg/profile","BSD-2-Clause" +"github.com/pmezard/go-difflib/difflib","BSD-3-Clause" +"github.com/pquerna/otp","Apache-2.0" +"github.com/prometheus/client_golang/prometheus","Apache-2.0" +"github.com/prometheus/client_model/go","Apache-2.0" +"github.com/prometheus/common","Apache-2.0" +"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg","BSD-3-Clause" +"github.com/prometheus/procfs","Apache-2.0" +"github.com/rogpeppe/go-internal/modfile","BSD-3-Clause" +"github.com/rs/cors","MIT" +"github.com/samber/lo","MIT" +"github.com/seatgeek/logrus-gelf-formatter","BSD-3-Clause" +"github.com/segmentio/backo-go","MIT" +"github.com/sergi/go-diff/diffmatchpatch","MIT" +"github.com/shopspring/decimal","MIT" +"github.com/sirupsen/logrus","MIT" +"github.com/slack-go/slack","BSD-2-Clause" +"github.com/sourcegraph/annotate","BSD-3-Clause" +"github.com/sourcegraph/syntaxhighlight","BSD-3-Clause" +"github.com/spf13/cast","MIT" +"github.com/spf13/cobra","Apache-2.0" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/stretchr/testify","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/sjson","MIT" +"github.com/urfave/negroni","MIT" +"github.com/wI2L/jsondiff","MIT" +"github.com/x448/float16","MIT" +"github.com/xeipuuv/gojsonpointer","Apache-2.0" +"github.com/xeipuuv/gojsonreference","Apache-2.0" +"github.com/xeipuuv/gojsonschema","Apache-2.0" +"github.com/xtgo/uuid","BSD-3-Clause" +"github.com/zmb3/spotify/v2","Apache-2.0" +"go.mongodb.org/mongo-driver","Apache-2.0" +"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace","Apache-2.0" +"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp","Apache-2.0" +"go.opentelemetry.io/contrib/propagators/b3","Apache-2.0" +"go.opentelemetry.io/contrib/propagators/jaeger","Apache-2.0" +"go.opentelemetry.io/contrib/samplers/jaegerremote","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/exporters/jaeger","Apache-2.0" +"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift","Apache-2.0" +"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift","GNU-All-permissive-Copying-License" +"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift","BSD-3-Clause" +"go.opentelemetry.io/otel/exporters/otlp/otlptrace","Apache-2.0" +"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp","Apache-2.0" +"go.opentelemetry.io/otel/exporters/zipkin","Apache-2.0" +"go.opentelemetry.io/otel/metric","Apache-2.0" +"go.opentelemetry.io/otel/sdk","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"go.opentelemetry.io/proto/otlp","Apache-2.0" +"golang.org/x/crypto","BSD-3-Clause" +"golang.org/x/exp/slices","BSD-3-Clause" +"golang.org/x/mod","BSD-3-Clause" +"golang.org/x/net","BSD-3-Clause" +"golang.org/x/oauth2","BSD-3-Clause" +"golang.org/x/sync","BSD-3-Clause" +"golang.org/x/sys","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"golang.org/x/xerrors","BSD-3-Clause" +"google.golang.org/genproto/googleapis/api","Apache-2.0" +"google.golang.org/genproto/googleapis/rpc","Apache-2.0" +"google.golang.org/grpc","Apache-2.0" +"google.golang.org/protobuf","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"gopkg.in/yaml.v3","MIT" +"sigs.k8s.io/yaml","MIT" +"sigs.k8s.io/yaml","BSD-3-Clause" +"dario.cat/mergo","BSD-3-Clause" +"github.com/Masterminds/goutils","Apache-2.0" +"github.com/Masterminds/semver/v3","MIT" +"github.com/Masterminds/sprig/v3","MIT" +"github.com/google/uuid","BSD-3-Clause" +"github.com/huandu/xstrings","MIT" +"github.com/imdario/mergo","BSD-3-Clause" +"github.com/mitchellh/copystructure","MIT" +"github.com/mitchellh/reflectwalk","MIT" +"github.com/shopspring/decimal","MIT" +"github.com/spf13/cast","MIT" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/arbovm/levenshtein","BSD-3-Clause" +"github.com/avast/retry-go/v3","MIT" +"github.com/bradleyjkemp/cupaloy/v2","MIT" +"github.com/davecgh/go-spew/spew","ISC" +"github.com/pmezard/go-difflib/difflib","BSD-3-Clause" +"github.com/bwmarrin/discordgo","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/cenkalti/backoff","MIT" +"github.com/bmatcuk/doublestar","MIT" +"github.com/cortesi/modd","MIT" +"github.com/cortesi/modd/conf","BSD-3-Clause" +"github.com/cortesi/moddwatch","MIT" +"github.com/cortesi/termlog","MIT" +"github.com/fatih/color","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"github.com/rjeczalik/notify","MIT" +"golang.org/x/crypto/ssh/terminal","BSD-3-Clause" +"golang.org/x/net/context","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/term","BSD-3-Clause" +"github.com/dghubble/oauth1","MIT" +"github.com/cespare/xxhash/v2","MIT" +"github.com/dgraph-io/ristretto","Apache-2.0" +"github.com/dgraph-io/ristretto/z","MIT" +"github.com/dustin/go-humanize","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/fatih/color","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/ghodss/yaml","MIT" +"github.com/ghodss/yaml","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"github.com/go-crypt/crypt","MIT" +"github.com/go-crypt/x","BSD-3-Clause" +"golang.org/x/sys/cpu","BSD-3-Clause" +"github.com/go-faker/faker/v4","MIT" +"golang.org/x/text","BSD-3-Clause" +"github.com/asaskevich/govalidator","MIT" +"github.com/go-openapi/errors","Apache-2.0" +"github.com/go-openapi/strfmt","Apache-2.0" +"github.com/google/uuid","BSD-3-Clause" +"github.com/mitchellh/mapstructure","MIT" +"github.com/oklog/ulid","Apache-2.0" +"go.mongodb.org/mongo-driver","Apache-2.0" +"github.com/gabriel-vasile/mimetype","MIT" +"github.com/go-playground/locales","MIT" +"github.com/go-playground/universal-translator","MIT" +"github.com/go-playground/validator/v10","MIT" +"github.com/leodido/go-urn","MIT" +"golang.org/x/crypto/sha3","BSD-3-Clause" +"golang.org/x/net/html","BSD-3-Clause" +"golang.org/x/sys/cpu","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"github.com/go-swagger/go-swagger","Apache-2.0" +"github.com/gobuffalo/httptest","MIT" +"github.com/gobuffalo/httptest/internal/takeon/github.com/ajg/form","BSD-3-Clause" +"github.com/gobuffalo/httptest/internal/takeon/github.com/markbates/hmax","MIT" +"filippo.io/edwards25519","BSD-3-Clause" +"github.com/Masterminds/semver/v3","MIT" +"github.com/aymerick/douceur","MIT" +"github.com/fatih/color","MIT" +"github.com/fatih/structs","MIT" +"github.com/go-sql-driver/mysql","MPL-2.0" +"github.com/gobuffalo/envy","MIT" +"github.com/gobuffalo/fizz","MIT" +"github.com/gobuffalo/flect","MIT" +"github.com/gobuffalo/github_flavored_markdown","MIT" +"github.com/gobuffalo/github_flavored_markdown/internal/russross/blackfriday","BSD-2-Clause" +"github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/sanitized_anchor_name","MIT" +"github.com/gobuffalo/helpers","MIT" +"github.com/gobuffalo/nulls","MIT" +"github.com/gobuffalo/plush/v4","MIT" +"github.com/gobuffalo/pop/v6","MIT" +"github.com/gobuffalo/tags/v3","MIT" +"github.com/gobuffalo/validate/v3","MIT" +"github.com/gofrs/uuid","MIT" +"github.com/gorilla/css/scanner","BSD-3-Clause" +"github.com/jackc/chunkreader/v2","MIT" +"github.com/jackc/pgconn","MIT" +"github.com/jackc/pgio","MIT" +"github.com/jackc/pgpassfile","MIT" +"github.com/jackc/pgproto3/v2","MIT" +"github.com/jackc/pgservicefile","MIT" +"github.com/jackc/pgx/v5","MIT" +"github.com/jackc/puddle/v2","MIT" +"github.com/jmoiron/sqlx","MIT" +"github.com/joho/godotenv","MIT" +"github.com/kballard/go-shellquote","MIT" +"github.com/luna-duclos/instrumentedsql","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"github.com/microcosm-cc/bluemonday","BSD-3-Clause" +"github.com/rogpeppe/go-internal/modfile","BSD-3-Clause" +"github.com/sergi/go-diff/diffmatchpatch","MIT" +"github.com/sourcegraph/annotate","BSD-3-Clause" +"github.com/sourcegraph/syntaxhighlight","BSD-3-Clause" +"golang.org/x/crypto/pbkdf2","BSD-3-Clause" +"golang.org/x/mod","BSD-3-Clause" +"golang.org/x/net/html","BSD-3-Clause" +"golang.org/x/sync","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"github.com/gofrs/uuid","MIT" +"github.com/golang-jwt/jwt/v4","MIT" +"github.com/golang-jwt/jwt/v5","MIT" +"github.com/google/go-jsonnet","Apache-2.0" +"gopkg.in/yaml.v2","Apache-2.0" +"sigs.k8s.io/yaml","MIT" +"sigs.k8s.io/yaml","BSD-3-Clause" +"github.com/gorilla/securecookie","BSD-3-Clause" +"github.com/gorilla/sessions","BSD-3-Clause" +"github.com/gtank/cryptopasta","CC0-1.0" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/hashicorp/go-cleanhttp","MPL-2.0" +"github.com/hashicorp/go-retryablehttp","MPL-2.0" +"github.com/hashicorp/golang-lru/v2","MPL-2.0" +"github.com/hashicorp/golang-lru/v2/simplelru","BSD-3-Clause" +"github.com/inhies/go-bytesize","BSD-3-Clause" +"github.com/jarcoal/httpmock","MIT" +"github.com/jmoiron/sqlx","MIT" +"github.com/julienschmidt/httprouter","BSD-3-Clause" +"github.com/knadh/koanf/parsers/json","MIT" +"github.com/lestrrat-go/jwx","MIT" +"github.com/lestrrat-go/option","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/lestrrat-go/jwx/v2","MIT" +"github.com/lestrrat-go/option","MIT" +"github.com/luna-duclos/instrumentedsql","MIT" +"github.com/gorilla/context","BSD-3-Clause" +"github.com/gorilla/mux","BSD-3-Clause" +"github.com/gorilla/pat","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"github.com/ian-kent/envconf","MIT" +"github.com/ian-kent/go-log","MIT" +"github.com/ian-kent/goose","MIT" +"github.com/ian-kent/linkio","Unknown" +"github.com/mailhog/MailHog","MIT" +"github.com/mailhog/MailHog-Server","MIT" +"github.com/mailhog/MailHog-UI","MIT" +"github.com/mailhog/data","MIT" +"github.com/mailhog/http","MIT" +"github.com/mailhog/mhsendmail/cmd","MIT" +"github.com/mailhog/smtp","MIT" +"github.com/mailhog/storage","MIT" +"github.com/ogier/pflag","BSD-3-Clause" +"github.com/philhofer/fwd","MIT" +"github.com/t-k/fluent-logger-golang/fluent","Unknown" +"github.com/tinylib/msgp/msgp","MIT" +"golang.org/x/crypto","BSD-3-Clause" +"gopkg.in/mgo.v2","BSD-2-Clause" +"gopkg.in/mgo.v2/bson","BSD-2-Clause" +"gopkg.in/mgo.v2/internal/json","BSD-3-Clause" +"github.com/mattn/goveralls","MIT" +"golang.org/x/mod","BSD-3-Clause" +"golang.org/x/tools","BSD-3-Clause" +"github.com/mohae/deepcopy","MIT" +"github.com/montanaflynn/stats","MIT" +"github.com/nyaruka/phonenumbers","MIT" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/protobuf","BSD-3-Clause" +"github.com/ory/analytics-go/v5","MIT" +"github.com/segmentio/backo-go","MIT" +"github.com/xtgo/uuid","BSD-3-Clause" +"github.com/ory/client-go","Unknown" +"golang.org/x/oauth2","BSD-3-Clause" +"dario.cat/mergo","BSD-3-Clause" +"github.com/Nvveen/Gotty","BSD-2-Clause" +"github.com/cenkalti/backoff/v4","MIT" +"github.com/containerd/continuity/pathdriver","Apache-2.0" +"github.com/docker/cli","Apache-2.0" +"github.com/docker/docker","Apache-2.0" +"github.com/docker/go-connections/nat","Apache-2.0" +"github.com/docker/go-units","Apache-2.0" +"github.com/gogo/protobuf/proto","BSD-3-Clause" +"github.com/google/shlex","Apache-2.0" +"github.com/mitchellh/mapstructure","MIT" +"github.com/moby/docker-image-spec/specs-go/v1","Apache-2.0" +"github.com/moby/term","Apache-2.0" +"github.com/opencontainers/go-digest","Apache-2.0" +"github.com/opencontainers/image-spec/specs-go","Apache-2.0" +"github.com/opencontainers/runc/libcontainer/user","Apache-2.0" +"github.com/ory/dockertest/v3","Apache-2.0" +"github.com/ory/dockertest/v3/docker","BSD-2-Clause" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/sirupsen/logrus","MIT" +"github.com/xeipuuv/gojsonpointer","Apache-2.0" +"github.com/xeipuuv/gojsonreference","Apache-2.0" +"github.com/xeipuuv/gojsonschema","Apache-2.0" +"golang.org/x/sys/unix","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"github.com/fsnotify/fsnotify","BSD-3-Clause" +"github.com/hashicorp/hcl","MPL-2.0" +"github.com/magiconair/properties","BSD-2-Clause" +"github.com/mitchellh/mapstructure","MIT" +"github.com/ory/go-acc","Apache-2.0" +"github.com/pelletier/go-toml/v2","MIT" +"github.com/sagikazarmark/slog-shim","BSD-3-Clause" +"github.com/spf13/afero","Apache-2.0" +"github.com/spf13/cast","MIT" +"github.com/spf13/cobra","Apache-2.0" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/spf13/viper","MIT" +"github.com/subosito/gotenv","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"gopkg.in/ini.v1","Apache-2.0" +"gopkg.in/yaml.v3","MIT" +"github.com/ory/graceful","Apache-2.0" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/golang/protobuf/proto","BSD-3-Clause" +"github.com/ory/herodot","Apache-2.0" +"github.com/pkg/errors","BSD-2-Clause" +"golang.org/x/net","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/genproto/googleapis/rpc","Apache-2.0" +"google.golang.org/grpc","Apache-2.0" +"google.golang.org/protobuf","BSD-3-Clause" +"github.com/ory/hydra-client-go/v2","Apache-2.0" +"golang.org/x/oauth2","BSD-3-Clause" +"github.com/nyaruka/phonenumbers","MIT" +"github.com/ory/jsonschema/v3","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/protobuf","BSD-3-Clause" +"github.com/ory/mail/v3","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/ory/nosurf","MIT" +"github.com/ory/x","Apache-2.0" +"github.com/peterhellberg/link","MIT" +"github.com/phayes/freeport","BSD-3-Clause" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/boombuler/barcode","MIT" +"github.com/pquerna/otp","Apache-2.0" +"github.com/rs/cors","MIT" +"github.com/samber/lo","MIT" +"golang.org/x/text","BSD-3-Clause" +"github.com/sirupsen/logrus","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"github.com/slack-go/slack","BSD-2-Clause" +"github.com/spf13/cobra","Apache-2.0" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/stretchr/testify","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/sjson","MIT" +"github.com/urfave/negroni","MIT" +"github.com/tidwall/gjson","MIT" +"github.com/tidwall/match","MIT" +"github.com/tidwall/pretty","MIT" +"github.com/tidwall/sjson","MIT" +"github.com/wI2L/jsondiff","MIT" +"github.com/zmb3/spotify/v2","Apache-2.0" +"golang.org/x/oauth2","BSD-3-Clause" +"github.com/felixge/httpsnoop","MIT" +"github.com/go-logr/logr","Apache-2.0" +"github.com/go-logr/stdr","Apache-2.0" +"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/metric","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"github.com/go-logr/logr","Apache-2.0" +"github.com/go-logr/stdr","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/metric","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"go.opentelemetry.io/otel/sdk","Apache-2.0" +"go.opentelemetry.io/otel","Apache-2.0" +"go.opentelemetry.io/otel/trace","Apache-2.0" +"golang.org/x/oauth2","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"golang.org/x/net","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/genproto/googleapis/rpc/status","Apache-2.0" +"google.golang.org/grpc","Apache-2.0" +"google.golang.org/protobuf","BSD-3-Clause" + From d4f96ceedb07581075c60380c0586894dbed7392 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:58:40 +0000 Subject: [PATCH 139/158] chore: update repository templates to https://github.com/ory/meta/commit/b1eed8856cd301603956084d58f021707ace940a --- .github/ISSUE_TEMPLATE/BUG-REPORT.yml | 18 ++++++++++------ .github/ISSUE_TEMPLATE/DESIGN-DOC.yml | 18 ++++++++++------ .github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml | 24 ++++++++++++++-------- .github/ISSUE_TEMPLATE/config.yml | 6 ++++-- .github/workflows/licenses.yml | 2 +- 5 files changed, 45 insertions(+), 23 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml index 4048324eb8a7..d0b98a4c15ef 100644 --- a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -12,17 +12,22 @@ body: - attributes: label: "Preflight checklist" options: - - label: "I could not find a solution in the existing issues, docs, nor + - label: + "I could not find a solution in the existing issues, docs, nor discussions." required: true - - label: "I agree to follow this project's [Code of + - label: + "I agree to follow this project's [Code of Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)." required: true - - label: "I have read and am following this repository's [Contribution + - label: + "I have read and am following this repository's [Contribution Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - - label: "I have joined the [Ory Community Slack](https://slack.ory.sh)." - - label: "I am signed up to the [Ory Security Patch + - label: + "I have joined the [Ory Community Slack](https://slack.ory.sh)." + - label: + "I am signed up to the [Ory Security Patch Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes @@ -57,7 +62,8 @@ body: validations: required: true - attributes: - description: "Please copy and paste any relevant log output. This will be + description: + "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. Please redact any sensitive information" label: "Relevant log output" diff --git a/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml b/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml index b5741119698b..0fb22fad2b48 100644 --- a/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml +++ b/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml @@ -1,7 +1,8 @@ # AUTO-GENERATED, DO NOT EDIT! # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml -description: "A design document is needed for non-trivial changes to the code base." +description: + "A design document is needed for non-trivial changes to the code base." labels: - rfc name: "Design Document" @@ -22,17 +23,22 @@ body: - attributes: label: "Preflight checklist" options: - - label: "I could not find a solution in the existing issues, docs, nor + - label: + "I could not find a solution in the existing issues, docs, nor discussions." required: true - - label: "I agree to follow this project's [Code of + - label: + "I agree to follow this project's [Code of Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)." required: true - - label: "I have read and am following this repository's [Contribution + - label: + "I have read and am following this repository's [Contribution Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - - label: "I have joined the [Ory Community Slack](https://slack.ory.sh)." - - label: "I am signed up to the [Ory Security Patch + - label: + "I have joined the [Ory Community Slack](https://slack.ory.sh)." + - label: + "I am signed up to the [Ory Security Patch Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml index 7152fbdde4cf..e0e42201886e 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml @@ -1,7 +1,8 @@ # AUTO-GENERATED, DO NOT EDIT! # Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml -description: "Suggest an idea for this project without a plan for implementation" +description: + "Suggest an idea for this project without a plan for implementation" labels: - feat name: "Feature Request" @@ -15,17 +16,22 @@ body: - attributes: label: "Preflight checklist" options: - - label: "I could not find a solution in the existing issues, docs, nor + - label: + "I could not find a solution in the existing issues, docs, nor discussions." required: true - - label: "I agree to follow this project's [Code of + - label: + "I agree to follow this project's [Code of Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)." required: true - - label: "I have read and am following this repository's [Contribution + - label: + "I have read and am following this repository's [Contribution Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md)." required: true - - label: "I have joined the [Ory Community Slack](https://slack.ory.sh)." - - label: "I am signed up to the [Ory Security Patch + - label: + "I have joined the [Ory Community Slack](https://slack.ory.sh)." + - label: + "I am signed up to the [Ory Security Patch Newsletter](https://www.ory.sh/l/sign-up-newsletter)." id: checklist type: checkboxes @@ -38,7 +44,8 @@ body: id: ory-network-project type: input - attributes: - description: "Is your feature request related to a problem? Please describe." + description: + "Is your feature request related to a problem? Please describe." label: "Describe your problem" placeholder: "A clear and concise description of what the problem is. Ex. I'm always @@ -72,7 +79,8 @@ body: validations: required: true - attributes: - description: "Add any other context or screenshots about the feature request here." + description: + "Add any other context or screenshots about the feature request here." label: Additional Context id: additional type: textarea diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index ef4c482ae405..abb0b696c9d9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -5,8 +5,10 @@ blank_issues_enabled: false contact_links: - name: Ory Kratos Forum url: https://github.com/ory/kratos/discussions - about: Please ask and answer questions here, show your implementations and + about: + Please ask and answer questions here, show your implementations and discuss ideas. - name: Ory Chat url: https://www.ory.sh/chat - about: Hang out with other Ory community members to ask and answer questions. + about: + Hang out with other Ory community members to ask and answer questions. diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 7d0eb3acde3a..0a12162ed8a1 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -21,7 +21,7 @@ jobs: token: ${{ secrets.ORY_BOT_PAT || secrets.GITHUB_TOKEN }} - name: Check licenses uses: ory/ci/licenses/check@master - - name: Write licenses + - name: Write, commit, push licenses uses: ory/ci/licenses/write@master if: ${{ github.ref == 'refs/heads/main' || github.ref == From daa573737a8caff7cfd56474d0d113311c8029e0 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 30 Dec 2024 23:49:57 +0000 Subject: [PATCH 140/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7f68260ed27..911aced50cad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-27)](#2024-12-27) +- [ (2024-12-30)](#2024-12-30) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-27) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-30) ## Breaking Changes @@ -400,6 +400,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Registration post persist hooks should not be cancelable ([#4148](https://github.com/ory/kratos/issues/4148)) ([18056a0](https://github.com/ory/kratos/commit/18056a0f1cfdf42769e5a974b2526ccf5c608cc2)) +- **sdk:** Add missing captcha group + ([#4254](https://github.com/ory/kratos/issues/4254)) + ([241111b](https://github.com/ory/kratos/commit/241111b21f5d96b26ff8bc8106dc8a527c68063b)) - **sdk:** Remove incorrect attributes ([#4163](https://github.com/ory/kratos/issues/4163)) ([88c68aa](https://github.com/ory/kratos/commit/88c68aa07281a638c9897e76d300d1095b17601d)) From 555c997f95ea1982dfdb6042658a4f3e7c24d420 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 31 Dec 2024 10:34:01 +0000 Subject: [PATCH 141/158] chore: update repository templates to https://github.com/ory/meta/commit/6dd58197127fcc22f1602fe2481e65e3fb356441 --- .github/workflows/licenses.yml | 6 ++++++ .github/workflows/milestone.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 0a12162ed8a1..68ebf0a0e13e 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -26,3 +26,9 @@ jobs: if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' }} + with: + author-email: + ${{ secrets.ORY_BOT_PAT && + '60093411+ory-bot@users.noreply.github.com' || github.actor + + '@users.noreply.github.com' }} + author-name: ${{ secrets.ORY_BOT_PAT && 'ory-bot' || github.actor }} diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml index 5d25a715ddd8..218b9c6e62a1 100644 --- a/.github/workflows/milestone.yml +++ b/.github/workflows/milestone.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: token: ${{ secrets.TOKEN_PRIVILEGED }} - name: Milestone Documentation Generator From 898fcb4b08016465c80186757a1e42f8348dea69 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 31 Dec 2024 10:45:02 +0000 Subject: [PATCH 142/158] chore: update repository templates to https://github.com/ory/meta/commit/cb2a20fceb295da97a8988e4947a3555b3f026a8 --- .github/workflows/licenses.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 68ebf0a0e13e..171e019634ba 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -29,6 +29,6 @@ jobs: with: author-email: ${{ secrets.ORY_BOT_PAT && - '60093411+ory-bot@users.noreply.github.com' || github.actor + - '@users.noreply.github.com' }} + '60093411+ory-bot@users.noreply.github.com' || + format('{0}@users.noreply.github.com', github.actor) }} author-name: ${{ secrets.ORY_BOT_PAT && 'ory-bot' || github.actor }} From 18d7f5e8c70719fb22fbaa8669e2bc217ccd176c Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:45:32 +0000 Subject: [PATCH 143/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 911aced50cad..1ae680f41bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-30)](#2024-12-30) +- [ (2024-12-31)](#2024-12-31) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-30) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-31) ## Breaking Changes From 1faf7cc4ebf4ce43d4b018d0be6bbc877f014602 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:11:47 +0000 Subject: [PATCH 144/158] chore: update repository templates to https://github.com/ory/meta/commit/c091d7964885eaf0458fca234bf3521ffc4bc43b --- .github/workflows/licenses.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 171e019634ba..38c16511ee90 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -25,7 +25,7 @@ jobs: uses: ory/ci/licenses/write@master if: ${{ github.ref == 'refs/heads/main' || github.ref == - 'refs/heads/master' }} + 'refs/heads/master' || github.ref == 'refs/heads/v3' }} with: author-email: ${{ secrets.ORY_BOT_PAT && From b23d81f73f8a229596dfbe359454999eff9e1789 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:17:16 +0000 Subject: [PATCH 145/158] autogen: update license overview --- .reports/dep-licenses.csv | 489 +++++++++++++++++++------------------- 1 file changed, 243 insertions(+), 246 deletions(-) diff --git a/.reports/dep-licenses.csv b/.reports/dep-licenses.csv index efcfe9f0d706..208f80a50b3e 100644 --- a/.reports/dep-licenses.csv +++ b/.reports/dep-licenses.csv @@ -1,3 +1,246 @@ +"module name","licenses" +"@lukeed/csprng@1.1.0","MIT" +"@nestjs/axios@0.1.0","MIT" +"@nestjs/common@9.3.11","MIT" +"@nestjs/core@9.3.11","MIT" +"@nuxtjs/opencollective@0.3.2","MIT" +"@openapitools/openapi-generator-cli@2.7.0","Apache-2.0" +"ansi-escapes@4.3.2","MIT" +"ansi-regex@5.0.1","MIT" +"ansi-styles@4.3.0","MIT" +"argparse@1.0.10","MIT" +"asynckit@0.4.0","MIT" +"axios@0.27.2","MIT" +"balanced-match@1.0.2","MIT" +"base64-js@1.5.1","MIT" +"bl@4.1.0","MIT" +"brace-expansion@1.1.11","MIT" +"buffer@5.7.1","MIT" +"chalk@4.1.2","MIT" +"chardet@0.7.0","MIT" +"cli-cursor@3.1.0","MIT" +"cli-spinners@2.8.0","MIT" +"cli-width@3.0.0","ISC" +"cliui@7.0.4","ISC" +"clone@1.0.4","MIT" +"color-convert@2.0.1","MIT" +"color-name@1.1.4","MIT" +"combined-stream@1.0.8","MIT" +"commander@8.3.0","MIT" +"compare-versions@4.1.4","MIT" +"concat-map@0.0.1","MIT" +"concurrently@6.5.1","MIT" +"consola@2.15.3","MIT" +"console.table@0.10.0","MIT" +"date-fns@2.28.0","MIT" +"defaults@1.0.3","MIT" +"delayed-stream@1.0.0","MIT" +"easy-table@1.1.0","MIT" +"emoji-regex@8.0.0","MIT" +"escalade@3.1.1","MIT" +"escape-string-regexp@1.0.5","MIT" +"external-editor@3.1.0","MIT" +"fast-safe-stringify@2.1.1","MIT" +"figures@3.2.0","MIT" +"follow-redirects@1.15.4","MIT" +"form-data@4.0.0","MIT" +"fs-extra@10.1.0","MIT" +"fs.realpath@1.0.0","ISC" +"get-caller-file@2.0.5","ISC" +"glob@7.1.6","ISC" +"graceful-fs@4.2.10","ISC" +"has-flag@4.0.0","MIT" +"iconv-lite@0.4.24","MIT" +"ieee754@1.2.1","BSD-3-Clause" +"inflight@1.0.6","ISC" +"inherits@2.0.4","ISC" +"inquirer@8.2.5","MIT" +"is-fullwidth-code-point@3.0.0","MIT" +"is-interactive@1.0.0","MIT" +"is-unicode-supported@0.1.0","MIT" +"iterare@1.2.1","ISC" +"jsonfile@6.1.0","MIT" +"lodash@4.17.21","MIT" +"log-symbols@4.1.0","MIT" +"mime-db@1.52.0","MIT" +"mime-types@2.1.35","MIT" +"mimic-fn@2.1.0","MIT" +"minimatch@3.1.2","ISC" +"mute-stream@0.0.8","ISC" +"node-fetch@2.6.9","MIT" +"once@1.4.0","ISC" +"onetime@5.1.2","MIT" +"ora@5.4.1","MIT" +"os-tmpdir@1.0.2","MIT" +"path-is-absolute@1.0.1","MIT" +"path-to-regexp@3.2.0","MIT" +"readable-stream@3.6.2","MIT" +"reflect-metadata@0.1.13","Apache-2.0" +"require-directory@2.1.1","MIT" +"restore-cursor@3.1.0","MIT" +"run-async@2.4.1","MIT" +"rxjs@6.6.7","Apache-2.0" +"rxjs@7.8.0","Apache-2.0" +"safe-buffer@5.2.1","MIT" +"safer-buffer@2.1.2","MIT" +"signal-exit@3.0.7","ISC" +"spawn-command@0.0.2-1","MIT" +"sprintf-js@1.0.3","BSD-3-Clause" +"string-width@4.2.3","MIT" +"string_decoder@1.3.0","MIT" +"strip-ansi@6.0.1","MIT" +"supports-color@7.2.0","MIT" +"supports-color@8.1.1","MIT" +"through@2.3.8","MIT" +"tmp@0.0.33","MIT" +"tr46@0.0.3","MIT" +"tree-kill@1.2.2","MIT" +"tslib@1.14.1","0BSD" +"tslib@2.0.3","0BSD" +"tslib@2.4.0","0BSD" +"tslib@2.5.0","0BSD" +"type-fest@0.21.3","(MIT OR CC0-1.0)" +"uid@2.0.1","MIT" +"universalify@2.0.0","MIT" +"util-deprecate@1.0.2","MIT" +"wcwidth@1.0.1","MIT" +"webidl-conversions@3.0.1","BSD-2-Clause" +"whatwg-url@5.0.0","MIT" +"wrap-ansi@7.0.0","MIT" +"wrappy@1.0.2","ISC" +"y18n@5.0.8","ISC" +"yamljs@0.3.0","MIT" +"yargs-parser@20.2.9","ISC" +"yargs@16.2.0","MIT" + +"dario.cat/mergo","BSD-3-Clause" +"github.com/arbovm/levenshtein","BSD-3-Clause" +"github.com/bwmarrin/discordgo","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/cenkalti/backoff","MIT" +"github.com/bmatcuk/doublestar","MIT" +"github.com/cortesi/modd","MIT" +"github.com/cortesi/modd/conf","BSD-3-Clause" +"github.com/cortesi/moddwatch","MIT" +"github.com/cortesi/termlog","MIT" +"github.com/fatih/color","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"github.com/rjeczalik/notify","MIT" +"golang.org/x/crypto/ssh/terminal","BSD-3-Clause" +"golang.org/x/net/context","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/term","BSD-3-Clause" +"github.com/dghubble/oauth1","MIT" +"github.com/cespare/xxhash/v2","MIT" +"github.com/dgraph-io/ristretto","Apache-2.0" +"github.com/dgraph-io/ristretto/z","MIT" +"github.com/dustin/go-humanize","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/fatih/color","MIT" +"github.com/mattn/go-colorable","MIT" +"github.com/mattn/go-isatty","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"github.com/ghodss/yaml","MIT" +"github.com/ghodss/yaml","BSD-3-Clause" +"gopkg.in/yaml.v2","Apache-2.0" +"github.com/go-crypt/crypt","MIT" +"github.com/go-crypt/x","BSD-3-Clause" +"golang.org/x/sys/cpu","BSD-3-Clause" +"github.com/asaskevich/govalidator","MIT" +"github.com/go-openapi/errors","Apache-2.0" +"github.com/go-openapi/strfmt","Apache-2.0" +"github.com/google/uuid","BSD-3-Clause" +"github.com/mitchellh/mapstructure","MIT" +"github.com/oklog/ulid","Apache-2.0" +"go.mongodb.org/mongo-driver","Apache-2.0" +"github.com/go-swagger/go-swagger","Apache-2.0" +"github.com/gobuffalo/httptest","MIT" +"github.com/gobuffalo/httptest/internal/takeon/github.com/ajg/form","BSD-3-Clause" +"github.com/gobuffalo/httptest/internal/takeon/github.com/markbates/hmax","MIT" +"github.com/gofrs/uuid","MIT" +"github.com/google/go-jsonnet","Apache-2.0" +"gopkg.in/yaml.v2","Apache-2.0" +"sigs.k8s.io/yaml","MIT" +"sigs.k8s.io/yaml","BSD-3-Clause" +"github.com/gorilla/securecookie","BSD-3-Clause" +"github.com/gorilla/sessions","BSD-3-Clause" +"github.com/gtank/cryptopasta","CC0-1.0" +"golang.org/x/crypto","BSD-3-Clause" +"github.com/hashicorp/go-cleanhttp","MPL-2.0" +"github.com/hashicorp/go-retryablehttp","MPL-2.0" +"github.com/inhies/go-bytesize","BSD-3-Clause" +"github.com/jarcoal/httpmock","MIT" +"github.com/jmoiron/sqlx","MIT" +"github.com/julienschmidt/httprouter","BSD-3-Clause" +"github.com/lestrrat-go/jwx","MIT" +"github.com/lestrrat-go/option","MIT" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/luna-duclos/instrumentedsql","MIT" +"github.com/gorilla/context","BSD-3-Clause" +"github.com/gorilla/mux","BSD-3-Clause" +"github.com/gorilla/pat","BSD-3-Clause" +"github.com/gorilla/websocket","BSD-2-Clause" +"github.com/ian-kent/envconf","MIT" +"github.com/ian-kent/go-log","MIT" +"github.com/ian-kent/goose","MIT" +"github.com/ian-kent/linkio","Unknown" +"github.com/mailhog/MailHog","MIT" +"github.com/mailhog/MailHog-Server","MIT" +"github.com/mailhog/MailHog-UI","MIT" +"github.com/mailhog/data","MIT" +"github.com/mailhog/http","MIT" +"github.com/mailhog/mhsendmail/cmd","MIT" +"github.com/mailhog/smtp","MIT" +"github.com/mailhog/storage","MIT" +"github.com/ogier/pflag","BSD-3-Clause" +"github.com/philhofer/fwd","MIT" +"github.com/t-k/fluent-logger-golang/fluent","Unknown" +"github.com/tinylib/msgp/msgp","MIT" +"golang.org/x/crypto","BSD-3-Clause" +"gopkg.in/mgo.v2","BSD-2-Clause" +"gopkg.in/mgo.v2/bson","BSD-2-Clause" +"gopkg.in/mgo.v2/internal/json","BSD-3-Clause" +"github.com/mattn/goveralls","MIT" +"golang.org/x/mod","BSD-3-Clause" +"golang.org/x/tools","BSD-3-Clause" +"github.com/mohae/deepcopy","MIT" +"github.com/montanaflynn/stats","MIT" +"github.com/nyaruka/phonenumbers","MIT" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/protobuf","BSD-3-Clause" +"github.com/ory/client-go","Unknown" +"golang.org/x/oauth2","BSD-3-Clause" +"github.com/fsnotify/fsnotify","BSD-3-Clause" +"github.com/hashicorp/hcl","MPL-2.0" +"github.com/magiconair/properties","BSD-2-Clause" +"github.com/mitchellh/mapstructure","MIT" +"github.com/ory/go-acc","Apache-2.0" +"github.com/pelletier/go-toml/v2","MIT" +"github.com/sagikazarmark/slog-shim","BSD-3-Clause" +"github.com/spf13/afero","Apache-2.0" +"github.com/spf13/cast","MIT" +"github.com/spf13/cobra","Apache-2.0" +"github.com/spf13/pflag","BSD-3-Clause" +"github.com/spf13/viper","MIT" +"github.com/subosito/gotenv","MIT" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"gopkg.in/ini.v1","Apache-2.0" +"gopkg.in/yaml.v3","MIT" +"github.com/ory/graceful","Apache-2.0" +"github.com/pkg/errors","BSD-2-Clause" +"github.com/golang/protobuf/proto","BSD-3-Clause" +"github.com/ory/herodot","Apache-2.0" +"github.com/pkg/errors","BSD-2-Clause" +"golang.org/x/net","BSD-3-Clause" +"golang.org/x/sys/unix","BSD-3-Clause" +"golang.org/x/text","BSD-3-Clause" +"google.golang.org/genproto/googleapis/rpc","Apache-2.0" +"google.golang.org/grpc","Apache-2.0" +"google.golang.org/protobuf","BSD-3-Clause" "code.dny.dev/ssrf","MIT" "dario.cat/mergo","BSD-3-Clause" "filippo.io/edwards25519","BSD-3-Clause" @@ -230,249 +473,6 @@ "gopkg.in/yaml.v3","MIT" "sigs.k8s.io/yaml","MIT" "sigs.k8s.io/yaml","BSD-3-Clause" -"dario.cat/mergo","BSD-3-Clause" -"github.com/Masterminds/goutils","Apache-2.0" -"github.com/Masterminds/semver/v3","MIT" -"github.com/Masterminds/sprig/v3","MIT" -"github.com/google/uuid","BSD-3-Clause" -"github.com/huandu/xstrings","MIT" -"github.com/imdario/mergo","BSD-3-Clause" -"github.com/mitchellh/copystructure","MIT" -"github.com/mitchellh/reflectwalk","MIT" -"github.com/shopspring/decimal","MIT" -"github.com/spf13/cast","MIT" -"golang.org/x/crypto","BSD-3-Clause" -"github.com/arbovm/levenshtein","BSD-3-Clause" -"github.com/avast/retry-go/v3","MIT" -"github.com/bradleyjkemp/cupaloy/v2","MIT" -"github.com/davecgh/go-spew/spew","ISC" -"github.com/pmezard/go-difflib/difflib","BSD-3-Clause" -"github.com/bwmarrin/discordgo","BSD-3-Clause" -"github.com/gorilla/websocket","BSD-2-Clause" -"golang.org/x/crypto","BSD-3-Clause" -"github.com/cenkalti/backoff","MIT" -"github.com/bmatcuk/doublestar","MIT" -"github.com/cortesi/modd","MIT" -"github.com/cortesi/modd/conf","BSD-3-Clause" -"github.com/cortesi/moddwatch","MIT" -"github.com/cortesi/termlog","MIT" -"github.com/fatih/color","MIT" -"github.com/mattn/go-colorable","MIT" -"github.com/mattn/go-isatty","MIT" -"github.com/rjeczalik/notify","MIT" -"golang.org/x/crypto/ssh/terminal","BSD-3-Clause" -"golang.org/x/net/context","BSD-3-Clause" -"golang.org/x/sys/unix","BSD-3-Clause" -"golang.org/x/term","BSD-3-Clause" -"github.com/dghubble/oauth1","MIT" -"github.com/cespare/xxhash/v2","MIT" -"github.com/dgraph-io/ristretto","Apache-2.0" -"github.com/dgraph-io/ristretto/z","MIT" -"github.com/dustin/go-humanize","MIT" -"github.com/pkg/errors","BSD-2-Clause" -"golang.org/x/sys/unix","BSD-3-Clause" -"github.com/fatih/color","MIT" -"github.com/mattn/go-colorable","MIT" -"github.com/mattn/go-isatty","MIT" -"golang.org/x/sys/unix","BSD-3-Clause" -"github.com/ghodss/yaml","MIT" -"github.com/ghodss/yaml","BSD-3-Clause" -"gopkg.in/yaml.v2","Apache-2.0" -"github.com/go-crypt/crypt","MIT" -"github.com/go-crypt/x","BSD-3-Clause" -"golang.org/x/sys/cpu","BSD-3-Clause" -"github.com/go-faker/faker/v4","MIT" -"golang.org/x/text","BSD-3-Clause" -"github.com/asaskevich/govalidator","MIT" -"github.com/go-openapi/errors","Apache-2.0" -"github.com/go-openapi/strfmt","Apache-2.0" -"github.com/google/uuid","BSD-3-Clause" -"github.com/mitchellh/mapstructure","MIT" -"github.com/oklog/ulid","Apache-2.0" -"go.mongodb.org/mongo-driver","Apache-2.0" -"github.com/gabriel-vasile/mimetype","MIT" -"github.com/go-playground/locales","MIT" -"github.com/go-playground/universal-translator","MIT" -"github.com/go-playground/validator/v10","MIT" -"github.com/leodido/go-urn","MIT" -"golang.org/x/crypto/sha3","BSD-3-Clause" -"golang.org/x/net/html","BSD-3-Clause" -"golang.org/x/sys/cpu","BSD-3-Clause" -"golang.org/x/text","BSD-3-Clause" -"github.com/go-swagger/go-swagger","Apache-2.0" -"github.com/gobuffalo/httptest","MIT" -"github.com/gobuffalo/httptest/internal/takeon/github.com/ajg/form","BSD-3-Clause" -"github.com/gobuffalo/httptest/internal/takeon/github.com/markbates/hmax","MIT" -"filippo.io/edwards25519","BSD-3-Clause" -"github.com/Masterminds/semver/v3","MIT" -"github.com/aymerick/douceur","MIT" -"github.com/fatih/color","MIT" -"github.com/fatih/structs","MIT" -"github.com/go-sql-driver/mysql","MPL-2.0" -"github.com/gobuffalo/envy","MIT" -"github.com/gobuffalo/fizz","MIT" -"github.com/gobuffalo/flect","MIT" -"github.com/gobuffalo/github_flavored_markdown","MIT" -"github.com/gobuffalo/github_flavored_markdown/internal/russross/blackfriday","BSD-2-Clause" -"github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/sanitized_anchor_name","MIT" -"github.com/gobuffalo/helpers","MIT" -"github.com/gobuffalo/nulls","MIT" -"github.com/gobuffalo/plush/v4","MIT" -"github.com/gobuffalo/pop/v6","MIT" -"github.com/gobuffalo/tags/v3","MIT" -"github.com/gobuffalo/validate/v3","MIT" -"github.com/gofrs/uuid","MIT" -"github.com/gorilla/css/scanner","BSD-3-Clause" -"github.com/jackc/chunkreader/v2","MIT" -"github.com/jackc/pgconn","MIT" -"github.com/jackc/pgio","MIT" -"github.com/jackc/pgpassfile","MIT" -"github.com/jackc/pgproto3/v2","MIT" -"github.com/jackc/pgservicefile","MIT" -"github.com/jackc/pgx/v5","MIT" -"github.com/jackc/puddle/v2","MIT" -"github.com/jmoiron/sqlx","MIT" -"github.com/joho/godotenv","MIT" -"github.com/kballard/go-shellquote","MIT" -"github.com/luna-duclos/instrumentedsql","MIT" -"github.com/mattn/go-colorable","MIT" -"github.com/mattn/go-isatty","MIT" -"github.com/microcosm-cc/bluemonday","BSD-3-Clause" -"github.com/rogpeppe/go-internal/modfile","BSD-3-Clause" -"github.com/sergi/go-diff/diffmatchpatch","MIT" -"github.com/sourcegraph/annotate","BSD-3-Clause" -"github.com/sourcegraph/syntaxhighlight","BSD-3-Clause" -"golang.org/x/crypto/pbkdf2","BSD-3-Clause" -"golang.org/x/mod","BSD-3-Clause" -"golang.org/x/net/html","BSD-3-Clause" -"golang.org/x/sync","BSD-3-Clause" -"golang.org/x/sys/unix","BSD-3-Clause" -"golang.org/x/text","BSD-3-Clause" -"gopkg.in/yaml.v2","Apache-2.0" -"github.com/gofrs/uuid","MIT" -"github.com/golang-jwt/jwt/v4","MIT" -"github.com/golang-jwt/jwt/v5","MIT" -"github.com/google/go-jsonnet","Apache-2.0" -"gopkg.in/yaml.v2","Apache-2.0" -"sigs.k8s.io/yaml","MIT" -"sigs.k8s.io/yaml","BSD-3-Clause" -"github.com/gorilla/securecookie","BSD-3-Clause" -"github.com/gorilla/sessions","BSD-3-Clause" -"github.com/gtank/cryptopasta","CC0-1.0" -"golang.org/x/crypto","BSD-3-Clause" -"github.com/hashicorp/go-cleanhttp","MPL-2.0" -"github.com/hashicorp/go-retryablehttp","MPL-2.0" -"github.com/hashicorp/golang-lru/v2","MPL-2.0" -"github.com/hashicorp/golang-lru/v2/simplelru","BSD-3-Clause" -"github.com/inhies/go-bytesize","BSD-3-Clause" -"github.com/jarcoal/httpmock","MIT" -"github.com/jmoiron/sqlx","MIT" -"github.com/julienschmidt/httprouter","BSD-3-Clause" -"github.com/knadh/koanf/parsers/json","MIT" -"github.com/lestrrat-go/jwx","MIT" -"github.com/lestrrat-go/option","MIT" -"github.com/pkg/errors","BSD-2-Clause" -"github.com/lestrrat-go/jwx/v2","MIT" -"github.com/lestrrat-go/option","MIT" -"github.com/luna-duclos/instrumentedsql","MIT" -"github.com/gorilla/context","BSD-3-Clause" -"github.com/gorilla/mux","BSD-3-Clause" -"github.com/gorilla/pat","BSD-3-Clause" -"github.com/gorilla/websocket","BSD-2-Clause" -"github.com/ian-kent/envconf","MIT" -"github.com/ian-kent/go-log","MIT" -"github.com/ian-kent/goose","MIT" -"github.com/ian-kent/linkio","Unknown" -"github.com/mailhog/MailHog","MIT" -"github.com/mailhog/MailHog-Server","MIT" -"github.com/mailhog/MailHog-UI","MIT" -"github.com/mailhog/data","MIT" -"github.com/mailhog/http","MIT" -"github.com/mailhog/mhsendmail/cmd","MIT" -"github.com/mailhog/smtp","MIT" -"github.com/mailhog/storage","MIT" -"github.com/ogier/pflag","BSD-3-Clause" -"github.com/philhofer/fwd","MIT" -"github.com/t-k/fluent-logger-golang/fluent","Unknown" -"github.com/tinylib/msgp/msgp","MIT" -"golang.org/x/crypto","BSD-3-Clause" -"gopkg.in/mgo.v2","BSD-2-Clause" -"gopkg.in/mgo.v2/bson","BSD-2-Clause" -"gopkg.in/mgo.v2/internal/json","BSD-3-Clause" -"github.com/mattn/goveralls","MIT" -"golang.org/x/mod","BSD-3-Clause" -"golang.org/x/tools","BSD-3-Clause" -"github.com/mohae/deepcopy","MIT" -"github.com/montanaflynn/stats","MIT" -"github.com/nyaruka/phonenumbers","MIT" -"golang.org/x/text","BSD-3-Clause" -"google.golang.org/protobuf","BSD-3-Clause" -"github.com/ory/analytics-go/v5","MIT" -"github.com/segmentio/backo-go","MIT" -"github.com/xtgo/uuid","BSD-3-Clause" -"github.com/ory/client-go","Unknown" -"golang.org/x/oauth2","BSD-3-Clause" -"dario.cat/mergo","BSD-3-Clause" -"github.com/Nvveen/Gotty","BSD-2-Clause" -"github.com/cenkalti/backoff/v4","MIT" -"github.com/containerd/continuity/pathdriver","Apache-2.0" -"github.com/docker/cli","Apache-2.0" -"github.com/docker/docker","Apache-2.0" -"github.com/docker/go-connections/nat","Apache-2.0" -"github.com/docker/go-units","Apache-2.0" -"github.com/gogo/protobuf/proto","BSD-3-Clause" -"github.com/google/shlex","Apache-2.0" -"github.com/mitchellh/mapstructure","MIT" -"github.com/moby/docker-image-spec/specs-go/v1","Apache-2.0" -"github.com/moby/term","Apache-2.0" -"github.com/opencontainers/go-digest","Apache-2.0" -"github.com/opencontainers/image-spec/specs-go","Apache-2.0" -"github.com/opencontainers/runc/libcontainer/user","Apache-2.0" -"github.com/ory/dockertest/v3","Apache-2.0" -"github.com/ory/dockertest/v3/docker","BSD-2-Clause" -"github.com/pkg/errors","BSD-2-Clause" -"github.com/sirupsen/logrus","MIT" -"github.com/xeipuuv/gojsonpointer","Apache-2.0" -"github.com/xeipuuv/gojsonreference","Apache-2.0" -"github.com/xeipuuv/gojsonschema","Apache-2.0" -"golang.org/x/sys/unix","BSD-3-Clause" -"gopkg.in/yaml.v2","Apache-2.0" -"github.com/fsnotify/fsnotify","BSD-3-Clause" -"github.com/hashicorp/hcl","MPL-2.0" -"github.com/magiconair/properties","BSD-2-Clause" -"github.com/mitchellh/mapstructure","MIT" -"github.com/ory/go-acc","Apache-2.0" -"github.com/pelletier/go-toml/v2","MIT" -"github.com/sagikazarmark/slog-shim","BSD-3-Clause" -"github.com/spf13/afero","Apache-2.0" -"github.com/spf13/cast","MIT" -"github.com/spf13/cobra","Apache-2.0" -"github.com/spf13/pflag","BSD-3-Clause" -"github.com/spf13/viper","MIT" -"github.com/subosito/gotenv","MIT" -"golang.org/x/sys/unix","BSD-3-Clause" -"golang.org/x/text","BSD-3-Clause" -"gopkg.in/ini.v1","Apache-2.0" -"gopkg.in/yaml.v3","MIT" -"github.com/ory/graceful","Apache-2.0" -"github.com/pkg/errors","BSD-2-Clause" -"github.com/golang/protobuf/proto","BSD-3-Clause" -"github.com/ory/herodot","Apache-2.0" -"github.com/pkg/errors","BSD-2-Clause" -"golang.org/x/net","BSD-3-Clause" -"golang.org/x/sys/unix","BSD-3-Clause" -"golang.org/x/text","BSD-3-Clause" -"google.golang.org/genproto/googleapis/rpc","Apache-2.0" -"google.golang.org/grpc","Apache-2.0" -"google.golang.org/protobuf","BSD-3-Clause" -"github.com/ory/hydra-client-go/v2","Apache-2.0" -"golang.org/x/oauth2","BSD-3-Clause" -"github.com/nyaruka/phonenumbers","MIT" -"github.com/ory/jsonschema/v3","BSD-3-Clause" -"golang.org/x/text","BSD-3-Clause" -"google.golang.org/protobuf","BSD-3-Clause" -"github.com/ory/mail/v3","MIT" -"github.com/pkg/errors","BSD-2-Clause" "github.com/ory/nosurf","MIT" "github.com/ory/x","Apache-2.0" "github.com/peterhellberg/link","MIT" @@ -504,8 +504,6 @@ "github.com/tidwall/pretty","MIT" "github.com/tidwall/sjson","MIT" "github.com/wI2L/jsondiff","MIT" -"github.com/zmb3/spotify/v2","Apache-2.0" -"golang.org/x/oauth2","BSD-3-Clause" "github.com/felixge/httpsnoop","MIT" "github.com/go-logr/logr","Apache-2.0" "github.com/go-logr/stdr","Apache-2.0" @@ -529,4 +527,3 @@ "google.golang.org/genproto/googleapis/rpc/status","Apache-2.0" "google.golang.org/grpc","Apache-2.0" "google.golang.org/protobuf","BSD-3-Clause" - From 7578f00f925b853c5012f8d55e2eba592ebc23bf Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:17:41 +0000 Subject: [PATCH 146/158] chore: update repository templates to https://github.com/ory/meta/commit/44efd83ab7aab755d07b60db9049091bd8ad2533 --- .github/workflows/licenses.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 38c16511ee90..3f47b2223002 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -9,6 +9,7 @@ on: branches: - main - master + - v3 jobs: licenses: From 2c5bb21224e28d5218354349f77514f4fbe71762 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 24 Dec 2024 14:28:55 +0100 Subject: [PATCH 147/158] feat: fewer DB loads when linking credentials, add tracing --- selfservice/flow/login/hook.go | 18 +++++++++++------- selfservice/strategy/oidc/strategy.go | 15 ++++++++++++--- selfservice/strategy/oidc/strategy_settings.go | 8 +++++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/selfservice/flow/login/hook.go b/selfservice/flow/login/hook.go index 5978cb5a3b33..0ab9cd2c6198 100644 --- a/selfservice/flow/login/hook.go +++ b/selfservice/flow/login/hook.go @@ -10,7 +10,6 @@ import ( "net/url" "time" - "github.com/gofrs/uuid" "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" @@ -393,7 +392,10 @@ func (e *HookExecutor) PreLoginHook(w http.ResponseWriter, r *http.Request, a *F } // maybeLinkCredentials links the identity with the credentials of the inner context of the login flow. -func (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.Session, ident *identity.Identity, loginFlow *Flow) error { +func (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.Session, ident *identity.Identity, loginFlow *Flow) (err error) { + ctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, "HookExecutor.PostLoginHook.maybeLinkCredentials") + defer otelx.End(span, &err) + if e.checkAAL(ctx, sess, loginFlow) != nil { // we don't yet want to link credentials because the required AAL is not satisfied return nil @@ -406,7 +408,7 @@ func (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.S return nil } - if err = e.checkDuplicateCredentialsIdentifierMatch(ctx, ident.ID, lc.DuplicateIdentifier); err != nil { + if err = e.checkDuplicateCredentialsIdentifierMatch(ctx, ident, lc.DuplicateIdentifier); err != nil { return err } strategy, err := e.d.AllLoginStrategies().Strategy(lc.CredentialsType) @@ -431,11 +433,13 @@ func (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.S return nil } -func (e *HookExecutor) checkDuplicateCredentialsIdentifierMatch(ctx context.Context, identityID uuid.UUID, match string) error { - i, err := e.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, identityID) - if err != nil { - return err +func (e *HookExecutor) checkDuplicateCredentialsIdentifierMatch(ctx context.Context, i *identity.Identity, match string) error { + if len(i.Credentials) == 0 { + if err := e.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, i, identity.ExpandCredentials); err != nil { + return err + } } + for _, credentials := range i.Credentials { for _, identifier := range credentials.Identifiers { if identifier == match { diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index 06af7be1ce58..f04f06d35899 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -778,10 +778,19 @@ func (s *Strategy) processIDToken(r *http.Request, provider Provider, idToken, i return claims, nil } -func (s *Strategy) linkCredentials(ctx context.Context, i *identity.Identity, tokens *identity.CredentialsOIDCEncryptedTokens, provider, subject, organization string) error { - if err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, i, identity.ExpandCredentials); err != nil { - return err +func (s *Strategy) linkCredentials(ctx context.Context, i *identity.Identity, tokens *identity.CredentialsOIDCEncryptedTokens, provider, subject, organization string) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "strategy.oidc.linkCredentials", trace.WithAttributes( + attribute.String("provider", provider), + // attribute.String("subject", subject), // PII + attribute.String("organization", organization))) + defer otelx.End(span, &err) + + if len(i.Credentials) == 0 { + if err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, i, identity.ExpandCredentials); err != nil { + return err + } } + var conf identity.CredentialsOIDC creds, err := i.ParseCredentials(s.ID(), &conf) if errors.Is(err, herodot.ErrNotFound) { diff --git a/selfservice/strategy/oidc/strategy_settings.go b/selfservice/strategy/oidc/strategy_settings.go index dcc49f405be2..fa82ab5a1499 100644 --- a/selfservice/strategy/oidc/strategy_settings.go +++ b/selfservice/strategy/oidc/strategy_settings.go @@ -518,7 +518,10 @@ func (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWrite return err } -func (s *Strategy) Link(ctx context.Context, i *identity.Identity, credentialsConfig sqlxx.JSONRawMessage) error { +func (s *Strategy) Link(ctx context.Context, i *identity.Identity, credentialsConfig sqlxx.JSONRawMessage) (err error) { + ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.Link") + defer otelx.End(span, &err) + var credentialsOIDCConfig identity.CredentialsOIDC if err := json.Unmarshal(credentialsConfig, &credentialsOIDCConfig); err != nil { return err @@ -540,8 +543,7 @@ func (s *Strategy) Link(ctx context.Context, i *identity.Identity, credentialsCo return err } - options := []identity.ManagerOption{identity.ManagerAllowWriteProtectedTraits} - if err := s.d.IdentityManager().Update(ctx, i, options...); err != nil { + if err := s.d.IdentityManager().Update(ctx, i, identity.ManagerAllowWriteProtectedTraits); err != nil { return err } From f1349ba6a08cb358a84092ba7ffb9a2a1587f03c Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:56:00 +0000 Subject: [PATCH 148/158] autogen: update license overview --- .reports/dep-licenses.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/.reports/dep-licenses.csv b/.reports/dep-licenses.csv index 208f80a50b3e..d27071822dcf 100644 --- a/.reports/dep-licenses.csv +++ b/.reports/dep-licenses.csv @@ -527,3 +527,4 @@ "google.golang.org/genproto/googleapis/rpc/status","Apache-2.0" "google.golang.org/grpc","Apache-2.0" "google.golang.org/protobuf","BSD-3-Clause" + From 73045728036eaeb8c6a527c332f7c8af2ead3a30 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 2 Jan 2025 13:55:57 +0000 Subject: [PATCH 149/158] chore: update repository templates to https://github.com/ory/meta/commit/83e71e6e97a5eab38bede33eceef40d550d1fe6e --- .github/workflows/licenses.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 3f47b2223002..4d9965010970 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -8,8 +8,8 @@ on: push: branches: - main - - master - v3 + - master jobs: licenses: From 25b862b0193dabee308f25d4d05ba06c9696ac5c Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:04:41 +0000 Subject: [PATCH 150/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae680f41bfa..c9b67d734fab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2024-12-31)](#2024-12-31) +- [ (2025-01-02)](#2025-01-02) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2024-12-31) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2025-01-02) ## Breaking Changes @@ -532,6 +532,8 @@ https://github.com/ory-corp/cloud/issues/7176 - Fast add credential type lookups ([#4177](https://github.com/ory/kratos/issues/4177)) ([eeb1355](https://github.com/ory/kratos/commit/eeb13552118504f17b48f2c7e002e777f5ee73f4)) +- Fewer DB loads when linking credentials, add tracing + ([2c5bb21](https://github.com/ory/kratos/commit/2c5bb21224e28d5218354349f77514f4fbe71762)) - Gracefully handle failing password rehashing during login ([#4235](https://github.com/ory/kratos/issues/4235)) ([3905787](https://github.com/ory/kratos/commit/39057879821b387b49f5d4f7cb19b9e02ec924a7)): From 9bc83a410b8de9d649b6393f136889dd14098b0d Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Mon, 6 Jan 2025 11:13:35 +0100 Subject: [PATCH 151/158] fix: add resend node to after registration verification flow (#4260) --- selfservice/hook/verification.go | 8 ++++++++ selfservice/hook/verification_test.go | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/selfservice/hook/verification.go b/selfservice/hook/verification.go index 6fdd039c146f..a96bacf331ac 100644 --- a/selfservice/hook/verification.go +++ b/selfservice/hook/verification.go @@ -17,6 +17,7 @@ import ( "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/selfservice/flow/verification" "github.com/ory/kratos/session" + "github.com/ory/kratos/text" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" "github.com/ory/x/otelx" @@ -137,6 +138,13 @@ func (e *Verifier) do( return err } + if address.Value != "" && address.Via == identity.VerifiableAddressTypeEmail { + verificationFlow.UI.Nodes.Append( + node.NewInputField(address.Via, address.Value, node.CodeGroup, node.InputAttributeTypeSubmit). + WithMetaLabel(text.NewInfoNodeResendOTP()), + ) + } + if err := e.r.VerificationFlowPersister().CreateVerificationFlow(ctx, verificationFlow); err != nil { return err } diff --git a/selfservice/hook/verification_test.go b/selfservice/hook/verification_test.go index 5e815fa4d4a4..40de354bc518 100644 --- a/selfservice/hook/verification_test.go +++ b/selfservice/hook/verification_test.go @@ -86,6 +86,7 @@ func TestVerifier(t *testing.T) { expectedVerificationFlow, err := reg.VerificationFlowPersister().GetVerificationFlow(ctx, fView.ID) require.NoError(t, err) require.Equal(t, expectedVerificationFlow.State, flow.StateEmailSent) + require.NotNil(t, expectedVerificationFlow.UI.Nodes.Find("email")) messages, err := reg.CourierPersister().NextMessages(context.Background(), 12) require.NoError(t, err) @@ -133,7 +134,7 @@ func TestVerifier(t *testing.T) { require.Len(t, messages, 0) }) - t.Run("name=register", func(t *testing.T) { + t.Run("name=settings", func(t *testing.T) { t.Parallel() conf, reg := internal.NewFastRegistryWithMocks(t) testhelpers.SetDefaultIdentitySchema(conf, "file://./stub/verify.schema.json") From 23f323274616c7383668ecb3c405416b8a0e30f0 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 6 Jan 2025 10:16:39 +0000 Subject: [PATCH 152/158] autogen: update license overview --- .reports/dep-licenses.csv | 113 -------------------------------------- 1 file changed, 113 deletions(-) diff --git a/.reports/dep-licenses.csv b/.reports/dep-licenses.csv index d27071822dcf..0cc925e42922 100644 --- a/.reports/dep-licenses.csv +++ b/.reports/dep-licenses.csv @@ -1,117 +1,4 @@ "module name","licenses" -"@lukeed/csprng@1.1.0","MIT" -"@nestjs/axios@0.1.0","MIT" -"@nestjs/common@9.3.11","MIT" -"@nestjs/core@9.3.11","MIT" -"@nuxtjs/opencollective@0.3.2","MIT" -"@openapitools/openapi-generator-cli@2.7.0","Apache-2.0" -"ansi-escapes@4.3.2","MIT" -"ansi-regex@5.0.1","MIT" -"ansi-styles@4.3.0","MIT" -"argparse@1.0.10","MIT" -"asynckit@0.4.0","MIT" -"axios@0.27.2","MIT" -"balanced-match@1.0.2","MIT" -"base64-js@1.5.1","MIT" -"bl@4.1.0","MIT" -"brace-expansion@1.1.11","MIT" -"buffer@5.7.1","MIT" -"chalk@4.1.2","MIT" -"chardet@0.7.0","MIT" -"cli-cursor@3.1.0","MIT" -"cli-spinners@2.8.0","MIT" -"cli-width@3.0.0","ISC" -"cliui@7.0.4","ISC" -"clone@1.0.4","MIT" -"color-convert@2.0.1","MIT" -"color-name@1.1.4","MIT" -"combined-stream@1.0.8","MIT" -"commander@8.3.0","MIT" -"compare-versions@4.1.4","MIT" -"concat-map@0.0.1","MIT" -"concurrently@6.5.1","MIT" -"consola@2.15.3","MIT" -"console.table@0.10.0","MIT" -"date-fns@2.28.0","MIT" -"defaults@1.0.3","MIT" -"delayed-stream@1.0.0","MIT" -"easy-table@1.1.0","MIT" -"emoji-regex@8.0.0","MIT" -"escalade@3.1.1","MIT" -"escape-string-regexp@1.0.5","MIT" -"external-editor@3.1.0","MIT" -"fast-safe-stringify@2.1.1","MIT" -"figures@3.2.0","MIT" -"follow-redirects@1.15.4","MIT" -"form-data@4.0.0","MIT" -"fs-extra@10.1.0","MIT" -"fs.realpath@1.0.0","ISC" -"get-caller-file@2.0.5","ISC" -"glob@7.1.6","ISC" -"graceful-fs@4.2.10","ISC" -"has-flag@4.0.0","MIT" -"iconv-lite@0.4.24","MIT" -"ieee754@1.2.1","BSD-3-Clause" -"inflight@1.0.6","ISC" -"inherits@2.0.4","ISC" -"inquirer@8.2.5","MIT" -"is-fullwidth-code-point@3.0.0","MIT" -"is-interactive@1.0.0","MIT" -"is-unicode-supported@0.1.0","MIT" -"iterare@1.2.1","ISC" -"jsonfile@6.1.0","MIT" -"lodash@4.17.21","MIT" -"log-symbols@4.1.0","MIT" -"mime-db@1.52.0","MIT" -"mime-types@2.1.35","MIT" -"mimic-fn@2.1.0","MIT" -"minimatch@3.1.2","ISC" -"mute-stream@0.0.8","ISC" -"node-fetch@2.6.9","MIT" -"once@1.4.0","ISC" -"onetime@5.1.2","MIT" -"ora@5.4.1","MIT" -"os-tmpdir@1.0.2","MIT" -"path-is-absolute@1.0.1","MIT" -"path-to-regexp@3.2.0","MIT" -"readable-stream@3.6.2","MIT" -"reflect-metadata@0.1.13","Apache-2.0" -"require-directory@2.1.1","MIT" -"restore-cursor@3.1.0","MIT" -"run-async@2.4.1","MIT" -"rxjs@6.6.7","Apache-2.0" -"rxjs@7.8.0","Apache-2.0" -"safe-buffer@5.2.1","MIT" -"safer-buffer@2.1.2","MIT" -"signal-exit@3.0.7","ISC" -"spawn-command@0.0.2-1","MIT" -"sprintf-js@1.0.3","BSD-3-Clause" -"string-width@4.2.3","MIT" -"string_decoder@1.3.0","MIT" -"strip-ansi@6.0.1","MIT" -"supports-color@7.2.0","MIT" -"supports-color@8.1.1","MIT" -"through@2.3.8","MIT" -"tmp@0.0.33","MIT" -"tr46@0.0.3","MIT" -"tree-kill@1.2.2","MIT" -"tslib@1.14.1","0BSD" -"tslib@2.0.3","0BSD" -"tslib@2.4.0","0BSD" -"tslib@2.5.0","0BSD" -"type-fest@0.21.3","(MIT OR CC0-1.0)" -"uid@2.0.1","MIT" -"universalify@2.0.0","MIT" -"util-deprecate@1.0.2","MIT" -"wcwidth@1.0.1","MIT" -"webidl-conversions@3.0.1","BSD-2-Clause" -"whatwg-url@5.0.0","MIT" -"wrap-ansi@7.0.0","MIT" -"wrappy@1.0.2","ISC" -"y18n@5.0.8","ISC" -"yamljs@0.3.0","MIT" -"yargs-parser@20.2.9","ISC" -"yargs@16.2.0","MIT" "dario.cat/mergo","BSD-3-Clause" "github.com/arbovm/levenshtein","BSD-3-Clause" From b95fd3fa723521807824cad84e4a9ce812172311 Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Fri, 10 Jan 2025 14:46:09 +0100 Subject: [PATCH 153/158] fix: don't show oidc subject in login hints (#4264) --- identity/manager.go | 62 +++++++++++++-------------- identity/manager_test.go | 14 +++--- selfservice/flow/registration/hook.go | 2 +- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/identity/manager.go b/identity/manager.go index a09a08a778cd..74c984eb13af 100644 --- a/identity/manager.go +++ b/identity/manager.go @@ -10,6 +10,7 @@ import ( "reflect" "slices" "sort" + "strings" "github.com/ory/kratos/schema" "github.com/ory/x/sqlcon" @@ -102,7 +103,7 @@ func (m *Manager) Create(ctx context.Context, i *Identity, opts ...ManagerOption return nil } -func (m *Manager) ConflictingIdentity(ctx context.Context, i *Identity) (found *Identity, foundConflictAddress string, err error) { +func (m *Manager) ConflictingIdentity(ctx context.Context, i *Identity) (found *Identity, foundConflictAddress string, conflictAddressType string, err error) { for ct, cred := range i.Credentials { for _, id := range cred.Identifiers { found, _, err = m.r.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, ct, id) @@ -112,10 +113,10 @@ func (m *Manager) ConflictingIdentity(ctx context.Context, i *Identity) (found * // FindByCredentialsIdentifier does not expand identity credentials. if err = m.r.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, found, ExpandCredentials); err != nil { - return nil, "", err + return nil, "", "", err } - return found, id, nil + return found, id, ct.String(), nil } } @@ -125,16 +126,16 @@ func (m *Manager) ConflictingIdentity(ctx context.Context, i *Identity) (found * if errors.Is(err, sqlcon.ErrNoRows) { continue } else if err != nil { - return nil, "", err + return nil, "", "", err } foundConflictAddress = conflictingAddress.Value found, err = m.r.PrivilegedIdentityPool().GetIdentity(ctx, conflictingAddress.IdentityID, ExpandCredentials) if err != nil { - return nil, "", err + return nil, "", "", err } - return found, foundConflictAddress, nil + return found, foundConflictAddress, va.Via, nil } // Last option: check the recovery address @@ -143,19 +144,19 @@ func (m *Manager) ConflictingIdentity(ctx context.Context, i *Identity) (found * if errors.Is(err, sqlcon.ErrNoRows) { continue } else if err != nil { - return nil, "", err + return nil, "", "", err } foundConflictAddress = conflictingAddress.Value found, err = m.r.PrivilegedIdentityPool().GetIdentity(ctx, conflictingAddress.IdentityID, ExpandCredentials) if err != nil { - return nil, "", err + return nil, "", "", err } - return found, foundConflictAddress, nil + return found, foundConflictAddress, string(va.Via), nil } - return nil, "", sqlcon.ErrNoRows + return nil, "", "", sqlcon.ErrNoRows } func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identity) (err error) { @@ -163,7 +164,7 @@ func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identi return &ErrDuplicateCredentials{error: e} } - found, foundConflictAddress, err := m.ConflictingIdentity(ctx, i) + found, foundConflictAddress, conflictingAddressType, err := m.ConflictingIdentity(ctx, i) if err != nil { if errors.Is(err, sqlcon.ErrNoRows) { return &ErrDuplicateCredentials{error: e} @@ -181,6 +182,11 @@ func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identi }) duplicateCredErr := &ErrDuplicateCredentials{error: e} + // OIDC credentials are not email addresses but the sub claim from the OIDC provider. + // This is useless for the user, so in that case, we don't set the identifier hint. + if conflictingAddressType != CredentialsTypeOIDC.String() { + duplicateCredErr.SetIdentifierHint(strings.Trim(foundConflictAddress, " ")) + } for _, cred := range creds { if cred.Config == nil { @@ -192,11 +198,9 @@ func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identi // in to the first factor (obviously). switch cred.Type { case CredentialsTypePassword: - identifierHint := foundConflictAddress - if len(cred.Identifiers) > 0 { - identifierHint = cred.Identifiers[0] + if duplicateCredErr.IdentifierHint() == "" && len(cred.Identifiers) == 1 { + duplicateCredErr.SetIdentifierHint(cred.Identifiers[0]) } - duplicateCredErr.SetIdentifierHint(identifierHint) var cfg CredentialsPassword if err := json.Unmarshal(cred.Config, &cfg); err != nil { @@ -209,14 +213,7 @@ func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identi } duplicateCredErr.AddCredentialsType(cred.Type) - case CredentialsTypeCodeAuth: - identifierHint := foundConflictAddress - if len(cred.Identifiers) > 0 { - identifierHint = cred.Identifiers[0] - } - - duplicateCredErr.SetIdentifierHint(identifierHint) duplicateCredErr.AddCredentialsType(cred.Type) case CredentialsTypeOIDC: var cfg CredentialsOIDC @@ -230,7 +227,6 @@ func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identi } duplicateCredErr.AddCredentialsType(cred.Type) - duplicateCredErr.SetIdentifierHint(foundConflictAddress) duplicateCredErr.availableOIDCProviders = available case CredentialsTypeWebAuthn: var cfg CredentialsWebAuthnConfig @@ -238,15 +234,12 @@ func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identi return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to JSON decode identity credentials %s for identity %s.", cred.Type, found.ID)) } - identifierHint := foundConflictAddress - if len(cred.Identifiers) > 0 { - identifierHint = cred.Identifiers[0] + if duplicateCredErr.IdentifierHint() == "" && len(cred.Identifiers) == 1 { + duplicateCredErr.SetIdentifierHint(cred.Identifiers[0]) } - for _, webauthn := range cfg.Credentials { if webauthn.IsPasswordless { duplicateCredErr.AddCredentialsType(cred.Type) - duplicateCredErr.SetIdentifierHint(identifierHint) break } } @@ -256,15 +249,12 @@ func (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identi return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to JSON decode identity credentials %s for identity %s.", cred.Type, found.ID)) } - identifierHint := foundConflictAddress - if len(cred.Identifiers) > 0 { - identifierHint = cred.Identifiers[0] + if duplicateCredErr.IdentifierHint() == "" && len(cred.Identifiers) == 1 { + duplicateCredErr.SetIdentifierHint(cred.Identifiers[0]) } - for _, webauthn := range cfg.Credentials { if webauthn.IsPasswordless { duplicateCredErr.AddCredentialsType(cred.Type) - duplicateCredErr.SetIdentifierHint(identifierHint) break } } @@ -343,6 +333,7 @@ func (e *CreateIdentitiesError) Error() string { e.init() return fmt.Sprintf("create identities error: %d identities failed", len(e.failedIdentities)) } + func (e *CreateIdentitiesError) Unwrap() []error { e.init() var errs []error @@ -356,17 +347,20 @@ func (e *CreateIdentitiesError) AddFailedIdentity(ident *Identity, err *herodot. e.init() e.failedIdentities[ident] = err } + func (e *CreateIdentitiesError) Merge(other *CreateIdentitiesError) { e.init() for k, v := range other.failedIdentities { e.failedIdentities[k] = v } } + func (e *CreateIdentitiesError) Contains(ident *Identity) bool { e.init() _, found := e.failedIdentities[ident] return found } + func (e *CreateIdentitiesError) Find(ident *Identity) *FailedIdentity { e.init() if err, found := e.failedIdentities[ident]; found { @@ -375,12 +369,14 @@ func (e *CreateIdentitiesError) Find(ident *Identity) *FailedIdentity { return nil } + func (e *CreateIdentitiesError) ErrOrNil() error { if e == nil || len(e.failedIdentities) == 0 { return nil } return e } + func (e *CreateIdentitiesError) init() { if e.failedIdentities == nil { e.failedIdentities = map[*Identity]*herodot.DefaultError{} diff --git a/identity/manager_test.go b/identity/manager_test.go index b7659eba68a1..3e1efaff0673 100644 --- a/identity/manager_test.go +++ b/identity/manager_test.go @@ -282,6 +282,7 @@ func TestManager(t *testing.T) { assert.ErrorAs(t, err, &verr) assert.ElementsMatch(t, []string{"oidc"}, verr.AvailableCredentials()) assert.ElementsMatch(t, []string{"google", "github"}, verr.AvailableOIDCProviders()) + // The conflicting identifier is the oidc subject, which is not useful for the user assert.Equal(t, email, verr.IdentifierHint()) }) @@ -756,7 +757,7 @@ func TestManager(t *testing.T) { require.NoError(t, reg.IdentityManager().Create(ctx, conflicOnRecoveryAddress)) t.Run("case=returns not found if no conflict", func(t *testing.T) { - found, foundConflictAddress, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ + found, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ Credentials: map[identity.CredentialsType]identity.Credentials{ identity.CredentialsTypePassword: {Identifiers: []string{"no-conflict@example.com"}}, }, @@ -764,10 +765,11 @@ func TestManager(t *testing.T) { assert.ErrorIs(t, err, sqlcon.ErrNoRows) assert.Nil(t, found) assert.Empty(t, foundConflictAddress) + assert.Empty(t, addressType) }) t.Run("case=conflict on identifier", func(t *testing.T) { - found, foundConflictAddress, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ + found, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ Credentials: map[identity.CredentialsType]identity.Credentials{ identity.CredentialsTypePassword: {Identifiers: []string{"conflict-on-identifier@example.com"}}, }, @@ -775,10 +777,11 @@ func TestManager(t *testing.T) { require.NoError(t, err) assert.Equal(t, conflicOnIdentifier.ID, found.ID) assert.Equal(t, "conflict-on-identifier@example.com", foundConflictAddress) + assert.EqualValues(t, string(identity.CredentialsTypePassword), addressType) }) t.Run("case=conflict on verifiable address", func(t *testing.T) { - found, foundConflictAddress, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ + found, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ VerifiableAddresses: []identity.VerifiableAddress{{ Value: "conflict-on-va@example.com", Via: "email", @@ -787,10 +790,10 @@ func TestManager(t *testing.T) { require.NoError(t, err) assert.Equal(t, conflicOnVerifiableAddress.ID, found.ID) assert.Equal(t, "conflict-on-va@example.com", foundConflictAddress) + assert.Equal(t, "email", addressType) }) - t.Run("case=conflict on recovery address", func(t *testing.T) { - found, foundConflictAddress, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ + found, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(ctx, &identity.Identity{ RecoveryAddresses: []identity.RecoveryAddress{{ Value: "conflict-on-ra@example.com", Via: "email", @@ -799,6 +802,7 @@ func TestManager(t *testing.T) { require.NoError(t, err) assert.Equal(t, conflicOnRecoveryAddress.ID, found.ID) assert.Equal(t, "conflict-on-ra@example.com", foundConflictAddress) + assert.Equal(t, "email", addressType) }) }) } diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index ab7400b60936..d53e1ffcb047 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -330,7 +330,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque } func (e *HookExecutor) getDuplicateIdentifier(ctx context.Context, i *identity.Identity) (string, error) { - _, id, err := e.d.IdentityManager().ConflictingIdentity(ctx, i) + _, id, _, err := e.d.IdentityManager().ConflictingIdentity(ctx, i) if err != nil { return "", err } From 906f6c8fdf9ec0834993a44f8a19697b38dd63d2 Mon Sep 17 00:00:00 2001 From: Henning Perl Date: Fri, 10 Jan 2025 15:17:25 +0100 Subject: [PATCH 154/158] fix: stricter JSON patch checking for PATCH identities (#4263) --- go.mod | 2 +- go.sum | 2 ++ identity/handler.go | 2 +- identity/handler_test.go | 51 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index deba6fbdf766..325a5d447573 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.675 + github.com/ory/x v0.0.689 github.com/peterhellberg/link v1.2.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 5f0133b6a64f..c6f36f82358f 100644 --- a/go.sum +++ b/go.sum @@ -642,6 +642,8 @@ github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpi github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/ory/x v0.0.675 h1:K6GpVo99BXBFv2UiwMjySNNNqCFKGswynrt7vWQJFU8= github.com/ory/x v0.0.675/go.mod h1:zJmnDtKje2FCP4EeFvRsKk94XXiqKCSGJMZcirAfhUs= +github.com/ory/x v0.0.689 h1:pMXmnw2aoHiq4jRX9xtGXqX+VU3USEwlUUbwNCxmiZQ= +github.com/ory/x v0.0.689/go.mod h1:UpPgjobuyIyHh1pG4LxqmfMpuNOnzf2BzwyouwBeCk4= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= diff --git a/identity/handler.go b/identity/handler.go index 6d458636854d..938344fb48fc 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -916,7 +916,7 @@ func (h *Handler) patch(w http.ResponseWriter, r *http.Request, ps httprouter.Pa patchedIdentity := WithAdminMetadataInJSON(*identity) - if err := jsonx.ApplyJSONPatch(requestBody, &patchedIdentity, "/id", "/stateChangedAt", "/credentials"); err != nil { + if err := jsonx.ApplyJSONPatch(requestBody, &patchedIdentity, "/id", "/stateChangedAt", "/credentials", "/credentials/**"); err != nil { h.r.Writer().WriteError(w, r, errors.WithStack( herodot. ErrBadRequest. diff --git a/identity/handler_test.go b/identity/handler_test.go index 66f4936961f3..2d002b9ee056 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -1203,6 +1203,57 @@ func TestHandler(t *testing.T) { } }) + t.Run("case=PATCH should fail if credential orgs are updated", func(t *testing.T) { + uuid := x.NewUUID().String() + email := uuid + "@ory.sh" + i := &identity.Identity{Traits: identity.Traits(`{"email":"` + email + `"}`)} + i.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{ + Type: identity.CredentialsTypeOIDC, + Identifiers: []string{email}, + Config: sqlxx.JSONRawMessage(`{"providers": [{"provider": "some-provider"}]}`), + }) + require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i)) + + for name, ts := range map[string]*httptest.Server{"public": publicTS, "admin": adminTS} { + t.Run("endpoint="+name, func(t *testing.T) { + patch := []patch{ + {"op": "replace", "path": "/credentials/oidc/config/providers/0/organization", "value": "foo"}, + } + + res := send(t, ts, "PATCH", "/identities/"+i.ID.String(), http.StatusBadRequest, &patch) + + assert.EqualValues(t, "patch includes denied path: /credentials/oidc/config/providers/0/organization", res.Get("error.message").String(), "%s", res.Raw) + }) + } + }) + + t.Run("case=PATCH should fail to update credential password", func(t *testing.T) { + uuid := x.NewUUID().String() + email := uuid + "@ory.sh" + password := "ljanf123akf" + p, err := reg.Hasher(ctx).Generate(context.Background(), []byte(password)) + require.NoError(t, err) + i := &identity.Identity{Traits: identity.Traits(`{"email":"` + email + `"}`)} + i.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{ + Type: identity.CredentialsTypePassword, + Identifiers: []string{email}, + Config: sqlxx.JSONRawMessage(`{"hashed_password":"` + string(p) + `"}`), + }) + require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i)) + + for name, ts := range map[string]*httptest.Server{"public": publicTS, "admin": adminTS} { + t.Run("endpoint="+name, func(t *testing.T) { + patch := []patch{ + {"op": "replace", "path": "/credentials/password/config/hashed_password", "value": "foo"}, + } + + res := send(t, ts, "PATCH", "/identities/"+i.ID.String(), http.StatusBadRequest, &patch) + + assert.EqualValues(t, "patch includes denied path: /credentials/password/config/hashed_password", res.Get("error.message").String(), "%s", res.Raw) + }) + } + }) + t.Run("case=PATCH should not invalidate credentials ory/cloud#148", func(t *testing.T) { // see https://github.com/ory/cloud/issues/148 From 57fef20a6a78fb8dc951dd2b62c25d46e8286e4d Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:09:31 +0000 Subject: [PATCH 155/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b67d734fab..87c9786b02bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2025-01-02)](#2025-01-02) +- [ (2025-01-10)](#2025-01-10) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2025-01-02) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2025-01-10) ## Breaking Changes @@ -366,12 +366,18 @@ https://github.com/ory-corp/cloud/issues/7176 - Add missing autocomplete attributes to identifier_first strategy ([#4215](https://github.com/ory/kratos/issues/4215)) ([e1f29c2](https://github.com/ory/kratos/commit/e1f29c2d3524f9444ec067c52d2c9f1d44fa6539)) +- Add resend node to after registration verification flow + ([#4260](https://github.com/ory/kratos/issues/4260)) + ([9bc83a4](https://github.com/ory/kratos/commit/9bc83a410b8de9d649b6393f136889dd14098b0d)) - Cancel conditional passkey before trying again ([#4247](https://github.com/ory/kratos/issues/4247)) ([d9f6f75](https://github.com/ory/kratos/commit/d9f6f75b6a43aad996f6390f73616a2cf596c6e4)) - Do not roll back transaction on partial identity insert error ([#4211](https://github.com/ory/kratos/issues/4211)) ([82660f0](https://github.com/ory/kratos/commit/82660f04e2f33d0aa86fccee42c90773a901d400)) +- Don't show oidc subject in login hints + ([#4264](https://github.com/ory/kratos/issues/4264)) + ([b95fd3f](https://github.com/ory/kratos/commit/b95fd3fa723521807824cad84e4a9ce812172311)) - Duplicate autocomplete trigger ([6bbf915](https://github.com/ory/kratos/commit/6bbf91593a37e4973a86f610290ebab44df8dc81)) - Enable b2b_sso hook in more places @@ -415,6 +421,9 @@ https://github.com/ory-corp/cloud/issues/7176 - Span names ([#4232](https://github.com/ory/kratos/issues/4232)) ([dbae98a](https://github.com/ory/kratos/commit/dbae98a26b8e2a3328d8510745ddb58c18b7ad3d)) +- Stricter JSON patch checking for PATCH identities + ([#4263](https://github.com/ory/kratos/issues/4263)) + ([906f6c8](https://github.com/ory/kratos/commit/906f6c8fdf9ec0834993a44f8a19697b38dd63d2)) - Truncate updated at ([#4149](https://github.com/ory/kratos/issues/4149)) ([2f8aaee](https://github.com/ory/kratos/commit/2f8aaee0716835caaba0dff9b6cc457c2cdff5d4)) - Use context for readiness probes From 3622cd5f588721fa84eb138079347f40e480b946 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:02:00 +0000 Subject: [PATCH 156/158] chore: update repository templates to https://github.com/ory/meta/commit/e54ac5d59869341cc3ffb2e58fd8b0cba28ec7f7 --- .github/workflows/cve-scan.yaml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/cve-scan.yaml b/.github/workflows/cve-scan.yaml index b8aa0197182a..70e5a28e937f 100644 --- a/.github/workflows/cve-scan.yaml +++ b/.github/workflows/cve-scan.yaml @@ -31,14 +31,7 @@ jobs: SHA_SHORT=$(git rev-parse --short HEAD) REPO_NAME=${{ github.event.repository.name }} - # Append -sqlite to SHA_SHORT if repo is hydra - if [ "${REPO_NAME}" = "hydra" ]; then - echo "Repo is hydra, appending -sqlite to SHA_SHORT" - IMAGE_NAME="oryd/${REPO_NAME}:${SHA_SHORT}-sqlite" - else - echo "Repo is not hydra, using default IMAGE_NAME" - IMAGE_NAME="oryd/${REPO_NAME}:${SHA_SHORT}" - fi + IMAGE_NAME="oryd/${REPO_NAME}:${SHA_SHORT}" # Output values for debugging echo "Values to be set:" From 44eb305cf91672798f7d57550a026c6b970f7566 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:02:19 +0100 Subject: [PATCH 157/158] fix: add missing saml group (#4268) --- internal/client-go/model_ui_node.go | 2 +- internal/httpclient/model_ui_node.go | 2 +- spec/api.json | 7 ++++--- spec/swagger.json | 7 ++++--- ui/node/node.go | 1 + 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/internal/client-go/model_ui_node.go b/internal/client-go/model_ui_node.go index 84b728514114..b83ec542c65a 100644 --- a/internal/client-go/model_ui_node.go +++ b/internal/client-go/model_ui_node.go @@ -18,7 +18,7 @@ import ( // UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `` tag, or an `` but also `some plain text`. type UiNode struct { Attributes UiNodeAttributes `json:"attributes"` - // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup + // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup saml SAMLGroup Group string `json:"group"` Messages []UiText `json:"messages"` Meta UiNodeMeta `json:"meta"` diff --git a/internal/httpclient/model_ui_node.go b/internal/httpclient/model_ui_node.go index 84b728514114..b83ec542c65a 100644 --- a/internal/httpclient/model_ui_node.go +++ b/internal/httpclient/model_ui_node.go @@ -18,7 +18,7 @@ import ( // UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `` tag, or an `` but also `some plain text`. type UiNode struct { Attributes UiNodeAttributes `json:"attributes"` - // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup + // Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup saml SAMLGroup Group string `json:"group"` Messages []UiText `json:"messages"` Meta UiNodeMeta `json:"meta"` diff --git a/spec/api.json b/spec/api.json index 6f31f07b7172..210e6cca8592 100644 --- a/spec/api.json +++ b/spec/api.json @@ -2231,7 +2231,7 @@ "$ref": "#/components/schemas/uiNodeAttributes" }, "group": { - "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup", + "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup\nsaml SAMLGroup", "enum": [ "default", "password", @@ -2244,10 +2244,11 @@ "webauthn", "passkey", "identifier_first", - "captcha" + "captcha", + "saml" ], "type": "string", - "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup" + "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup\nsaml SAMLGroup" }, "messages": { "$ref": "#/components/schemas/uiTexts" diff --git a/spec/swagger.json b/spec/swagger.json index 38c8d2d8555e..f2f4f05ab25b 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -5349,7 +5349,7 @@ "$ref": "#/definitions/uiNodeAttributes" }, "group": { - "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup", + "description": "Group specifies which group (e.g. password authenticator) this node belongs to.\ndefault DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup\nsaml SAMLGroup", "type": "string", "enum": [ "default", @@ -5363,9 +5363,10 @@ "webauthn", "passkey", "identifier_first", - "captcha" + "captcha", + "saml" ], - "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup" + "x-go-enum-desc": "default DefaultGroup\npassword PasswordGroup\noidc OpenIDConnectGroup\nprofile ProfileGroup\nlink LinkGroup\ncode CodeGroup\ntotp TOTPGroup\nlookup_secret LookupGroup\nwebauthn WebAuthnGroup\npasskey PasskeyGroup\nidentifier_first IdentifierFirstGroup\ncaptcha CaptchaGroup\nsaml SAMLGroup" }, "messages": { "$ref": "#/definitions/uiTexts" diff --git a/ui/node/node.go b/ui/node/node.go index 7d9137db20ff..b8e50e39bb84 100644 --- a/ui/node/node.go +++ b/ui/node/node.go @@ -51,6 +51,7 @@ const ( PasskeyGroup UiNodeGroup = "passkey" IdentifierFirstGroup UiNodeGroup = "identifier_first" CaptchaGroup UiNodeGroup = "captcha" // Available in OEL + SAMLGroup UiNodeGroup = "saml" // Available in OEL ) func (g UiNodeGroup) String() string { From c9fe7d60bbf6fa9c058ef36b6e8b08aaa8228dab Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:57:28 +0000 Subject: [PATCH 158/158] autogen(docs): regenerate and update changelog [skip ci] --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87c9786b02bd..f3f2eefd11d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Table of Contents** -- [ (2025-01-10)](#2025-01-10) +- [ (2025-01-15)](#2025-01-15) - [Breaking Changes](#breaking-changes) - [Bug Fixes](#bug-fixes) - [Code Refactoring](#code-refactoring) @@ -339,7 +339,7 @@ -# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2025-01-10) +# [](https://github.com/ory/kratos/compare/v1.3.0...v) (2025-01-15) ## Breaking Changes @@ -366,6 +366,8 @@ https://github.com/ory-corp/cloud/issues/7176 - Add missing autocomplete attributes to identifier_first strategy ([#4215](https://github.com/ory/kratos/issues/4215)) ([e1f29c2](https://github.com/ory/kratos/commit/e1f29c2d3524f9444ec067c52d2c9f1d44fa6539)) +- Add missing saml group ([#4268](https://github.com/ory/kratos/issues/4268)) + ([44eb305](https://github.com/ory/kratos/commit/44eb305cf91672798f7d57550a026c6b970f7566)) - Add resend node to after registration verification flow ([#4260](https://github.com/ory/kratos/issues/4260)) ([9bc83a4](https://github.com/ory/kratos/commit/9bc83a410b8de9d649b6393f136889dd14098b0d))