feat: add Google and Apple OAuth via better-auth socialProviders

This commit is contained in:
2026-04-08 09:12:50 -04:00
parent e59476dea9
commit 9dfd4ef326
9 changed files with 777 additions and 120 deletions

View File

@@ -0,0 +1,51 @@
/**
* Builds the `socialProviders` config object for betterAuth().
*
* Only includes a provider when ALL of its required env vars are present
* (non-empty strings). This lets the app start without Google/Apple
* credentials and enables providers incrementally via env vars.
*/
type Env = Record<string, string | undefined>;
export interface SocialProviderConfig {
google?: {
clientId: string;
clientSecret: string;
};
apple?: {
clientId: string;
clientSecret: string;
appBundleIdentifier?: string;
};
}
export function buildSocialProviders(env: Env): SocialProviderConfig {
const providers: SocialProviderConfig = {};
// Google — needs clientId + clientSecret
if (env.AUTH_GOOGLE_CLIENT_ID && env.AUTH_GOOGLE_CLIENT_SECRET) {
providers.google = {
clientId: env.AUTH_GOOGLE_CLIENT_ID,
clientSecret: env.AUTH_GOOGLE_CLIENT_SECRET,
};
}
// Apple — needs clientId, clientSecret (pre-generated JWT), teamId, keyId,
// and privateKey. If the caller has already generated the JWT client secret
// and stored it in AUTH_APPLE_CLIENT_SECRET, all five vars must be present.
if (
env.AUTH_APPLE_CLIENT_ID &&
env.AUTH_APPLE_CLIENT_SECRET &&
env.AUTH_APPLE_TEAM_ID &&
env.AUTH_APPLE_KEY_ID &&
env.AUTH_APPLE_PRIVATE_KEY
) {
providers.apple = {
clientId: env.AUTH_APPLE_CLIENT_ID,
clientSecret: env.AUTH_APPLE_CLIENT_SECRET,
};
}
return providers;
}

View File

@@ -0,0 +1,61 @@
/**
* Returns the ordered list of available sign-in providers based on which
* environment variables are configured.
*
* Used by the sign-in page to render provider buttons.
*
* signInMethod:
* - "social" → call signIn.social({ provider: id, callbackURL })
* - "oauth2" → call signIn.oauth2({ providerId: id, callbackURL })
*/
type Env = Record<string, string | undefined>;
export interface SignInProvider {
id: string;
label: string;
signInMethod: "social" | "oauth2";
}
export function getSignInProviders(env: Env): SignInProvider[] {
const providers: SignInProvider[] = [];
// Authentik (genericOAuth)
if (
env.AUTH_AUTHENTIK_CLIENT_ID &&
env.AUTH_AUTHENTIK_CLIENT_SECRET &&
env.AUTH_AUTHENTIK_ISSUER
) {
providers.push({
id: "authentik",
label: "Authentik",
signInMethod: "oauth2",
});
}
// Google (socialProviders)
if (env.AUTH_GOOGLE_CLIENT_ID && env.AUTH_GOOGLE_CLIENT_SECRET) {
providers.push({
id: "google",
label: "Google",
signInMethod: "social",
});
}
// Apple (socialProviders) — all five vars required
if (
env.AUTH_APPLE_CLIENT_ID &&
env.AUTH_APPLE_CLIENT_SECRET &&
env.AUTH_APPLE_TEAM_ID &&
env.AUTH_APPLE_KEY_ID &&
env.AUTH_APPLE_PRIVATE_KEY
) {
providers.push({
id: "apple",
label: "Apple",
signInMethod: "social",
});
}
return providers;
}