Authentication
Resolve sign-in, session, and redirect issues
Authentication issues resolve through systematic diagnosis. Start with environment variables, then check OAuth configuration, then verify Convex setup.
Sign-In Redirect Loop
Symptoms: Browser redirects endlessly between /sign-in
and protected routes. Console shows UNAUTHENTICATED
errors.
Diagnosis: Session cookies not persisting or Convex cannot verify tokens. Check DevTools → Application → Cookies for session cookie.
Solution:
# Verify environment variables
grep APP_BASE_URL .env.local
grep BETTER_AUTH_SECRET .env.local
grep CONVEX_SITE_URL .env.local
# Sync to Convex
npx convex env set APP_BASE_URL http://localhost:3000
npx convex env set DASHBOARD_BASE_URL http://localhost:3001
npx convex env set BETTER_AUTH_SECRET <same-as-local>
Clear browser cookies and test again.
OAuth Callback Errors
Symptoms: redirect_uri_mismatch
error from Google/GitHub OAuth.
Diagnosis: OAuth provider redirect URIs don't match application URLs exactly.
Solution:
Add to Google Cloud Console → Credentials → OAuth 2.0 Client → Authorized redirect URIs:
http://localhost:3000/api/auth/callback/google
http://localhost:3001/api/auth/callback/google
https://your-domain.com/api/auth/callback/google
Add to GitHub OAuth App → Authorization callback URL:
http://localhost:3000/api/auth/callback/github
Session Not Persisting
Symptoms: User signs in successfully but session doesn't persist across page reloads.
Diagnosis: Cookie attributes incompatible with environment (Secure flag on HTTP, domain mismatch).
Solution:
Development uses non-secure cookies:
const isProduction = process.env.NODE_ENV === "production";
const cookieName = isProduction ? "__Host-session" : "session";
const sessionCookie = {
name: cookieName,
httpOnly: true,
path: "/",
sameSite: "lax" as const,
secure: isProduction, // false in development
};
Verify NODE_ENV
is not accidentally set to production
in development.
Missing User Data After Sign-In
Symptoms: Sign-in succeeds but getCurrentSession()
returns null
.
Diagnosis: BetterAuth component not creating Convex user records.
Solution:
Verify convex/auth.ts
callbacks are registered:
const authCallbacks: AuthCallbacks = {
onCreateUser: async (ctx, user) => {
const now = Date.now();
return await ctx.db.insert("user", {
name: user.name,
email: user.email,
emailVerified: user.emailVerified,
image: user.image || undefined,
createdAt: now,
updatedAt: now,
});
},
// ... other callbacks
};
export const { createUser, updateUser, deleteUser, createSession } =
betterAuthComponent.createAuthFunctions<DataModel>(authCallbacks);
Check Convex dashboard for user records in user
table.
CORS Errors on Auth Endpoints
Symptoms: CORS policy: No 'Access-Control-Allow-Origin' header
errors in browser console.
Diagnosis: ALLOWED_WEB_ORIGINS
not configured or doesn't include request origin.
Solution:
ALLOWED_WEB_ORIGINS=http://localhost:3000,http://localhost:3001
Sync to Convex:
npx convex env set ALLOWED_WEB_ORIGINS http://localhost:3000,http://localhost:3001
Restart development server.
Convex Auth Token Invalid
Symptoms: getToken()
returns null
even when user is signed in.
Diagnosis: Token verification failing due to secret mismatch or expired session.
Solution:
Verify BETTER_AUTH_SECRET
matches across platform and Convex:
# Check local env
grep BETTER_AUTH_SECRET .env.local
# Check Convex env
npx convex env list
# Sync if different
npx convex env set BETTER_AUTH_SECRET <same-value>
Generate new secret if needed:
openssl rand -base64 32
Related Documentation
- Authentication Overview - BetterAuth + Convex integration architecture
- Protected Pages - Server-side session validation patterns
- Local Development - Environment setup troubleshooting