From 0c8c226acbae568074e20ac3550ac613872d40b5 Mon Sep 17 00:00:00 2001 From: masnwilliams <43387599+masnwilliams@users.noreply.github.com> Date: Tue, 12 May 2026 20:34:01 +0000 Subject: [PATCH 1/4] auto-resolve sso provider icons via simple icons cdn --- .../src/components/icons.tsx | 52 ------------ .../src/components/sso-provider.tsx | 82 +++++++++++-------- .../managed-auth-react/src/styles/styles.css | 12 +++ 3 files changed, 62 insertions(+), 84 deletions(-) diff --git a/packages/managed-auth-react/src/components/icons.tsx b/packages/managed-auth-react/src/components/icons.tsx index a9a368d..1624e75 100644 --- a/packages/managed-auth-react/src/components/icons.tsx +++ b/packages/managed-auth-react/src/components/icons.tsx @@ -198,55 +198,3 @@ export const SpinnerIcon = (p: IconProps) => ( /> ); - -// SSO provider marks -export const GoogleMark = (p: IconProps) => ( - -); - -export const GitHubMark = (p: IconProps) => ( - -); - -export const MicrosoftMark = (p: IconProps) => ( - -); - -export const FacebookMark = (p: IconProps) => ( - -); - -export const AppleMark = (p: IconProps) => ( - -); diff --git a/packages/managed-auth-react/src/components/sso-provider.tsx b/packages/managed-auth-react/src/components/sso-provider.tsx index 87a2421..d82c32b 100644 --- a/packages/managed-auth-react/src/components/sso-provider.tsx +++ b/packages/managed-auth-react/src/components/sso-provider.tsx @@ -1,40 +1,58 @@ -import type { ReactNode } from "react"; -import { - AppleMark, - BuildingIcon, - FacebookMark, - GitHubMark, - GoogleMark, - KeyIcon, - MicrosoftMark, -} from "./icons"; +import { useState, type ReactNode } from "react"; +import { BuildingIcon, KeyIcon } from "./icons"; export interface SSOProviderInfo { label: string; icon: ReactNode; } +const NON_BRAND_ICONS: Record = { + passkey: { label: "Passkey", icon: }, + sso: { label: "SSO", icon: }, + saml: { label: "SSO", icon: }, +}; + +function slugify(provider: string): string { + return provider.toLowerCase().replace(/[^a-z0-9]/g, ""); +} + +function titleCase(provider: string): string { + return provider + .split(/[\s_-]+/) + .filter(Boolean) + .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) + .join(" "); +} + +function SSOProviderIcon({ provider }: { provider: string }) { + const [errored, setErrored] = useState(false); + const slug = slugify(provider); + const letter = provider.trim().charAt(0).toUpperCase() || "?"; + + if (!slug || errored) { + return ( + + ); + } + + return ( + setErrored(true)} + /> + ); +} + export function getSSOProviderInfo(provider: string): SSOProviderInfo { - const p = provider.toLowerCase(); - if (p.includes("google")) - return { label: "Google", icon: }; - if (p.includes("github")) - return { label: "GitHub", icon: }; - if (p.includes("microsoft") || p.includes("azure")) - return { - label: "Microsoft", - icon: , - }; - if (p.includes("facebook")) - return { - label: "Facebook", - icon: , - }; - if (p.includes("apple")) - return { label: "Apple", icon: }; - if (p.includes("saml") || p.includes("sso")) - return { label: "SSO", icon: }; - if (p.includes("passkey")) - return { label: "Passkey", icon: }; - return { label: provider, icon: null }; + const key = slugify(provider); + const nonBrand = NON_BRAND_ICONS[key]; + if (nonBrand) return nonBrand; + return { + label: titleCase(provider), + icon: , + }; } diff --git a/packages/managed-auth-react/src/styles/styles.css b/packages/managed-auth-react/src/styles/styles.css index ff3a2d5..c871c5e 100644 --- a/packages/managed-auth-react/src/styles/styles.css +++ b/packages/managed-auth-react/src/styles/styles.css @@ -510,6 +510,18 @@ height: 1.25rem; } +.kma-sso-icon--letter { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: var(--kma-color-muted, #6b7280); + color: #fff; + font-size: 0.75rem; + font-weight: 600; + line-height: 1; +} + /* ---------- Form / Input ---------- */ .kma-form { From 3a4e6855b1944804d9e33f4e94fe73a107ea48ee Mon Sep 17 00:00:00 2001 From: Mason Williams Date: Wed, 13 May 2026 17:00:38 -0400 Subject: [PATCH 2/4] Hybrid SSO icons: inline SVGs for top providers, Simple Icons CDN fallback Keep multicolor inline SVGs for Google, GitHub, GitLab, Microsoft, Facebook, and Apple. Fall back to Simple Icons CDN for any other provider, with a letter avatar as the final fallback. Co-authored-by: Cursor --- .../src/components/icons.tsx | 82 +++++++++++++++++++ .../src/components/sso-provider.tsx | 44 ++++++++-- 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/packages/managed-auth-react/src/components/icons.tsx b/packages/managed-auth-react/src/components/icons.tsx index 1624e75..9dbd6af 100644 --- a/packages/managed-auth-react/src/components/icons.tsx +++ b/packages/managed-auth-react/src/components/icons.tsx @@ -198,3 +198,85 @@ export const SpinnerIcon = (p: IconProps) => ( /> ); + +// SSO provider marks +export const GoogleMark = (p: IconProps) => ( + +); + +export const GitHubMark = (p: IconProps) => ( + +); + +export const GitLabMark = (p: IconProps) => ( + +); + +export const MicrosoftMark = (p: IconProps) => ( + +); + +export const FacebookMark = (p: IconProps) => ( + +); + +export const AppleMark = (p: IconProps) => ( + +); diff --git a/packages/managed-auth-react/src/components/sso-provider.tsx b/packages/managed-auth-react/src/components/sso-provider.tsx index d82c32b..7548de6 100644 --- a/packages/managed-auth-react/src/components/sso-provider.tsx +++ b/packages/managed-auth-react/src/components/sso-provider.tsx @@ -1,15 +1,34 @@ import { useState, type ReactNode } from "react"; -import { BuildingIcon, KeyIcon } from "./icons"; +import { + AppleMark, + BuildingIcon, + FacebookMark, + GitHubMark, + GitLabMark, + GoogleMark, + KeyIcon, + MicrosoftMark, +} from "./icons"; export interface SSOProviderInfo { label: string; icon: ReactNode; } -const NON_BRAND_ICONS: Record = { - passkey: { label: "Passkey", icon: }, - sso: { label: "SSO", icon: }, - saml: { label: "SSO", icon: }, +const BUILTIN_PROVIDERS: Record< + string, + { label: string; Icon: (p: { className?: string }) => ReactNode } +> = { + google: { label: "Google", Icon: GoogleMark }, + github: { label: "GitHub", Icon: GitHubMark }, + gitlab: { label: "GitLab", Icon: GitLabMark }, + microsoft: { label: "Microsoft", Icon: MicrosoftMark }, + azure: { label: "Microsoft", Icon: MicrosoftMark }, + facebook: { label: "Facebook", Icon: FacebookMark }, + apple: { label: "Apple", Icon: AppleMark }, + passkey: { label: "Passkey", Icon: KeyIcon }, + sso: { label: "SSO", Icon: BuildingIcon }, + saml: { label: "SSO", Icon: BuildingIcon }, }; function slugify(provider: string): string { @@ -24,7 +43,7 @@ function titleCase(provider: string): string { .join(" "); } -function SSOProviderIcon({ provider }: { provider: string }) { +function CDNProviderIcon({ provider }: { provider: string }) { const [errored, setErrored] = useState(false); const slug = slugify(provider); const letter = provider.trim().charAt(0).toUpperCase() || "?"; @@ -49,10 +68,17 @@ function SSOProviderIcon({ provider }: { provider: string }) { export function getSSOProviderInfo(provider: string): SSOProviderInfo { const key = slugify(provider); - const nonBrand = NON_BRAND_ICONS[key]; - if (nonBrand) return nonBrand; + + const builtin = BUILTIN_PROVIDERS[key]; + if (builtin) { + return { + label: builtin.label, + icon: , + }; + } + return { label: titleCase(provider), - icon: , + icon: , }; } From 65cc025b41a811b5de5f42f5d787fd98a4a242e5 Mon Sep 17 00:00:00 2001 From: masnwilliams <43387599+masnwilliams@users.noreply.github.com> Date: Tue, 12 May 2026 23:27:14 +0000 Subject: [PATCH 3/4] fix sso provider matching and stale error state --- .../src/components/sso-provider.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/managed-auth-react/src/components/sso-provider.tsx b/packages/managed-auth-react/src/components/sso-provider.tsx index 7548de6..c387209 100644 --- a/packages/managed-auth-react/src/components/sso-provider.tsx +++ b/packages/managed-auth-react/src/components/sso-provider.tsx @@ -1,4 +1,4 @@ -import { useState, type ReactNode } from "react"; +import { useEffect, useState, type ReactNode } from "react"; import { AppleMark, BuildingIcon, @@ -45,6 +45,7 @@ function titleCase(provider: string): string { function CDNProviderIcon({ provider }: { provider: string }) { const [errored, setErrored] = useState(false); + useEffect(() => setErrored(false), [provider]); const slug = slugify(provider); const letter = provider.trim().charAt(0).toUpperCase() || "?"; @@ -67,10 +68,13 @@ function CDNProviderIcon({ provider }: { provider: string }) { } export function getSSOProviderInfo(provider: string): SSOProviderInfo { - const key = slugify(provider); + const slug = slugify(provider); - const builtin = BUILTIN_PROVIDERS[key]; - if (builtin) { + const builtinKey = Object.keys(BUILTIN_PROVIDERS).find((k) => + slug.includes(k), + ); + if (builtinKey) { + const builtin = BUILTIN_PROVIDERS[builtinKey]; return { label: builtin.label, icon: , From d518bb4d2b2f510c6b7fa611c188914d8223a84e Mon Sep 17 00:00:00 2001 From: masnwilliams <43387599+masnwilliams@users.noreply.github.com> Date: Tue, 12 May 2026 23:47:26 +0000 Subject: [PATCH 4/4] track errored slug to avoid stale fallback flash useEffect runs after paint, so when the provider prop changes the first render still sees errored=true and briefly shows the letter avatar. Tracking the errored slug directly makes the check happen during render. --- packages/managed-auth-react/src/components/icons.tsx | 10 ++-------- .../managed-auth-react/src/components/sso-provider.tsx | 9 ++++----- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/managed-auth-react/src/components/icons.tsx b/packages/managed-auth-react/src/components/icons.tsx index 9dbd6af..431bc57 100644 --- a/packages/managed-auth-react/src/components/icons.tsx +++ b/packages/managed-auth-react/src/components/icons.tsx @@ -230,10 +230,7 @@ export const GitHubMark = (p: IconProps) => ( export const GitLabMark = (p: IconProps) => (