fix(auth): support employee ID usernames
giteabot backport / giteabot (push) Canceled after 0s
giteabot / giteabot (push) Canceled after 0s
release-nightly / nightly-binary (push) Canceled after 0s
release-nightly / nightly-container (push) Canceled after 0s
release-nightly-snapcraft / build-and-publish (push) Canceled after 0s

Assisted-by: Codex:GPT-5
This commit is contained in:
2026-08-29 18:42:44 +08:00
parent 746b23b098
commit ab55a432ec
6 changed files with 33 additions and 2 deletions
+2 -1
View File
@@ -1735,8 +1735,9 @@ LEVEL = Info
;; userid = use the userid / sub attribute
;; nickname = use the nickname attribute
;; preferred_username = use the preferred_username attribute
;; employee_id = use the employee_id attribute (requires the provider to return this custom claim)
;; email = use the username part of the email attribute
;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
;; Note: `nickname`, `preferred_username`, `employee_id` and `email` options will normalize input strings using the following criteria:
;; - diacritics are removed
;; - the characters in the set ['´`] are removed
;; - the characters in the set [\s~+] are replaced with "-"
+2 -1
View File
@@ -20,11 +20,12 @@ const (
OAuth2UsernameNickname OAuth2UsernameType = "nickname" // use nickname field
OAuth2UsernameEmail OAuth2UsernameType = "email" // use email field
OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username" // use preferred_username field
OAuth2UsernameEmployeeID OAuth2UsernameType = "employee_id" // use employee_id field
)
func (username OAuth2UsernameType) isValid() bool {
switch username {
case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail, OAuth2UsernamePreferredUsername:
case OAuth2UsernameUserid, OAuth2UsernameNickname, OAuth2UsernameEmail, OAuth2UsernamePreferredUsername, OAuth2UsernameEmployeeID:
return true
}
return false
+8
View File
@@ -88,3 +88,11 @@ ENABLE_OPENID_CONNECT_PKCE = true
loadOAuth2ClientFrom(cfg)
assert.True(t, OAuth2Client.EnableOpenIDConnectPKCE)
}
func TestOAuth2ClientEmployeeIDUsername(t *testing.T) {
cfg, _ := NewConfigProviderFromData(`[oauth2_client]
USERNAME = employee_id
`)
loadOAuth2ClientFrom(cfg)
assert.Equal(t, OAuth2UsernameEmployeeID, OAuth2Client.Username)
}
+5
View File
@@ -450,6 +450,11 @@ func extractUserNameFromOAuth2(gothUser *goth.User) (string, error) {
return user_model.NormalizeUserName(preferredUsername)
}
return "", nil
case setting.OAuth2UsernameEmployeeID:
if employeeID, ok := gothUser.RawData["employee_id"].(string); ok {
return user_model.NormalizeUserName(employeeID)
}
return "", nil
case setting.OAuth2UsernameNickname:
return user_model.NormalizeUserName(gothUser.NickName)
default: // OAuth2UsernameUserid
+14
View File
@@ -184,6 +184,20 @@ func TestWebAuthOAuth2(t *testing.T) {
})
}
func TestExtractUserNameFromOAuth2EmployeeID(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2Client.Username, setting.OAuth2UsernameEmployeeID)()
username, err := extractUserNameFromOAuth2(&goth.User{
RawData: map[string]any{"employee_id": "RC001"},
})
require.NoError(t, err)
assert.Equal(t, "RC001", username)
username, err = extractUserNameFromOAuth2(&goth.User{RawData: map[string]any{}})
require.NoError(t, err)
assert.Empty(t, username)
}
func TestOpenIDRequireTwoFactor(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
mockOpt := contexttest.MockContextOption{SessionStore: session.NewMockMemStore("dummy-sid-openid")}
+2
View File
@@ -160,6 +160,8 @@ func SignInOAuthCallback(ctx *context.Context) {
missingFields = append(missingFields, "nickname")
case setting.OAuth2UsernamePreferredUsername:
missingFields = append(missingFields, "preferred_username")
case setting.OAuth2UsernameEmployeeID:
missingFields = append(missingFields, "employee_id")
} // else: "UserID" and "Email" have been handled above separately
}
if len(missingFields) > 0 {