How to Add Social Login with BetterAuth
Social authentication has become the standard for modern web applications, yet many developers still struggle with outdated Auth.js documentation or expensive SaaS solutions. BetterAuth, a framework-agnostic TypeScript authentication library now at version 1.x, offers a self-hosted alternative that’s both powerful and straightforward to implement.
Key Takeaways
- BetterAuth provides a TypeScript-first, self-hosted authentication solution with simple OAuth2 integration
- Configure social providers on the server and use a unified
signIn.social()API on the client - The Generic OAuth plugin enables integration with any OAuth 2.0 or OIDC-compliant provider
- Session management and token handling work out of the box with reactive components
What is BetterAuth?
BetterAuth is a TypeScript-first authentication library that runs on your infrastructure. Unlike NextAuth (now Auth.js) with its complex adapter patterns, or SaaS providers like Clerk that lock you into their pricing, BetterAuth provides a clean API with built-in support for OAuth2 with BetterAuth through its socialProviders configuration.
The library shines in its simplicity: configure providers on the server, create a client with createAuthClient, and call authClient.signIn.social(). No separate sign-up endpoints, no confusing flows—just authentication that works.
Setting Up BetterAuth Social Login
Server Configuration
Start by installing BetterAuth and configuring your authentication server with Google and GitHub login with BetterAuth:
// auth.ts
import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { db } from "./db"
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "sqlite"
}),
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}
}
})
This configuration handles the OAuth2 flow automatically. BetterAuth handles the OAuth exchange and session creation, but you must configure the correct callback URLs in each provider’s dashboard.
Client Setup
Create your authentication client to enable BetterAuth social login in your frontend:
// auth-client.ts
import { createAuthClient } from "better-auth/client"
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL // Your app's base URL
})
Implementing Social Login in Next.js
Here’s how to implement Next.js social authentication with a simple login component:
// components/login-button.tsx
import { authClient } from "@/lib/auth-client"
export function LoginButton() {
const handleGoogleLogin = async () => {
await authClient.signIn.social({
provider: "google",
callbackURL: "/dashboard"
})
}
const handleGitHubLogin = async () => {
await authClient.signIn.social({
provider: "github",
callbackURL: "/dashboard"
})
}
return (
<div className="flex gap-4">
<button onClick={handleGoogleLogin}>
Sign in with Google
</button>
<button onClick={handleGitHubLogin}>
Sign in with GitHub
</button>
</div>
)
}
The signIn.social() method handles everything: redirecting to the provider, handling callbacks, and establishing the session. You can optionally specify errorCallbackURL for failed authentications or newUserCallbackURL for first-time users.
Discover how at OpenReplay.com.
Custom OAuth Providers with Generic OAuth Plugin
For providers beyond the built-in ones, BetterAuth offers the Generic OAuth plugin in BetterAuth. This plugin enables integration with any OAuth 2.0 or OIDC-compliant provider:
// auth.ts with custom provider
import { betterAuth } from "better-auth"
import { genericOAuth } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [{
providerId: "custom-provider",
discoveryUrl: "https://provider.com/.well-known/openid-configuration",
clientId: process.env.CUSTOM_CLIENT_ID!,
clientSecret: process.env.CUSTOM_CLIENT_SECRET!,
scopes: ["openid", "email", "profile"]
}]
})
]
})
This flexibility means you’re not limited to mainstream providers—enterprise SSO, regional services, or internal OAuth servers all work seamlessly.
Session Management and Token Handling
BetterAuth provides reactive session management out of the box:
// components/user-profile.tsx
import { authClient } from "@/lib/auth-client"
export function UserProfile() {
const { data: session, isPending } = authClient.useSession()
if (isPending) return <div>Loading...</div>
if (!session) return <div>Not authenticated</div>
return (
<div>
<p>Welcome, {session.user.name}</p>
<button onClick={() => authClient.signOut()}>
Sign out
</button>
</div>
)
}
A note on tokens: refresh token behavior varies by provider. GitHub, for instance, doesn’t provide refresh tokens by default, while Google does. BetterAuth handles these differences transparently, but understanding your provider’s specifics helps when debugging edge cases.
Production Considerations
When deploying BetterAuth social authentication:
- Environment Variables: Secure your OAuth credentials properly
- Callback URLs: Update provider configurations with production URLs
- Database Migrations: Run your normal database migrations after updating the BetterAuth schema (for example via
npx @better-auth/cli generatewhen using Drizzle). - HTTPS: OAuth2 requires secure connections in production
Account linking—connecting multiple social accounts to one user—is an evolving feature in BetterAuth. The library handles basic cases well, but complex scenarios might require custom logic.
Conclusion
BetterAuth brings modern, TypeScript-first authentication to your stack without the complexity of Auth.js or the costs of SaaS providers. With built-in support for major providers and the Generic OAuth plugin for custom integrations, it covers virtually any OAuth2 with BetterAuth scenario you’ll encounter.
The unified signIn.social() API, reactive session management, and framework-agnostic design make BetterAuth a solid choice for teams wanting control over their authentication infrastructure while maintaining developer productivity.
FAQs
BetterAuth typically has a smaller bundle size due to its modular architecture and TypeScript-first approach. Performance is comparable for basic operations, but BetterAuth's simpler API often results in faster implementation and fewer runtime errors.
Yes, BetterAuth supports multiple database adapters including PostgreSQL, MySQL, and MongoDB through Drizzle ORM. Simply change the provider option in the drizzleAdapter configuration to match your database type.
BetterAuth handles basic account linking automatically by default, preventing duplicate accounts. For complex scenarios like merging existing accounts, you'll need to implement custom logic using BetterAuth's hooks and middleware system.
No, BetterAuth automatically handles token refresh when available from the provider. The library manages token lifecycle transparently, though some providers like GitHub don't provide refresh tokens by default.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.