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
- Create OAuth App at https://github.com/settings/developers
- Set Authorization callback URL:
http://localhost:3000/api/auth/callback/github - Add credentials to
.env:
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
Google OAuth
- Create project at https://console.cloud.google.com
- Enable Google+ API
- Create OAuth 2.0 credentials
- Add authorized redirect URI:
http://localhost:3000/api/auth/callback/google - Add credentials to
.env:
GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your_client_secret
Discord OAuth
- Create application at https://discord.com/developers/applications
- Add OAuth2 redirect:
http://localhost:3000/api/auth/callback/discord - 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
- Callback URLs: Add all environments (dev, staging, prod) to OAuth app
- Scopes: Request minimum scopes needed
- Token Storage: Better Auth stores tokens securely in database
- Token Refresh: Implement automatic token refresh for long-lived sessions
- Account Linking: Enable for better UX when user signs in with different providers
- Error Handling: Provide clear error messages for OAuth failures
- Provider Icons: Use official brand assets for OAuth buttons
- Mobile Deep Links: Configure deep links for mobile OAuth flows
- Email Matching: Consider auto-linking accounts with same email
- 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
}
}
});