From af5c071394ce874ab9368207a67d24090e828eed Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 15 Aug 2026 03:36:03 +0000 Subject: [PATCH 1/3] fix: show Appwrite icon for CLI OAuth consent avatars The first-party appwrite-cli client registers a wide wordmark as logoUri. Consent/outcome avatars use a square crop, so object-fit:cover left an unreadable fragment that looked like a missing logo (#3159). Prefer the square mark for that client and contain-fit other logos in the avatar. Co-authored-by: chiragaggarwal5k --- src/lib/helpers/oauth2-app-logo.test.ts | 27 +++++++++++++++++++ src/lib/helpers/oauth2-app-logo.ts | 18 +++++++++++++ .../(public)/oauth2/consent-card.svelte | 14 +++++++--- .../(public)/oauth2/outcome-card.svelte | 14 +++++++--- 4 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 src/lib/helpers/oauth2-app-logo.test.ts create mode 100644 src/lib/helpers/oauth2-app-logo.ts diff --git a/src/lib/helpers/oauth2-app-logo.test.ts b/src/lib/helpers/oauth2-app-logo.test.ts new file mode 100644 index 0000000000..1ab56b2c05 --- /dev/null +++ b/src/lib/helpers/oauth2-app-logo.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest'; +import { resolveOAuth2AppLogoUrl } from './oauth2-app-logo'; + +describe('resolveOAuth2AppLogoUrl', () => { + it('uses the square Appwrite icon for the first-party CLI client', () => { + expect( + resolveOAuth2AppLogoUrl({ + $id: 'appwrite-cli', + logoUri: 'https://example.com/wordmark.svg' + }) + ).toBe('/console/logos/appwrite-icon.svg'); + }); + + it('keeps third-party logoUri values', () => { + expect( + resolveOAuth2AppLogoUrl({ + $id: 'other-app', + logoUri: 'https://example.com/logo.png' + }) + ).toBe('https://example.com/logo.png'); + }); + + it('returns an empty string when no logo is available', () => { + expect(resolveOAuth2AppLogoUrl({ $id: 'other-app', logoUri: ' ' })).toBe(''); + expect(resolveOAuth2AppLogoUrl(null)).toBe(''); + }); +}); diff --git a/src/lib/helpers/oauth2-app-logo.ts b/src/lib/helpers/oauth2-app-logo.ts new file mode 100644 index 0000000000..3235ec749f --- /dev/null +++ b/src/lib/helpers/oauth2-app-logo.ts @@ -0,0 +1,18 @@ +import { base } from '$app/paths'; + +/** + * First-party clients may register a wide wordmark as `logoUri`. Consent and + * outcome avatars are square, so `object-fit: cover` crops those marks into + * unreadable fragments (see #3159 for Appwrite CLI). Prefer the square icon. + */ +const FIRST_PARTY_ICON_CLIENTS = new Set(['appwrite-cli']); + +export function resolveOAuth2AppLogoUrl( + app: { $id?: string; logoUri?: string } | null | undefined +): string { + if (app?.$id && FIRST_PARTY_ICON_CLIENTS.has(app.$id)) { + return `${base}/logos/appwrite-icon.svg`; + } + + return app?.logoUri?.trim() ?? ''; +} diff --git a/src/routes/(public)/oauth2/consent-card.svelte b/src/routes/(public)/oauth2/consent-card.svelte index 68018bb108..583e2a8c07 100644 --- a/src/routes/(public)/oauth2/consent-card.svelte +++ b/src/routes/(public)/oauth2/consent-card.svelte @@ -48,6 +48,7 @@ type ResourcePage, type ResourceNameMap } from '$lib/helpers/oauth2-authorization-details'; + import { resolveOAuth2AppLogoUrl } from '$lib/helpers/oauth2-app-logo'; import ResourceSelector from './resource-selector.svelte'; export type OAuth2Flow = 'authorization' | 'device'; @@ -110,6 +111,7 @@ ); const redirectHost = $derived(hostnameOf(grant.redirectUri)); + const appLogoUrl = $derived(resolveOAuth2AppLogoUrl(app)); const appInitial = $derived((app.name || '?').charAt(0).toUpperCase()); const accountInitial = $derived((accountLabel || '?').charAt(0).toUpperCase()); @@ -493,8 +495,8 @@