Skip to content

Authentication

Juca uses NextAuth v5 (Auth.js) for authentication with JWT-based sessions. Two authentication providers are configured: Google OAuth and Resend magic links (email).

The auth system is defined in src/lib/auth.ts:

// Exports
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [Google, Resend],
session: { strategy: 'jwt' },
pages: { signIn: '/auth/signin' },
// ...
});

Required environment variables for production:

VariablePurpose
GOOGLE_CLIENT_IDGoogle OAuth client ID
GOOGLE_CLIENT_SECRETGoogle OAuth client secret
AUTH_SECRETNextAuth JWT secret
RESEND_API_KEYResend API key for magic link emails
AUTH_EMAIL_FROMSender email for magic links
ADMIN_EMAILSComma-separated admin email whitelist

For local development, set ENABLE_DEV_AUTH=true to skip authentication entirely. This returns a hardcoded dev user:

// Dev user returned when ENABLE_DEV_AUTH=true
{ id: 'dev-user', email: 'dev@localhost', name: 'Dev User', role: 'admin' }

Juca uses two distinct auth patterns depending on context:

src/actions/utils.ts
import { requireActionAuth } from '@/actions/utils';
// In any server action:
const user = await requireActionAuth();
// Throws if not authenticated (unless ENABLE_DEV_AUTH=true)
// In any API route handler:
import { auth } from '@/lib/auth';
export async function GET(request: Request) {
const session = await auth();
if (!session?.user) return new Response('Unauthorized', { status: 401 });
// ...
}

Admin users are determined by the ADMIN_EMAILS environment variable (comma-separated, case-insensitive). The isAdmin() function checks membership, and the JWT callback adds a role field to the token.

Auth middleware (src/middleware.ts) protects all routes except static assets and the health endpoint:

src/middleware.ts
export { auth as middleware } from '@/lib/auth';
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|icons|manifest.json|sw.js).*)']
};
ComponentLocationPurpose
AuthProvidersrc/components/auth/AuthProvider.tsxNextAuth SessionProvider wrapper
Sign-in pagesrc/app/auth/signin/page.tsxCustom sign-in UI