Files
gh-rafaelcalleja-claude-mar…/skills/better-auth/references/oauth-providers.md
2025-11-30 08:48:52 +08:00

9.1 KiB

OAuth Providers

Better Auth provides built-in OAuth 2.0 support for social authentication. No plugins required.

Supported Providers

GitHub, Google, Apple, Discord, Facebook, Microsoft, Twitter/X, Spotify, Twitch, LinkedIn, Dropbox, GitLab, and more.

Basic OAuth Setup

Server Configuration

import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
      // Optional: custom scopes
      scope: ["user:email", "read:user"]
    },
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      scope: ["openid", "email", "profile"]
    },
    discord: {
      clientId: process.env.DISCORD_CLIENT_ID!,
      clientSecret: process.env.DISCORD_CLIENT_SECRET!,
    }
  }
});

Client Usage

import { authClient } from "@/lib/auth-client";

// Basic sign in
await authClient.signIn.social({
  provider: "github",
  callbackURL: "/dashboard"
});

// With callbacks
await authClient.signIn.social({
  provider: "google",
  callbackURL: "/dashboard",
  errorCallbackURL: "/error",
  newUserCallbackURL: "/welcome", // For first-time users
});

Provider Configuration

GitHub OAuth

  1. Create OAuth App at https://github.com/settings/developers
  2. Set Authorization callback URL: http://localhost:3000/api/auth/callback/github
  3. Add credentials to .env:
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret

Google OAuth

  1. Create project at https://console.cloud.google.com
  2. Enable Google+ API
  3. Create OAuth 2.0 credentials
  4. Add authorized redirect URI: http://localhost:3000/api/auth/callback/google
  5. Add credentials to .env:
GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your_client_secret

Discord OAuth

  1. Create application at https://discord.com/developers/applications
  2. Add OAuth2 redirect: http://localhost:3000/api/auth/callback/discord
  3. Add credentials:
DISCORD_CLIENT_ID=your_client_id
DISCORD_CLIENT_SECRET=your_client_secret

Apple Sign In

export const auth = betterAuth({
  socialProviders: {
    apple: {
      clientId: process.env.APPLE_CLIENT_ID!,
      clientSecret: process.env.APPLE_CLIENT_SECRET!,
      teamId: process.env.APPLE_TEAM_ID!,
      keyId: process.env.APPLE_KEY_ID!,
      privateKey: process.env.APPLE_PRIVATE_KEY!
    }
  }
});

Microsoft/Azure AD

export const auth = betterAuth({
  socialProviders: {
    microsoft: {
      clientId: process.env.MICROSOFT_CLIENT_ID!,
      clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
      tenantId: process.env.MICROSOFT_TENANT_ID, // Optional: for specific tenant
    }
  }
});

Twitter/X OAuth

export const auth = betterAuth({
  socialProviders: {
    twitter: {
      clientId: process.env.TWITTER_CLIENT_ID!,
      clientSecret: process.env.TWITTER_CLIENT_SECRET!,
    }
  }
});

Custom OAuth Provider

Add custom OAuth 2.0 provider:

import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    customProvider: {
      clientId: process.env.CUSTOM_CLIENT_ID!,
      clientSecret: process.env.CUSTOM_CLIENT_SECRET!,
      authorizationUrl: "https://provider.com/oauth/authorize",
      tokenUrl: "https://provider.com/oauth/token",
      userInfoUrl: "https://provider.com/oauth/userinfo",
      scope: ["email", "profile"],
      // Map provider user data to Better Auth user
      mapProfile: (profile) => ({
        id: profile.id,
        email: profile.email,
        name: profile.name,
        image: profile.avatar_url
      })
    }
  }
});

Account Linking

Link multiple OAuth providers to same user account.

Server Setup

export const auth = betterAuth({
  account: {
    accountLinking: {
      enabled: true,
      trustedProviders: ["google", "github"] // Auto-link these providers
    }
  }
});

Client Usage

// Link new provider to existing account
await authClient.linkSocial({
  provider: "google",
  callbackURL: "/profile"
});

// List linked accounts
const { data: session } = await authClient.getSession();
const accounts = session.user.accounts;

// Unlink account
await authClient.unlinkAccount({
  accountId: "account-id"
});

Token Management

Access OAuth Tokens

// Server-side
const session = await auth.api.getSession({
  headers: request.headers
});

const accounts = await auth.api.listAccounts({
  userId: session.user.id
});

// Get specific provider token
const githubAccount = accounts.find(a => a.providerId === "github");
const accessToken = githubAccount.accessToken;
const refreshToken = githubAccount.refreshToken;

Refresh Tokens

// Manually refresh OAuth token
const newToken = await auth.api.refreshToken({
  accountId: "account-id"
});

Use Provider API

// Example: Use GitHub token to fetch repos
const githubAccount = accounts.find(a => a.providerId === "github");

const response = await fetch("https://api.github.com/user/repos", {
  headers: {
    Authorization: `Bearer ${githubAccount.accessToken}`
  }
});

const repos = await response.json();

Advanced OAuth Configuration

Custom Scopes

export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
      scope: [
        "user:email",
        "read:user",
        "repo", // Access repositories
        "gist" // Access gists
      ]
    }
  }
});

State Parameter

Better Auth automatically handles OAuth state parameter for CSRF protection.

// Custom state validation
export const auth = betterAuth({
  advanced: {
    generateState: async () => {
      // Custom state generation
      return crypto.randomUUID();
    },
    validateState: async (state: string) => {
      // Custom state validation
      return true;
    }
  }
});

PKCE Support

Better Auth automatically uses PKCE (Proof Key for Code Exchange) for supported providers.

export const auth = betterAuth({
  socialProviders: {
    customProvider: {
      pkce: true, // Enable PKCE
      // ... other config
    }
  }
});

Error Handling

Client-Side

await authClient.signIn.social({
  provider: "github",
  errorCallbackURL: "/auth/error"
}, {
  onError: (ctx) => {
    console.error("OAuth error:", ctx.error);
    // Handle specific errors
    if (ctx.error.code === "OAUTH_ACCOUNT_ALREADY_LINKED") {
      alert("This account is already linked to another user");
    }
  }
});

Server-Side

export const auth = betterAuth({
  callbacks: {
    async onOAuthError({ error, provider }) {
      console.error(`OAuth error with ${provider}:`, error);
      // Log to monitoring service
      await logError(error);
    }
  }
});

Callback URLs

Development

http://localhost:3000/api/auth/callback/{provider}

Production

https://yourdomain.com/api/auth/callback/{provider}

Important: Add all callback URLs to OAuth provider settings.

UI Components

Sign In Button (React)

import { authClient } from "@/lib/auth-client";

export function SocialSignIn() {
  const handleOAuth = async (provider: string) => {
    await authClient.signIn.social({
      provider,
      callbackURL: "/dashboard"
    });
  };

  return (
    <div className="space-y-2">
      <button onClick={() => handleOAuth("github")}>
        Sign in with GitHub
      </button>
      <button onClick={() => handleOAuth("google")}>
        Sign in with Google
      </button>
      <button onClick={() => handleOAuth("discord")}>
        Sign in with Discord
      </button>
    </div>
  );
}

Best Practices

  1. Callback URLs: Add all environments (dev, staging, prod) to OAuth app
  2. Scopes: Request minimum scopes needed
  3. Token Storage: Better Auth stores tokens securely in database
  4. Token Refresh: Implement automatic token refresh for long-lived sessions
  5. Account Linking: Enable for better UX when user signs in with different providers
  6. Error Handling: Provide clear error messages for OAuth failures
  7. Provider Icons: Use official brand assets for OAuth buttons
  8. Mobile Deep Links: Configure deep links for mobile OAuth flows
  9. Email Matching: Consider auto-linking accounts with same email
  10. Privacy: Inform users what data you access from OAuth providers

Common Issues

Redirect URI Mismatch

Ensure callback URL in OAuth app matches exactly:

http://localhost:3000/api/auth/callback/github

Missing Scopes

Add required scopes for email access:

scope: ["user:email"] // GitHub
scope: ["email"] // Google

HTTPS Required

Some providers (Apple, Microsoft) require HTTPS callbacks. Use ngrok for local development:

ngrok http 3000

CORS Errors

Configure CORS if frontend/backend on different domains:

export const auth = betterAuth({
  advanced: {
    corsOptions: {
      origin: ["https://yourdomain.com"],
      credentials: true
    }
  }
});