Add an OAuth Provider
Add Twitter/X, Discord, or any other OAuth provider to your KitRocket auth system.
KitRocket uses Better Auth, which supports 20+ OAuth providers out of the box. This guide walks through adding a new one using Twitter/X as an example.
Step 1: Register your app with the provider
Go to the Twitter Developer Portal and create a new app.
Set the callback URL to:
http://localhost:3000/api/auth/callback/twitter
For production, also add:
https://yourdomain.com/api/auth/callback/twitter
Note the Client ID and Client Secret.
Step 2: Add environment variables
TWITTER_CLIENT_ID="your-twitter-client-id"
TWITTER_CLIENT_SECRET="your-twitter-client-secret"
Step 3: Configure Better Auth
Add the provider to your auth server configuration:
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/lib/db";
export const auth = betterAuth({
database: drizzleAdapter(db),
secret: process.env.AUTH_SECRET,
baseURL: process.env.BETTER_AUTH_URL,
emailAndPassword: {
enabled: true,
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
// Add the new provider:
twitter: {
clientId: process.env.TWITTER_CLIENT_ID!,
clientSecret: process.env.TWITTER_CLIENT_SECRET!,
},
},
});
Step 4: Add a login button
Add a Twitter sign-in button to the OAuth buttons component:
"use client";
import { signIn } from "@/lib/auth/client";
import { Button } from "@/components/ui/button";
export function OAuthButtons() {
return (
<div className="flex flex-col gap-2">
<Button
variant="outline"
className="w-full"
onClick={() => signIn.social({ provider: "google" })}
>
Continue with Google
</Button>
<Button
variant="outline"
className="w-full"
onClick={() => signIn.social({ provider: "github" })}
>
Continue with GitHub
</Button>
{/* Add the new button: */}
<Button
variant="outline"
className="w-full"
onClick={() => signIn.social({ provider: "twitter" })}
>
Continue with Twitter
</Button>
</div>
);
}
Step 5: Test
- Run
pnpm dev - Go to http://localhost:3000/login
- Click "Continue with Twitter"
- You should be redirected to Twitter's OAuth consent screen
- After authorizing, you're redirected back and logged in
- Check the
accountstable — a new row withprovider: "twitter"should exist
Other popular providers
Discord
discord: {
clientId: process.env.DISCORD_CLIENT_ID!,
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
},
Register at Discord Developer Portal. Callback URL: /api/auth/callback/discord.
Apple
apple: {
clientId: process.env.APPLE_CLIENT_ID!,
clientSecret: process.env.APPLE_CLIENT_SECRET!,
},
Register at Apple Developer. Note: Apple sign-in requires additional configuration (team ID, key file).
linkedin: {
clientId: process.env.LINKEDIN_CLIENT_ID!,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET!,
},
Register at LinkedIn Developer. Callback URL: /api/auth/callback/linkedin.
Production checklist
After testing locally:
- Add the production callback URL to the OAuth provider's settings
- Add the environment variables to Vercel (or your hosting platform)
- Redeploy
- Test the flow on the production URL
- Verify accounts are created correctly in the database
Troubleshooting
"OAuth callback URL mismatch"
The callback URL registered with the provider must exactly match what Better Auth generates. The format is:
{BETTER_AUTH_URL}/api/auth/callback/{provider}
Check that BETTER_AUTH_URL is set correctly for your environment.
"Invalid client_id"
Double-check the environment variables. Make sure there are no trailing spaces or newlines in the values.
User logged in but no name/avatar
Some providers don't return profile data by default. Check the provider's OAuth scope settings and request the appropriate scopes (e.g., profile, email).