StarterApp Docs
Troubleshooting

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:

convex/lib/auth.ts
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:

convex/auth.ts
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:

.env.local
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