better-auth-social-login
SkillDev toolsA skill for dev tools by theprimeagen.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the better-auth-social-login skill
What this skill tells your AI
The instructions your AI receives, as published by theprimeagen/skills in skills/better-auth-social-login/SKILL.md and read by ahel’s review.
better-auth-social-login
Goal
Implement Better Auth social login (OAuth/OIDC) with Drizzle and drizzle-kit using a secure, migration-safe workflow, including cookie/JWT hardening and exact auth table expectations.
Rules
- Configure providers under
socialProviderswith valid OAuth credentials and provider redirect URIs that exactly match your Better Auth callback route. - Set a correct
baseURL(orBETTER_AUTH_URL) for each environment; callback mismatch is the most common social login failure. - For Drizzle projects, do not use
@better-auth/cli migrate; use@better-auth/cli generatethendrizzle-kit generateanddrizzle-kit migrate. - Always run Better Auth schema generation with
--outputto a dedicated auth schema file. - Keep Better Auth config in a CLI-discoverable file path or pass
--configexplicitly. - Keep auth cookies
httpOnly; keepsameSiteatlaxunless you have a specific cross-site requirement. - Keep CSRF and origin protections enabled (
disableCSRFCheck: false,disableOriginCheck: falseby default). - Use
trustedOriginsas an explicit allowlist for browser origins that are allowed to use auth endpoints. - For shared-subdomain sessions, enable
crossSubDomainCookiesonly when needed and scopedomainas narrowly as possible. - If using JWT plugin, treat JWT as service token support, not a replacement for session-based browser auth.
- Rotate provider secrets and revoke compromised refresh/access tokens through provider consoles.
- Commit config, generated auth schema, and Drizzle migration artifacts together.
End-to-End Social Flow
- User initiates sign-in via
authClient.signIn.social({ provider }). - Better Auth redirects to provider authorize endpoint with callback URL, state, and PKCE values.
- Provider redirects to
/api/auth/callback/{provider}. - Better Auth validates OAuth state/PKCE and origin safeguards.
- Better Auth resolves/creates
userandaccountrecords. - Better Auth creates/updates
session, sets signed cookie(s), and returns control to app callback path.
Canonical Server Config (Drizzle + Social + JWT)
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { jwt } from "better-auth/plugins";
import { db, schema } from "../db";
export const auth = betterAuth({
baseURL: process.env.BETTER_AUTH_URL,
database: drizzleAdapter(db, {
provider: "pg",
schema,
}),
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
accessType: "offline",
prompt: "select_account consent",
},
},
trustedOrigins: [
"http://localhost:3000",
"https://app.example.com",
],
advanced: {
useSecureCookies: true,
// Keep origin and CSRF checks enabled by default.
// disableOriginCheck: false,
// disableCSRFCheck: false,
},
plugins: [
jwt(),
],
});
Cookie and Security Baseline
- Cookies are signed by Better Auth secret,
httpOnly, andsameSite=laxby default. secureis enabled in production mode; setadvanced.useSecureCookies: trueto force secure cookies.- Use
trustedOriginsto allow only known origins and block CSRF/open redirect vectors. - Keep OAuth callback URLs exact by scheme/host/port/path.
- Do not disable
disableCSRFCheckordisableOriginCheckexcept in controlled debugging. - For Safari cross-domain setups, use reverse proxy or shared parent domain with careful cookie domain scoping.
JWT Plugin Guidance
- Add
jwt()server plugin andjwtClient()client plugin when downstream services need bearer-style JWT. - Use
/api/auth/tokento retrieve JWT and/api/auth/jwksfor verifier keys. - Verify JWT in services using JWKS (
kid-aware cache strategy). - Default issuer/audience are based on
baseURL; set explicit values when required by consumers. - JWT plugin adds
jwkstable; include it in schema generation and migrations.
Exact Table Formation (Core + JWT)
Core Better Auth tables:
userid(pk)nameemail(unique)emailVerifiedimage(nullable)createdAtupdatedAt
sessionid(pk)userId(fk -> user.id)token(unique)expiresAtipAddress(nullable)userAgent(nullable)createdAtupdatedAt
accountid(pk)userId(fk -> user.id)accountId(provider account id)providerId(google/github/etc)accessToken(nullable)refreshToken(nullable)idToken(nullable)accessTokenExpiresAt(nullable)refreshTokenExpiresAt(nullable)scope(nullable)password(nullable; credential auth)createdAtupdatedAt
verificationid(pk)identifiervalueexpiresAtcreatedAtupdatedAt
JWT plugin table:
jwksid(pk)publicKeyprivateKeycreatedAtexpiresAt(nullable)
Notes:
- Better Auth also manages OAuth state/PKCE protections as part of its OAuth flow; keep generated schema current whenever auth/plugins change.
- Names can be customized (
modelName,fields, pluginschemamapping), but mappings must remain consistent across auth config, Drizzle schema, and migrations.
Drizzle Schema and Config Pattern
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: ["./src/db/schema.ts", "./src/db/auth-schema.ts"],
out: "./drizzle",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
Required Scripts
{
"scripts": {
"auth:schema": "npx @better-auth/cli@latest generate --config ./src/lib/auth.ts --output ./src/db/auth-schema.ts --yes",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:check": "drizzle-kit check",
"db:sync": "npm run auth:schema && npm run db:generate",
"db:sync:migrate": "npm run db:sync && npm run db:migrate"
}
}
Migration Workflow (Auth + Social Changes)
- Change social provider config, auth options, or plugins.
- Run
npm run auth:schema. - Run
npm run db:generate. - Review SQL migration output in
drizzle/*. - Run
npm run db:migrate. - Validate sign-in callback and session persistence in browser.
- Commit auth config +
auth-schema.ts+drizzle/*artifacts together.
Provider Setup Checklist
- GitHub redirect URI:
http://localhost:3000/api/auth/callback/github(dev) and production equivalent. - Google redirect URI:
http://localhost:3000/api/auth/callback/google(dev) and production equivalent. - Ensure
BETTER_AUTH_URL/baseURLmatches the deployed domain used by users. - For GitHub Apps, grant email read permission or expect email lookup failures.
- For Google refresh tokens, prefer
accessType: "offline"with consent prompt.
Common Failure Patterns
redirect_uri_mismatch- Fix: align provider console redirect URI with Better Auth callback and
baseURL.
- Fix: align provider console redirect URI with Better Auth callback and
- Social callback succeeds but no session in browser
- Fix: check cookie
secure/domain/SameSite settings and frontendcredentials: "include"usage.
- Fix: check cookie
- Safari only: session appears lost across domains
- Fix: proxy auth route under frontend domain or use shared parent domain cookie strategy.
- JWT verifies locally but fails in service
- Fix: verify issuer/audience and refresh JWKS on unknown
kid.
- Fix: verify issuer/audience and refresh JWKS on unknown
- Drizzle migration missing plugin tables
- Fix: regenerate auth schema before Drizzle generation and ensure
auth-schema.tsis listed indrizzle.config.ts.
- Fix: regenerate auth schema before Drizzle generation and ensure
Reference Docs
- Better Auth OAuth: https://www.better-auth.com/docs/concepts/oauth
- Better Auth cookies: https://www.better-auth.com/docs/concepts/cookies
- Better Auth security: https://www.better-auth.com/docs/reference/security
- Better Auth database concepts: https://www.better-auth.com/docs/concepts/database
- Better Auth JWT plugin: https://www.better-auth.com/docs/plugins/jwt
- Better Auth GitHub provider: https://www.better-auth.com/docs/authentication/github
- Better Auth Google provider: https://www.better-auth.com/docs/authentication/google
- Better Auth Drizzle adapter: https://www.better-auth.com/docs/adapters/drizzle
- Drizzle Kit overview: https://orm.drizzle.team/docs/kit-overview
- drizzle-kit generate: https://orm.drizzle.team/docs/drizzle-kit-generate
- drizzle-kit migrate: https://orm.drizzle.team/docs/drizzle-kit-migrate
Signals
- GitHub stars
- 233
- Forks
- 5
- Last commit
- Feb 2026
Advanced
- Catalog kind
- skill
- Gateway key
better-auth-social-login- Source
- github.com/theprimeagen/skills