From acd12ce9ddadb43462505c8fc7f31ff02e2ddc88 Mon Sep 17 00:00:00 2001 From: Gani Georgiev Date: Wed, 15 Oct 2025 17:25:43 +0300 Subject: [PATCH] [#7256] fixed legacy identitity field priority check when a username is a valid email address --- CHANGELOG.md | 5 +++ apis/record_auth_with_password.go | 37 ++++++++++++------- apis/record_auth_with_password_test.go | 51 ++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb4a463a..75cd9d30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## v0.30.3 (WIP) + +- Fixed legacy identitity field priority check when a username is a valid email address ([#7256](https://github.com/pocketbase/pocketbase/issues/7256)). + + ## v0.30.2 - Bumped min Go GitHub action version to 1.24.8 since it comes with some [minor security fixes](https://github.com/golang/go/issues?q=milestone%3AGo1.24.8+label%3ACherryPickApproved). diff --git a/apis/record_auth_with_password.go b/apis/record_auth_with_password.go index c28f911d..7a52ff5c 100644 --- a/apis/record_auth_with_password.go +++ b/apis/record_auth_with_password.go @@ -40,23 +40,32 @@ func recordAuthWithPassword(e *core.RequestEvent) error { if form.IdentityField != "" { foundRecord, foundErr = findRecordByIdentityField(e.App, collection, form.IdentityField, form.Identity) } else { - // prioritize email lookup - isEmail := is.EmailFormat.Validate(form.Identity) == nil - if isEmail && list.ExistInSlice(core.FieldNameEmail, collection.PasswordAuth.IdentityFields) { - foundRecord, foundErr = findRecordByIdentityField(e.App, collection, core.FieldNameEmail, form.Identity) + identityFields := collection.PasswordAuth.IdentityFields + + // @todo consider removing with the stable release or moving it in the collection save + // + // prioritize email lookup to minimize breaking changes with earlier versions + if len(identityFields) > 1 && identityFields[0] != core.FieldNameEmail { + identityFields = slices.Clone(identityFields) + slices.SortStableFunc(identityFields, func(a, b string) int { + if a == "email" { + return -1 + } + if b == "email" { + return 1 + } + return 0 + }) } - // search by the other identity fields - if !isEmail || foundErr != nil { - for _, name := range collection.PasswordAuth.IdentityFields { - if !isEmail && name == core.FieldNameEmail { - continue // no need to search by the email field if it is not an email - } + for _, name := range identityFields { + if name == core.FieldNameEmail && is.EmailFormat.Validate(form.Identity) != nil { + continue // no need to query the database if we know that the submitted value is not an email + } - foundRecord, foundErr = findRecordByIdentityField(e.App, collection, name, form.Identity) - if foundErr == nil { - break - } + foundRecord, foundErr = findRecordByIdentityField(e.App, collection, name, form.Identity) + if foundErr == nil { + break } } } diff --git a/apis/record_auth_with_password_test.go b/apis/record_auth_with_password_test.go index 8ed3aea6..37f7ce62 100644 --- a/apis/record_auth_with_password_test.go +++ b/apis/record_auth_with_password_test.go @@ -212,6 +212,57 @@ func TestRecordAuthWithPassword(t *testing.T) { "OnMailerRecordAuthAlertSend": 1, }, }, + { + // https://github.com/pocketbase/pocketbase/issues/7256 + Name: "valid non-email identity field with a value that is a properly formatted email", + Method: http.MethodPost, + URL: "/api/collections/clients/auth-with-password", + Body: strings.NewReader(`{ + "identity":"username_as_email@example.com", + "password":"1234567890" + }`), + BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) { + record, err := app.FindAuthRecordByEmail("clients", "test@example.com") + if err != nil { + t.Fatal(err) + } + + record.Set("username", "username_as_email@example.com") + + err = app.SaveNoValidate(record) + if err != nil { + t.Fatal(err) + } + }, + ExpectedStatus: 200, + ExpectedContent: []string{ + `"email":"test@example.com"`, + `"username":"username_as_email@example.com"`, + `"token":`, + }, + NotExpectedContent: []string{ + // hidden fields + `"tokenKey"`, + `"password"`, + }, + ExpectedEvents: map[string]int{ + "*": 0, + "OnRecordAuthWithPasswordRequest": 1, + "OnRecordAuthRequest": 1, + "OnRecordEnrich": 1, + // authOrigin track + "OnModelCreate": 1, + "OnModelCreateExecute": 1, + "OnModelAfterCreateSuccess": 1, + "OnModelValidate": 1, + "OnRecordCreate": 1, + "OnRecordCreateExecute": 1, + "OnRecordAfterCreateSuccess": 1, + "OnRecordValidate": 1, + "OnMailerSend": 1, + "OnMailerRecordAuthAlertSend": 1, + }, + }, { Name: "unknown explicit identityField", Method: http.MethodPost,