From be5d4516eca03f8e29f2609e526b8b3fe7600f2f Mon Sep 17 00:00:00 2001 From: Danny5487401 <64348131+Danny5487401@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:08:11 +0800 Subject: [PATCH] fix(auth): bundle OIDC provider login icons Signed-off-by: Danny5487401 <64348131+Danny5487401@users.noreply.github.com> --- web/public/auth0-logo.svg | 1 + web/public/keycloak-logo.svg | 1 + web/public/okta-logo.svg | 1 + web/src/features/auth/login-button.test.tsx | 58 +++++++++++++++++++++ web/src/features/auth/login-button.tsx | 9 +++- 5 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 web/public/auth0-logo.svg create mode 100644 web/public/keycloak-logo.svg create mode 100644 web/public/okta-logo.svg diff --git a/web/public/auth0-logo.svg b/web/public/auth0-logo.svg new file mode 100644 index 000000000..0de1fd303 --- /dev/null +++ b/web/public/auth0-logo.svg @@ -0,0 +1 @@ +Auth0 diff --git a/web/public/keycloak-logo.svg b/web/public/keycloak-logo.svg new file mode 100644 index 000000000..c048427e8 --- /dev/null +++ b/web/public/keycloak-logo.svg @@ -0,0 +1 @@ + diff --git a/web/public/okta-logo.svg b/web/public/okta-logo.svg new file mode 100644 index 000000000..e546c180a --- /dev/null +++ b/web/public/okta-logo.svg @@ -0,0 +1 @@ +Okta diff --git a/web/src/features/auth/login-button.test.tsx b/web/src/features/auth/login-button.test.tsx index a78d06730..a6c0c7f29 100644 --- a/web/src/features/auth/login-button.test.tsx +++ b/web/src/features/auth/login-button.test.tsx @@ -1,4 +1,6 @@ /** @vitest-environment jsdom */ +import { existsSync } from 'node:fs' +import { resolve } from 'node:path' import { cleanup, fireEvent, render, screen } from '@testing-library/react' import { afterEach, describe, expect, it, vi } from 'vitest' import type { AuthMethod } from '@/api/types' @@ -69,6 +71,62 @@ describe('AuthMethodButtonList', () => { expect(screen.queryByText('Login with Password')).toBeNull() }) + it.each([false, true])('uses the Keycloak icon while preserving existing provider icons (compact=%s)', (compact) => { + const keycloakMethod: AuthMethod = { + id: 'keycloak', + methodType: 'OAUTH_REDIRECT', + provider: 'Keycloak', + displayName: 'Keycloak', + actionUrl: '/oauth2/authorization/keycloak', + } + + render() + + expect(screen.getByRole('img', { name: 'Keycloak' }).getAttribute('src')).toBe('/keycloak-logo.svg') + expect(screen.getByRole('img', { name: 'github' }).getAttribute('src')).toBe('/github-logo.svg') + }) + + it.each([ + ['Auth0', 'auth0'], + ['OKTA', 'okta'], + ])('uses the %s provider icon', (provider, icon) => { + render() + + expect(screen.getByRole('img', { name: provider }).getAttribute('src')).toBe(`/${icon}-logo.svg`) + }) + + it.each(['Enterprise SSO', 'constructor', '__proto__'])('uses the packaged OIDC icon for unrecognized provider %s', (provider) => { + render() + + expect(screen.getByRole('img', { name: provider }).getAttribute('src')).toBe('/oidc-logo.svg') + }) + + it.each(['github', 'gitlab', 'feishu', 'dingtalk', 'keycloak', 'auth0', 'okta', 'unknown'])('bundles the icon selected for %s', (provider) => { + render() + + const iconPath = screen.getByRole('img', { name: provider }).getAttribute('src') ?? '' + expect(iconPath).toMatch(/^\/[a-z0-9-]+-logo\.svg$/) + expect(existsSync(resolve(process.cwd(), 'public', iconPath.slice(1)))).toBe(true) + }) + it('renders the supplied empty fallback when no OAuth methods are available', () => { render( method.methodType !== 'OAUTH_REDIRECT')} emptyFallback={

No methods

} />) diff --git a/web/src/features/auth/login-button.tsx b/web/src/features/auth/login-button.tsx index 08d9b6568..5f787c89e 100644 --- a/web/src/features/auth/login-button.tsx +++ b/web/src/features/auth/login-button.tsx @@ -12,14 +12,19 @@ interface LoginButtonProps { emptyFallback?: React.ReactNode } +const packagedIconProviders = new Set([ + 'github', 'gitlab', 'feishu', 'dingtalk', 'keycloak', 'auth0', 'okta', +]) + /** * Returns the appropriate icon for a given OAuth provider. */ function OAuthIcon({ provider, compact = false }: { provider: string, compact?: boolean }) { - const normalizedProvider = provider.toLowerCase() + const normalizedProvider = provider.trim().toLowerCase() + const iconProvider = packagedIconProviders.has(normalizedProvider) ? normalizedProvider : 'oidc' return ( {provider}