From f126dd261d14225d1758150fef454b1463a8abc1 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 14 Jul 2026 09:27:29 -0400 Subject: [PATCH 1/4] feat(ui): Brand known OAuth clients on the consent screen When an OAuth app has no uploaded logo, the consent screen now renders a recognizable brand mark for known clients (Claude, ChatGPT). Matching is keyed on the trusted, backend-resolved redirect domain, not the app-owner-set name, so a look-alike name cannot borrow the branding. --- .changeset/oauth-consent-known-clients.md | 5 +++ .../src/components/OAuthConsent/LogoGroup.tsx | 16 +++++-- .../components/OAuthConsent/OAuthConsent.tsx | 16 ++++++- .../__tests__/OAuthConsent.test.tsx | 23 ++++++++++ .../__tests__/knownClients.test.ts | 39 +++++++++++++++++ .../components/OAuthConsent/knownClients.ts | 43 +++++++++++++++++++ packages/ui/src/icons/claude.svg | 3 ++ packages/ui/src/icons/index.ts | 2 + packages/ui/src/icons/openai.svg | 3 ++ 9 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 .changeset/oauth-consent-known-clients.md create mode 100644 packages/ui/src/components/OAuthConsent/__tests__/knownClients.test.ts create mode 100644 packages/ui/src/components/OAuthConsent/knownClients.ts create mode 100644 packages/ui/src/icons/claude.svg create mode 100644 packages/ui/src/icons/openai.svg diff --git a/.changeset/oauth-consent-known-clients.md b/.changeset/oauth-consent-known-clients.md new file mode 100644 index 00000000000..c0f596bfa45 --- /dev/null +++ b/.changeset/oauth-consent-known-clients.md @@ -0,0 +1,5 @@ +--- +'@clerk/ui': patch +--- + +The OAuth consent screen now shows a recognizable brand mark for well-known OAuth clients (Claude, ChatGPT) when the requesting application has not uploaded its own logo. Recognition is based on the request's registrable redirect domain, so a look-alike application name cannot borrow the branding. diff --git a/packages/ui/src/components/OAuthConsent/LogoGroup.tsx b/packages/ui/src/components/OAuthConsent/LogoGroup.tsx index 09292f3f7c3..0431799b962 100644 --- a/packages/ui/src/components/OAuthConsent/LogoGroup.tsx +++ b/packages/ui/src/components/OAuthConsent/LogoGroup.tsx @@ -35,7 +35,17 @@ export function LogoGroupItem({ children, sx, ...props }: ComponentProps ({ color: t.colors.$primary500 }), +}: { + size?: 'sm' | 'md'; + sx?: ThemableCssProp; + icon?: React.ComponentType; + iconSx?: ThemableCssProp; +}) { const scale: ThemableCssProp = t => { const value = size === 'sm' ? t.space.$6 : t.space.$12; return { width: value, height: value }; @@ -63,8 +73,8 @@ export function LogoGroupIcon({ size = 'md', sx }: { size?: 'sm' | 'md'; sx?: Th elementDescriptor={descriptors.logoGroupIcon} > ({ color: t.colors.$primary500 })} + icon={icon} + sx={iconSx} /> ); diff --git a/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx b/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx index 15a8be991d5..1b25534ac2e 100644 --- a/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx +++ b/packages/ui/src/components/OAuthConsent/OAuthConsent.tsx @@ -13,6 +13,7 @@ import { Alert, Textarea } from '@/ui/primitives'; import { Route, Switch } from '@/ui/router'; import { InlineAction } from './InlineAction'; +import { getKnownOAuthClient } from './knownClients'; import { ListGroup, ListGroupContent, @@ -90,6 +91,11 @@ function _OAuthConsent() { const domainAction = data?.redirectDomain ?? getRedirectDisplay(redirectUrl); const viewFullUrlText = t(localizationKeys('oauthConsent.viewFullUrl')); + // When the OAuth app has no uploaded logo, fall back to a recognized client's + // brand mark (Claude, ChatGPT, ...). Keyed on the trusted redirect domain, not + // the app-owner-set name, so a look-alike name cannot borrow the branding. + const knownClient = oauthApplicationLogoUrl ? undefined : getKnownOAuthClient(domainAction); + // Error states only apply to the public flow. if (!hasContextCallbacks) { const errorMessage = !oauthClientId @@ -197,7 +203,10 @@ function _OAuthConsent() { {!oauthApplicationLogoUrl && logoImageUrl && ( - + @@ -208,7 +217,10 @@ function _OAuthConsent() { {/* no avatars */} {!oauthApplicationLogoUrl && !logoImageUrl && ( - + )} diff --git a/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx b/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx index 893b1344f2f..7e477e9f998 100644 --- a/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx +++ b/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx @@ -542,4 +542,27 @@ describe('OAuthConsent', () => { }); }); }); + + it('renders the branded logo badge for a recognized client with no uploaded logo', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withUser({ email_addresses: ['jane@example.com'] }); + }); + + props.setProps({ componentName: 'OAuthConsent' } as any); + mockOAuthApplication(fixtures.clerk, { + getConsentInfo: vi.fn().mockResolvedValue({ + ...fakeConsentInfo, + oauthApplicationName: 'Claude', + oauthApplicationLogoUrl: '', + redirectDomain: 'claude.ai', + }), + }); + + const { getByText, baseElement } = render(, { wrapper }); + + await waitFor(() => { + expect(getByText('Claude')).toBeVisible(); + expect(baseElement.querySelector('.cl-logoGroupIcon')).not.toBeNull(); + }); + }); }); diff --git a/packages/ui/src/components/OAuthConsent/__tests__/knownClients.test.ts b/packages/ui/src/components/OAuthConsent/__tests__/knownClients.test.ts new file mode 100644 index 00000000000..7dbe643f3e7 --- /dev/null +++ b/packages/ui/src/components/OAuthConsent/__tests__/knownClients.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest'; + +import { getKnownOAuthClient } from '../knownClients'; + +describe('getKnownOAuthClient', () => { + it('resolves Claude from its registrable domains', () => { + expect(getKnownOAuthClient('claude.ai')?.name).toBe('Claude'); + expect(getKnownOAuthClient('claude.com')?.name).toBe('Claude'); + expect(getKnownOAuthClient('anthropic.com')?.name).toBe('Claude'); + }); + + it('resolves ChatGPT from its registrable domains', () => { + expect(getKnownOAuthClient('chatgpt.com')?.name).toBe('ChatGPT'); + expect(getKnownOAuthClient('openai.com')?.name).toBe('ChatGPT'); + }); + + it('is case-insensitive and trims surrounding whitespace', () => { + expect(getKnownOAuthClient(' Claude.AI ')?.name).toBe('Claude'); + }); + + it('returns undefined for empty, nullish, or unknown domains', () => { + expect(getKnownOAuthClient('')).toBeUndefined(); + expect(getKnownOAuthClient(null)).toBeUndefined(); + expect(getKnownOAuthClient(undefined)).toBeUndefined(); + expect(getKnownOAuthClient('example.com')).toBeUndefined(); + }); + + it('does not match a subdomain string that is not the registrable domain', () => { + // Matching is exact on the PSL-resolved registrable domain, so a look-alike + // host must not borrow the branding. + expect(getKnownOAuthClient('claude.ai.evil.com')).toBeUndefined(); + expect(getKnownOAuthClient('notclaude.ai')).toBeUndefined(); + }); + + it('provides an icon component for matched clients', () => { + expect(getKnownOAuthClient('claude.ai')?.icon).toBeDefined(); + expect(getKnownOAuthClient('chatgpt.com')?.icon).toBeDefined(); + }); +}); diff --git a/packages/ui/src/components/OAuthConsent/knownClients.ts b/packages/ui/src/components/OAuthConsent/knownClients.ts new file mode 100644 index 00000000000..787808be540 --- /dev/null +++ b/packages/ui/src/components/OAuthConsent/knownClients.ts @@ -0,0 +1,43 @@ +import type React from 'react'; + +import { Claude, OpenAI } from '@/ui/icons'; +import type { ThemableCssProp } from '@/ui/styledSystem'; + +/** + * A curated OAuth client we can recognize and brand on the consent screen. + * Matching is keyed on the registrable redirect domain (the trusted, + * backend-resolved signal), never on the app-owner-set name or homepage. + */ +type KnownOAuthClient = { + name: string; + icon: React.ComponentType; + /** + * Overrides the default badge icon tint. Omit for icons that carry their own + * fill (e.g. a fixed brand color). + */ + iconSx?: ThemableCssProp; + /** Registrable domains (PSL-resolved) that identify this client. */ + domains: string[]; +}; + +const KNOWN_OAUTH_CLIENTS: KnownOAuthClient[] = [ + { name: 'Claude', icon: Claude, domains: ['claude.ai', 'claude.com', 'anthropic.com'] }, + { + name: 'ChatGPT', + icon: OpenAI, + iconSx: t => ({ color: t.colors.$colorForeground }), + domains: ['chatgpt.com', 'openai.com'], + }, +]; + +/** + * Resolves a known OAuth client from its registrable redirect domain, or + * `undefined` when the domain is empty or unrecognized. + */ +export function getKnownOAuthClient(domain?: string | null): KnownOAuthClient | undefined { + if (!domain) { + return undefined; + } + const normalized = domain.trim().toLowerCase(); + return KNOWN_OAUTH_CLIENTS.find(client => client.domains.includes(normalized)); +} diff --git a/packages/ui/src/icons/claude.svg b/packages/ui/src/icons/claude.svg new file mode 100644 index 00000000000..786fa29d08b --- /dev/null +++ b/packages/ui/src/icons/claude.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/icons/index.ts b/packages/ui/src/icons/index.ts index bbd9306f598..4dcc2fc7d10 100644 --- a/packages/ui/src/icons/index.ts +++ b/packages/ui/src/icons/index.ts @@ -20,6 +20,7 @@ export { default as Building } from './building.svg'; export { default as Caret } from './caret.svg'; export { default as Checkmark } from './checkmark.svg'; export { default as CheckmarkCircle } from './checkmark-circle.svg'; +export { default as Claude } from './claude.svg'; export { default as ChevronDown } from './chevron-down.svg'; export { default as ChevronLeft } from './chevron-left.svg'; export { default as ChevronRight } from './chevron-right.svg'; @@ -53,6 +54,7 @@ export { default as MagnifyingGlass } from './magnifying-glass.svg'; export { default as Menu } from './menu.svg'; export { default as Minus } from './minus.svg'; export { default as Mobile } from './mobile.svg'; +export { default as OpenAI } from './openai.svg'; export { default as Pen } from './pen.svg'; export { default as Plus } from './plus.svg'; export { default as Print } from './print.svg'; diff --git a/packages/ui/src/icons/openai.svg b/packages/ui/src/icons/openai.svg new file mode 100644 index 00000000000..747e3342e82 --- /dev/null +++ b/packages/ui/src/icons/openai.svg @@ -0,0 +1,3 @@ + + + From a3c090a5749a1602e06df5c2be28fb345e4acc1b Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 14 Jul 2026 10:47:39 -0400 Subject: [PATCH 2/4] fix(ui): Keep OAuth brand marks out of the shared ui-common chunk The Claude/ChatGPT brand SVGs lived in src/icons, which the ui-common rspack cacheGroup captures, pushing the shared chunk over its bundlewatch budget. Move them into the OAuthConsent component directory so they bundle into the lazy oauthConsent chunk instead, and bump that chunk's budget to account for the marks. --- packages/ui/bundlewatch.config.json | 2 +- packages/ui/src/components/OAuthConsent/brandIcons.ts | 11 +++++++++++ .../src/{icons => components/OAuthConsent}/claude.svg | 0 .../ui/src/components/OAuthConsent/knownClients.ts | 3 ++- .../src/{icons => components/OAuthConsent}/openai.svg | 0 packages/ui/src/icons/index.ts | 2 -- 6 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 packages/ui/src/components/OAuthConsent/brandIcons.ts rename packages/ui/src/{icons => components/OAuthConsent}/claude.svg (100%) rename packages/ui/src/{icons => components/OAuthConsent}/openai.svg (100%) diff --git a/packages/ui/bundlewatch.config.json b/packages/ui/bundlewatch.config.json index 08f942932d0..eff0191c9c9 100644 --- a/packages/ui/bundlewatch.config.json +++ b/packages/ui/bundlewatch.config.json @@ -26,7 +26,7 @@ { "path": "./dist/planDetails*.js", "maxSize": "5.5KB" }, { "path": "./dist/subscriptionDetails*.js", "maxSize": "7KB" }, { "path": "./dist/apiKeys*.js", "maxSize": "6KB" }, - { "path": "./dist/oauthConsent*.js", "maxSize": "6KB" }, + { "path": "./dist/oauthConsent*.js", "maxSize": "7.5KB" }, { "path": "./dist/up-billing-page*.js", "maxSize": "3KB" }, { "path": "./dist/op-billing-page*.js", "maxSize": "3KB" }, { "path": "./dist/up-plans-page*.js", "maxSize": "2.5KB" }, diff --git a/packages/ui/src/components/OAuthConsent/brandIcons.ts b/packages/ui/src/components/OAuthConsent/brandIcons.ts new file mode 100644 index 00000000000..80730c02b83 --- /dev/null +++ b/packages/ui/src/components/OAuthConsent/brandIcons.ts @@ -0,0 +1,11 @@ +// @ts-nocheck +/** + * Brand marks for recognized OAuth clients. These live in the component + * directory (not `src/icons`) on purpose: the `ui-common` rspack cacheGroup + * captures everything outside `/components`, so keeping them here bundles the + * marks into the lazy `oauthConsent` chunk instead of the shared chunk that + * loads on every component. The `@ts-nocheck` mirrors `icons/index.ts`: `.svg` + * modules are resolved by the bundler (svgr), not by tsc. + */ +export { default as Claude } from './claude.svg'; +export { default as OpenAI } from './openai.svg'; diff --git a/packages/ui/src/icons/claude.svg b/packages/ui/src/components/OAuthConsent/claude.svg similarity index 100% rename from packages/ui/src/icons/claude.svg rename to packages/ui/src/components/OAuthConsent/claude.svg diff --git a/packages/ui/src/components/OAuthConsent/knownClients.ts b/packages/ui/src/components/OAuthConsent/knownClients.ts index 787808be540..ebb18858578 100644 --- a/packages/ui/src/components/OAuthConsent/knownClients.ts +++ b/packages/ui/src/components/OAuthConsent/knownClients.ts @@ -1,8 +1,9 @@ import type React from 'react'; -import { Claude, OpenAI } from '@/ui/icons'; import type { ThemableCssProp } from '@/ui/styledSystem'; +import { Claude, OpenAI } from './brandIcons'; + /** * A curated OAuth client we can recognize and brand on the consent screen. * Matching is keyed on the registrable redirect domain (the trusted, diff --git a/packages/ui/src/icons/openai.svg b/packages/ui/src/components/OAuthConsent/openai.svg similarity index 100% rename from packages/ui/src/icons/openai.svg rename to packages/ui/src/components/OAuthConsent/openai.svg diff --git a/packages/ui/src/icons/index.ts b/packages/ui/src/icons/index.ts index 4dcc2fc7d10..bbd9306f598 100644 --- a/packages/ui/src/icons/index.ts +++ b/packages/ui/src/icons/index.ts @@ -20,7 +20,6 @@ export { default as Building } from './building.svg'; export { default as Caret } from './caret.svg'; export { default as Checkmark } from './checkmark.svg'; export { default as CheckmarkCircle } from './checkmark-circle.svg'; -export { default as Claude } from './claude.svg'; export { default as ChevronDown } from './chevron-down.svg'; export { default as ChevronLeft } from './chevron-left.svg'; export { default as ChevronRight } from './chevron-right.svg'; @@ -54,7 +53,6 @@ export { default as MagnifyingGlass } from './magnifying-glass.svg'; export { default as Menu } from './menu.svg'; export { default as Minus } from './minus.svg'; export { default as Mobile } from './mobile.svg'; -export { default as OpenAI } from './openai.svg'; export { default as Pen } from './pen.svg'; export { default as Plus } from './plus.svg'; export { default as Print } from './print.svg'; From f45f80066e2028e724a894d767bddc36a12e733c Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 21 Jul 2026 13:22:58 -0400 Subject: [PATCH 3/4] feat(ui): Unify OAuth consent logo container and label brand marks Consolidate the consent-screen logo chrome into a single LogoGroupItemContainer that sizes either an app logo or an icon from one size prop, add the logoGroupItemContainer appearance element, and expose an accessible name on recognized-client brand marks (decorative fallback stays aria-hidden). --- .changeset/oauth-consent-known-clients.md | 2 +- .../src/components/OAuthConsent/LogoGroup.tsx | 47 +++++++++---- .../components/OAuthConsent/OAuthConsent.tsx | 70 ++++++++++++------- .../__tests__/OAuthConsent.test.tsx | 6 +- .../src/customizables/elementDescriptors.ts | 1 + packages/ui/src/internal/appearance.ts | 1 + packages/ui/src/primitives/Icon.tsx | 5 ++ 7 files changed, 91 insertions(+), 41 deletions(-) diff --git a/.changeset/oauth-consent-known-clients.md b/.changeset/oauth-consent-known-clients.md index c0f596bfa45..7da474cf6fb 100644 --- a/.changeset/oauth-consent-known-clients.md +++ b/.changeset/oauth-consent-known-clients.md @@ -2,4 +2,4 @@ '@clerk/ui': patch --- -The OAuth consent screen now shows a recognizable brand mark for well-known OAuth clients (Claude, ChatGPT) when the requesting application has not uploaded its own logo. Recognition is based on the request's registrable redirect domain, so a look-alike application name cannot borrow the branding. +The OAuth consent screen now shows a recognizable brand mark for well-known OAuth clients (Claude, ChatGPT) when the requesting application has not uploaded its own logo. Recognition is based on the request's registrable redirect domain, so a look-alike application name cannot borrow the branding. The brand mark exposes an accessible name for assistive technologies, and the logo badges are now customizable through the new `logoGroupItemContainer` appearance element. diff --git a/packages/ui/src/components/OAuthConsent/LogoGroup.tsx b/packages/ui/src/components/OAuthConsent/LogoGroup.tsx index 0431799b962..446a7b3b2b9 100644 --- a/packages/ui/src/components/OAuthConsent/LogoGroup.tsx +++ b/packages/ui/src/components/OAuthConsent/LogoGroup.tsx @@ -35,20 +35,21 @@ export function LogoGroupItem({ children, sx, ...props }: ComponentProps ({ color: t.colors.$primary500 }), + children, }: { size?: 'sm' | 'md'; sx?: ThemableCssProp; - icon?: React.ComponentType; - iconSx?: ThemableCssProp; + children: React.ReactNode; }) { const scale: ThemableCssProp = t => { - const value = size === 'sm' ? t.space.$6 : t.space.$12; - return { width: value, height: value }; + const container = size === 'sm' ? t.space.$6 : t.space.$12; + const inner = size === 'sm' ? t.space.$3 : t.space.$5; + // `&&` doubles the container's specificity so it wins over a bare child + // (Icon / ApplicationLogo) that sizes itself with a single class. + return { width: container, height: container, '&& > *': { width: inner, height: inner } }; }; return ( @@ -70,16 +71,38 @@ export function LogoGroupIcon({ scale, sx, ]} - elementDescriptor={descriptors.logoGroupIcon} + elementDescriptor={descriptors.logoGroupItemContainer} > - + {children} ); } +export function LogoGroupIcon({ + icon = LockDottedCircle, + iconSx = t => ({ color: t.colors.$primary500 }), + label, +}: { + icon?: React.ComponentType; + iconSx?: ThemableCssProp; + /** + * Accessible name for a meaningful mark (e.g. a recognized client's brand). + * Omit for the decorative fallback, which is then hidden from assistive tech. + */ + label?: string; +}) { + return ( + + ); +} + export function LogoGroupSeparator() { return ( - + + + - + + + )} @@ -182,20 +186,24 @@ function _OAuthConsent() { {oauthApplicationLogoUrl && !logoImageUrl && ( - - + + + ({ position: 'absolute', - bottom: `calc(${t.space.$3} * -1)`, - insetInlineEnd: `calc(${t.space.$3} * -1)`, + bottom: `calc(${t.space.$2x5} * -1)`, + insetInlineEnd: `calc(${t.space.$2x5} * -1)`, })} - /> + > + + )} @@ -203,24 +211,32 @@ function _OAuthConsent() { {!oauthApplicationLogoUrl && logoImageUrl && ( - + + + - + + + )} {/* no avatars */} {!oauthApplicationLogoUrl && !logoImageUrl && ( - + + + )} diff --git a/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx b/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx index 7e477e9f998..fc401643835 100644 --- a/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx +++ b/packages/ui/src/components/OAuthConsent/__tests__/OAuthConsent.test.tsx @@ -562,7 +562,11 @@ describe('OAuthConsent', () => { await waitFor(() => { expect(getByText('Claude')).toBeVisible(); - expect(baseElement.querySelector('.cl-logoGroupIcon')).not.toBeNull(); + const icon = baseElement.querySelector('.cl-logoGroupIcon'); + expect(icon).not.toBeNull(); + // The brand mark conveys which app is requesting access, so it needs an accessible name. + expect(icon).toHaveAttribute('role', 'img'); + expect(icon).toHaveAttribute('aria-label', 'Claude'); }); }); }); diff --git a/packages/ui/src/customizables/elementDescriptors.ts b/packages/ui/src/customizables/elementDescriptors.ts index 0e8f3dc9d92..abe2260ba9f 100644 --- a/packages/ui/src/customizables/elementDescriptors.ts +++ b/packages/ui/src/customizables/elementDescriptors.ts @@ -55,6 +55,7 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'logoGroup', 'logoGroupItem', + 'logoGroupItemContainer', 'logoGroupIcon', 'logoGroupSeparator', diff --git a/packages/ui/src/internal/appearance.ts b/packages/ui/src/internal/appearance.ts index bb64ee12425..82010ace01b 100644 --- a/packages/ui/src/internal/appearance.ts +++ b/packages/ui/src/internal/appearance.ts @@ -183,6 +183,7 @@ export type ElementsConfig = { logoGroup: WithOptions; logoGroupItem: WithOptions; + logoGroupItemContainer: WithOptions; logoGroupIcon: WithOptions; logoGroupSeparator: WithOptions; diff --git a/packages/ui/src/primitives/Icon.tsx b/packages/ui/src/primitives/Icon.tsx index 42a29334628..0e0d04c396f 100644 --- a/packages/ui/src/primitives/Icon.tsx +++ b/packages/ui/src/primitives/Icon.tsx @@ -29,6 +29,11 @@ const { applyVariants, filterProps } = createVariants(theme => ({ // @ts-ignore export type IconProps = StyleVariants & { icon: React.ComponentType; + // Icon renders an ; allow the accessibility attributes an svg accepts so a + // meaningful icon can carry a name (decorative ones stay aria-hidden). + role?: React.AriaRole; + 'aria-label'?: string; + 'aria-hidden'?: boolean; }; export const Icon = (props: IconProps): JSX.Element => { From 905095235cc43d3f25d29bc031abb09ec77cb03a Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 21 Jul 2026 15:07:10 -0400 Subject: [PATCH 4/4] Apply suggestion from @alexcarpenter --- .changeset/oauth-consent-known-clients.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/oauth-consent-known-clients.md b/.changeset/oauth-consent-known-clients.md index 7da474cf6fb..200a84478c3 100644 --- a/.changeset/oauth-consent-known-clients.md +++ b/.changeset/oauth-consent-known-clients.md @@ -2,4 +2,4 @@ '@clerk/ui': patch --- -The OAuth consent screen now shows a recognizable brand mark for well-known OAuth clients (Claude, ChatGPT) when the requesting application has not uploaded its own logo. Recognition is based on the request's registrable redirect domain, so a look-alike application name cannot borrow the branding. The brand mark exposes an accessible name for assistive technologies, and the logo badges are now customizable through the new `logoGroupItemContainer` appearance element. +The OAuth consent screen now shows a recognizable brand mark for well-known OAuth clients (Claude, ChatGPT) when the requesting application has not uploaded its own logo.