feat(oidc): 支持自定义 OIDC 登录显示名称 (#6012)

* docs: add design spec for OIDC custom display name

Mirrors the existing Custom OAuth Provider name pattern so admins can
show a meaningful label instead of the hardcoded "OIDC" on the login
page and in related copy.

* feat(oidc): add configurable display name with OIDC fallback

* feat(oidc): use configured display name in provider name and status API

* feat(oidc): add display name field to default-theme OIDC settings

Claude-Session: https://claude.ai/code/session_01FDkWJqigJi9yE3HG5pjZP5

* feat(oidc): show configured display name on default-theme login button

* feat(oidc): add display name field to classic-theme OIDC settings

* feat(oidc): show configured display name on classic-theme login button

* fix(oidc): trim whitespace before applying display name fallback

* chore: remove internal design doc from PR

Design/planning docs are working artifacts for this session and
shouldn't be submitted to the upstream project.

* fix(oidc): lead with example in classic-theme display name placeholder

Reorders the combined placeholder to show the example first, then
the fallback note, matching the Custom OAuth Provider Name field's
placeholder convention (example-only) that this feature mirrors.

* fix(i18n): improve Russian grammar in OIDC display-name placeholder translation

Leads each clause with its condition/subject and adds the missing
verb, per PR review feedback.

* test(web): remove redundant OIDC harness tests
This commit is contained in:
June Chi
2026-07-29 16:27:15 +08:00
committed by GitHub
parent c27d1ef651
commit cb4c8c02f8
20 changed files with 213 additions and 6 deletions
@@ -107,9 +107,12 @@ export function OAuthProviders({
}
if (status?.oidc_enabled) {
const oidcDisplayName = status.oidc_display_name?.trim() || 'OIDC'
providerButtons.push({
key: 'oidc',
label: t('Continue with OIDC'),
label: t('Continue with {{name}}', {
name: oidcDisplayName,
}),
onClick: handleOIDCLogin,
})
}
+2
View File
@@ -103,6 +103,7 @@ export interface SystemStatus {
oidc_enabled?: boolean
oidc_authorization_endpoint?: string
oidc_client_id?: string
oidc_display_name?: string
linuxdo_oauth?: boolean
linuxdo_client_id?: string
telegram_oauth?: boolean
@@ -147,6 +148,7 @@ export interface SystemStatus {
oidc_enabled?: boolean
oidc_authorization_endpoint?: string
oidc_client_id?: string
oidc_display_name?: string
linuxdo_oauth?: boolean
linuxdo_client_id?: string
telegram_oauth?: boolean
@@ -40,6 +40,7 @@ const defaultAuthSettings: AuthSettings = {
'discord.client_id': '',
'discord.client_secret': '',
'oidc.enabled': false,
'oidc.display_name': '',
'oidc.client_id': '',
'oidc.client_secret': '',
'oidc.well_known': '',
@@ -72,6 +72,7 @@ const oauthSchema = z.object({
}),
oidc: z.object({
enabled: z.boolean(),
display_name: z.string(),
client_id: z.string(),
client_secret: z.string(),
well_known: z.string(),
@@ -102,6 +103,7 @@ type FlatOAuthDefaults = {
'discord.client_id': string
'discord.client_secret': string
'oidc.enabled': boolean
'oidc.display_name': string
'oidc.client_id': string
'oidc.client_secret': string
'oidc.well_known': string
@@ -184,6 +186,7 @@ const buildFormDefaults = (defaults: FlatOAuthDefaults): OAuthFormValues => ({
},
oidc: {
enabled: defaults['oidc.enabled'],
display_name: defaults['oidc.display_name'] ?? '',
client_id: defaults['oidc.client_id'] ?? '',
client_secret: defaults['oidc.client_secret'] ?? '',
well_known: defaults['oidc.well_known'] ?? '',
@@ -212,6 +215,7 @@ const normalizeFormValues = (values: OAuthFormValues): FlatOAuthDefaults => ({
'discord.client_id': values.discord.client_id,
'discord.client_secret': values.discord.client_secret,
'oidc.enabled': values.oidc.enabled,
'oidc.display_name': values.oidc.display_name,
'oidc.client_id': values.oidc.client_id,
'oidc.client_secret': values.oidc.client_secret,
'oidc.well_known': values.oidc.well_known,
@@ -617,6 +621,33 @@ export function OAuthSection(props: OAuthSectionProps) {
)}
/>
<FormField
control={form.control}
name='oidc.display_name'
render={({ field }) => (
<FormItem>
<FormLabel>{t('OIDC Display Name')}</FormLabel>
<FormControl>
<Input
placeholder={t('e.g. Company SSO')}
autoComplete='off'
value={field.value ?? ''}
onChange={(event) =>
field.onChange(event.target.value)
}
name={field.name}
onBlur={field.onBlur}
ref={field.ref}
/>
</FormControl>
<FormDescription>
{t('Defaults to "OIDC" if left blank')}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='oidc.client_id'
@@ -56,6 +56,7 @@ const AUTH_SECTIONS = [
'discord.client_id': settings['discord.client_id'],
'discord.client_secret': settings['discord.client_secret'],
'oidc.enabled': settings['oidc.enabled'],
'oidc.display_name': settings['oidc.display_name'],
'oidc.client_id': settings['oidc.client_id'],
'oidc.client_secret': settings['oidc.client_secret'],
'oidc.well_known': settings['oidc.well_known'],
@@ -24,7 +24,7 @@ import { updateSystemOption } from '../api'
import type { UpdateOptionRequest } from '../types'
// Configuration keys that require status refresh
const STATUS_RELATED_KEYS = [
const STATUS_RELATED_KEYS = new Set([
'HeaderNavModules',
'SidebarModulesAdmin',
'Notice',
@@ -36,7 +36,8 @@ const STATUS_RELATED_KEYS = [
'general_setting.quota_display_type',
'general_setting.custom_currency_symbol',
'general_setting.custom_currency_exchange_rate',
]
'oidc.display_name',
])
export function useUpdateOption() {
const queryClient = useQueryClient()
@@ -49,7 +50,7 @@ export function useUpdateOption() {
queryClient.invalidateQueries({ queryKey: ['system-options'] })
// If updating frontend-display-related config, also refresh status
if (STATUS_RELATED_KEYS.includes(variables.key)) {
if (STATUS_RELATED_KEYS.has(variables.key)) {
queryClient.invalidateQueries({ queryKey: ['status'] })
try {
window.localStorage.removeItem('status')
@@ -136,6 +136,7 @@ export type AuthSettings = {
'discord.client_id': string
'discord.client_secret': string
'oidc.enabled': boolean
'oidc.display_name': string
'oidc.client_id': string
'oidc.client_secret': string
'oidc.well_known': string
+3
View File
@@ -1580,6 +1580,9 @@
"Enable LinuxDO OAuth": "Enable LinuxDO OAuth",
"Enable model performance metrics": "Enable model performance metrics",
"Enable OIDC": "Enable OIDC",
"OIDC Display Name": "OIDC Display Name",
"e.g. Company SSO": "e.g. Company SSO",
"Defaults to \"OIDC\" if left blank": "Defaults to \"OIDC\" if left blank",
"Enable or disable this channel": "Enable or disable this channel",
"Enable or disable this model": "Enable or disable this model",
"Enable Passkey": "Enable Passkey",
+3
View File
@@ -1580,6 +1580,9 @@
"Enable LinuxDO OAuth": "Activer LinuxDO OAuth",
"Enable model performance metrics": "Activer les indicateurs de performance des modèles",
"Enable OIDC": "Activer OIDC",
"OIDC Display Name": "Nom d'affichage OIDC",
"e.g. Company SSO": "ex. SSO de l'entreprise",
"Defaults to \"OIDC\" if left blank": "Par défaut « OIDC » si laissé vide",
"Enable or disable this channel": "Activer ou désactiver ce canal",
"Enable or disable this model": "Activer ou désactiver ce modèle",
"Enable Passkey": "Activer Passkey",
+3
View File
@@ -1580,6 +1580,9 @@
"Enable LinuxDO OAuth": "LinuxDO OAuthを有効にする",
"Enable model performance metrics": "モデル性能メトリクスを有効化",
"Enable OIDC": "OIDCを有効にする",
"OIDC Display Name": "OIDC 表示名",
"e.g. Company SSO": "例:会社の SSO",
"Defaults to \"OIDC\" if left blank": "空欄の場合は「OIDC」がデフォルトで表示されます",
"Enable or disable this channel": "このチャネルを有効または無効にする",
"Enable or disable this model": "このモデルを有効または無効にする",
"Enable Passkey": "Passkeyを有効にする",
+3
View File
@@ -1580,6 +1580,9 @@
"Enable LinuxDO OAuth": "Включить LinuxDO OAuth",
"Enable model performance metrics": "Включить метрики производительности моделей",
"Enable OIDC": "Включить OIDC",
"OIDC Display Name": "Отображаемое имя OIDC",
"e.g. Company SSO": "например, корпоративный SSO",
"Defaults to \"OIDC\" if left blank": "Если оставить пустым, будет отображаться «OIDC».",
"Enable or disable this channel": "Включить или отключить этот канал",
"Enable or disable this model": "Включить или отключить эту модель",
"Enable Passkey": "Включить Passkey",
+3
View File
@@ -1580,6 +1580,9 @@
"Enable LinuxDO OAuth": "Bật LinuxDO OAuth",
"Enable model performance metrics": "Bật chỉ số hiệu năng mô hình",
"Enable OIDC": "Bật OIDC",
"OIDC Display Name": "Tên hiển thị OIDC",
"e.g. Company SSO": "ví dụ: SSO công ty",
"Defaults to \"OIDC\" if left blank": "Mặc định là \"OIDC\" nếu để trống",
"Enable or disable this channel": "Bật hoặc tắt kênh này",
"Enable or disable this model": "Bật hoặc tắt mô hình này",
"Enable Passkey": "Bật khóa truy cập",
+3
View File
@@ -1580,6 +1580,9 @@
"Enable LinuxDO OAuth": "啟用 LinuxDO OAuth",
"Enable model performance metrics": "啟用模型效能指標",
"Enable OIDC": "啟用 OIDC",
"OIDC Display Name": "OIDC 顯示名稱",
"e.g. Company SSO": "例如:公司 SSO",
"Defaults to \"OIDC\" if left blank": "留空則預設顯示為 \"OIDC\"",
"Enable or disable this channel": "啟用或停用此渠道",
"Enable or disable this model": "啟用或停用此模型",
"Enable Passkey": "啟用 Passkey",
+3
View File
@@ -1580,6 +1580,9 @@
"Enable LinuxDO OAuth": "启用 LinuxDO OAuth",
"Enable model performance metrics": "启用模型性能指标",
"Enable OIDC": "启用 OIDC",
"OIDC Display Name": "OIDC 显示名称",
"e.g. Company SSO": "例如:公司 SSO",
"Defaults to \"OIDC\" if left blank": "留空则默认显示为 \"OIDC\"",
"Enable or disable this channel": "启用或禁用此渠道",
"Enable or disable this model": "启用或禁用此模型",
"Enable Passkey": "启用 Passkey",