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/googleAdd to GitHub OAuth App → Authorization callback URL:
http://localhost:3000/api/auth/callback/githubSession 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 } =
authComponent.triggersApi();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:3001Sync to Convex:
npx convex env set ALLOWED_WEB_ORIGINS http://localhost:3000,http://localhost:3001Restart 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 32Related Documentation
- Authentication Overview - BetterAuth + Convex integration architecture
- Protected Pages - Server-side session validation patterns
- Local Development - Environment setup troubleshooting