Files
supabase/apps/studio/lib/external-identity-providers.ts
Saxon Fletcher e491182054 Auth flow improvements (#46967)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES/NO

## What kind of change does this PR introduce?

Bug fix, feature, docs update, ...

## What is the current behavior?

Please link any relevant issues here.

## What is the new behavior?

Feel free to include screenshots if it includes visual changes.

## Additional context

Add any other context or screenshots.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added “Continue with {provider}” sign-in and sign-up flows using
enabled external identity providers.
* Enabled inbound branding to focus a specific provider for customized
sign-in/sign-up experiences.

* **Improvements**
* Refined the sign-in options layout and “last used” tracking for
clearer authentication choices.
* Updated account identity/provider connection experiences (link/unlink
and management UI).

* **Bug Fixes**
  * Fixed hydration mismatches in sign-in and password-related layouts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-06-18 15:34:56 +08:00

117 lines
3.6 KiB
TypeScript

import { BASE_PATH } from './constants'
export type ExternalIdentityProviderConfig = {
id: string
authProvider: string
displayName: string
iconPath: string
scopes?: string
showOnSignIn: boolean
showOnSignUp: boolean
showInAccountPreferences: boolean
}
export type IdentityProviderDisplay = {
id: string
displayName: string
iconPath: string
/** The icon is a single-color mark that should be tinted to the theme's foreground color. */
hasMonochromeIcon?: boolean
}
const BUILT_IN_IDENTITY_PROVIDERS: Record<string, IdentityProviderDisplay> = {
email: {
id: 'email',
displayName: 'Email',
iconPath: `${BASE_PATH}/img/icons/email-icon2.svg`,
},
}
// Statically supported identity providers. To add a new one, declare its config here, gate its
// visibility behind a `dashboard_auth:sign_in_with_*` feature flag in `useEnabledIdentityProviders`,
// and add the matching flag to `packages/common/enabled-features/enabled-features.json`.
export const GITHUB_IDENTITY_PROVIDER: ExternalIdentityProviderConfig = {
id: 'github',
authProvider: 'github',
displayName: 'GitHub',
iconPath: '/img/icons/github-icon.svg',
showOnSignIn: true,
showOnSignUp: true,
showInAccountPreferences: false,
}
// Registry of every known provider, independent of which are currently enabled. Used for config and
// display lookups (e.g. resolving the provider that a mid-flow interstitial was reached with).
const IDENTITY_PROVIDERS: ExternalIdentityProviderConfig[] = [GITHUB_IDENTITY_PROVIDER]
export function normalizeIconPath(iconPath: string): string {
if (
iconPath.startsWith('http://') ||
iconPath.startsWith('https://') ||
iconPath.startsWith('/')
) {
return iconPath.startsWith('/') ? `${BASE_PATH}${iconPath}` : iconPath
}
return `${BASE_PATH}/${iconPath}`
}
export function getProviderDisplay(provider: string): IdentityProviderDisplay {
const config = IDENTITY_PROVIDERS.find(
({ id, authProvider }) => provider === id || provider === authProvider
)
if (config) {
return {
id: config.id,
displayName: config.displayName,
iconPath: normalizeIconPath(config.iconPath),
hasMonochromeIcon: true,
}
}
if (provider.startsWith('sso')) {
return {
id: provider,
displayName: 'SSO',
iconPath: `${BASE_PATH}/img/icons/saml-icon.svg`,
}
}
return (
BUILT_IN_IDENTITY_PROVIDERS[provider] ?? {
id: provider,
displayName: provider.replaceAll('_', ' '),
iconPath: `${BASE_PATH}/img/icons/saml-icon.svg`,
}
)
}
/**
* Builds the absolute URL an external provider's OAuth flow redirects back to: the MFA-check page
* (`/sign-in-mfa`), tagged with the provider id as the sign-in method and an optional `returnTo`
* destination. Callers should pass the result through `buildPathWithParams` to preserve the current
* location's search params across the OAuth round-trip.
*/
export function buildProviderAuthRedirect(providerId: string, returnTo?: string): string {
const origin =
typeof window !== 'undefined' && process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview'
? window.location.origin
: process.env.NEXT_PUBLIC_SITE_URL
const params = new URLSearchParams({ method: providerId })
if (returnTo) params.set('returnTo', returnTo)
return `${origin}${BASE_PATH}/sign-in-mfa?${params.toString()}`
}
export function getIdentityProviderConfig(
provider: string | undefined
): ExternalIdentityProviderConfig | undefined {
if (!provider) return undefined
return IDENTITY_PROVIDERS.find(
({ id, authProvider }) => provider === id || provider === authProvider
)
}