/**
* Identity Handler Factory
*
* Selects the appropriate OAuth handler based on the IDENTITY_PROVIDER environment variable.
*
* Usage:
* // In index.ts
* const handler = getIdentityHandler(env);
* return handler.fetch(request, env, ctx);
*/
import type { Hono } from 'hono';
import type { Env } from '../types';
import { GoogleHandler } from './google-handler';
import { MicrosoftHandler } from './microsoft-handler';
import { GitHubHandler } from './github-handler';
import { BetterAuthHandler } from './better-auth-handler';
/**
* Supported identity providers
* - 'better-auth': Uses better-auth for social login (recommended)
* - 'google', 'microsoft', 'github': Legacy DIY OAuth handlers
*/
export type IdentityProviderType = 'google' | 'microsoft' | 'github' | 'better-auth';
/**
* Handler type (Hono app with Env bindings)
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type IdentityHandler = Hono<any>;
/**
* Get the OAuth handler for the configured identity provider.
*
* @param env - Environment bindings (must include IDENTITY_PROVIDER)
* @returns The appropriate Hono handler app
* @throws If required credentials are missing for the selected provider
*/
export function getIdentityHandler(env: Env): IdentityHandler {
const provider = (env.IDENTITY_PROVIDER || 'google') as IdentityProviderType;
switch (provider) {
case 'better-auth':
validateBetterAuthEnv(env);
return BetterAuthHandler;
case 'microsoft':
validateMicrosoftEnv(env);
return MicrosoftHandler;
case 'github':
validateGitHubEnv(env);
return GitHubHandler;
case 'google':
default:
validateGoogleEnv(env);
return GoogleHandler;
}
}
/**
* Validate that better-auth is configured
*/
function validateBetterAuthEnv(env: Env): void {
if (!env.BETTER_AUTH_SECRET) {
throw new Error(
'Missing better-auth secret. Set BETTER_AUTH_SECRET secret.'
);
}
if (!env.DB) {
throw new Error(
'Missing D1 database binding. Configure DB binding in wrangler.jsonc.'
);
}
// At least one social provider should be configured
const hasProvider = env.GOOGLE_CLIENT_ID || env.MICROSOFT_CLIENT_ID || env.GITHUB_CLIENT_ID;
if (!hasProvider) {
throw new Error(
'No social providers configured. Set at least one of: GOOGLE_CLIENT_ID, MICROSOFT_CLIENT_ID, or GITHUB_CLIENT_ID.'
);
}
}
/**
* Validate that required Google OAuth credentials are set
*/
function validateGoogleEnv(env: Env): void {
if (!env.GOOGLE_CLIENT_ID || !env.GOOGLE_CLIENT_SECRET) {
throw new Error(
'Missing Google OAuth credentials. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET secrets.'
);
}
}
/**
* Validate that required Microsoft OAuth credentials are set
*/
function validateMicrosoftEnv(env: Env): void {
if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_CLIENT_SECRET) {
throw new Error(
'Missing Microsoft OAuth credentials. Set MICROSOFT_CLIENT_ID and MICROSOFT_CLIENT_SECRET secrets.'
);
}
// MICROSOFT_TENANT_ID is optional (defaults to 'common')
}
/**
* Validate that required GitHub OAuth credentials are set
*/
function validateGitHubEnv(env: Env): void {
if (!env.GITHUB_CLIENT_ID || !env.GITHUB_CLIENT_SECRET) {
throw new Error(
'Missing GitHub OAuth credentials. Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET secrets.'
);
}
}
/**
* Get the provider name from environment (with default)
*/
export function getProviderName(env: Env): IdentityProviderType {
return (env.IDENTITY_PROVIDER || 'google') as IdentityProviderType;
}
/**
* Check if a provider supports token refresh
*/
export function supportsTokenRefresh(provider: IdentityProviderType): boolean {
switch (provider) {
case 'github':
return false; // GitHub tokens don't expire
case 'google':
case 'microsoft':
default:
return true;
}
}