Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions LAUNCH_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ production `assertEntitlementDurability()` **throws on boot** without Upstash.

### 1.5 Google OAuth + NextAuth (sign-in) — ✅ LIVE (providers 200)
Verified 2026-07-14: `/api/auth/providers` returns Google and `/api/auth/csrf` returns a token, so `NEXTAUTH_SECRET` + Google creds are set in Production. Auth is Google-only and stays **off until `NEXTAUTH_SECRET` is set**.
- Create a Google OAuth app (Authorized redirect URI:
`https://<domain>/api/auth/callback/google`).
- Create a Google OAuth app and set Authorized redirect URI to the canonical
production callback: `https://uvai.io/api/auth/callback/google`.
- Set in `apps/web/.env.local`:
```
NEXTAUTH_SECRET=... # openssl rand -base64 32
NEXTAUTH_URL=https://<domain>
GOOGLE_OAUTH_CLIENT_ID=...
GOOGLE_OAUTH_CLIENT_SECRET=...
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
```
- Decision: confirm Google-only sign-up is acceptable for paying customers
(no email/password path exists today).
Expand Down
5 changes: 2 additions & 3 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# NextAuth / Google OAuth
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-here
GOOGLE_OAUTH_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET=your-google-client-secret
# GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET are also supported.
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-client-secret

# Stripe (test keys for local; production via Vercel env)
STRIPE_SECRET_KEY=sk_test_...
Expand Down
14 changes: 9 additions & 5 deletions apps/web/src/lib/__tests__/auth-config-source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ describe('auth configuration source safety', () => {
expect(source).not.toContain("signIn: '/api/auth/signin'");
});

it('accepts both project-specific and common Google OAuth env names', () => {
it('uses a single Google OAuth env naming convention', () => {
const source = readSource('lib/auth.ts');
expect(source).toContain('GOOGLE_OAUTH_CLIENT_ID');
expect(source).toContain('GOOGLE_CLIENT_ID');
expect(source).toContain('GOOGLE_OAUTH_CLIENT_SECRET');
expect(source).toContain('GOOGLE_CLIENT_SECRET');
expect(source).toContain("const googleClientId = (process.env.GOOGLE_CLIENT_ID || '').trim();");
expect(source).toContain(
"const googleClientSecret = (process.env.GOOGLE_CLIENT_SECRET || '').trim();",
);
expect(source).not.toContain('process.env.GOOGLE_OAUTH_CLIENT_ID || process.env.GOOGLE_CLIENT_ID');
expect(source).not.toContain(
'process.env.GOOGLE_OAUTH_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET',
);
});

it('keeps the root route as a landing page instead of redirecting to the app', () => {
Expand Down
28 changes: 15 additions & 13 deletions apps/web/src/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,36 @@ import type { NextAuthOptions } from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

const allowedDomain = process.env.AUTH_ALLOWED_EMAIL_DOMAIN?.trim().toLowerCase();
const googleClientId = (
process.env.GOOGLE_OAUTH_CLIENT_ID ||
process.env.GOOGLE_CLIENT_ID ||
''
).trim();
const googleClientSecret = (
process.env.GOOGLE_OAUTH_CLIENT_SECRET ||
process.env.GOOGLE_CLIENT_SECRET ||
''
).trim();
const googleClientId = (process.env.GOOGLE_CLIENT_ID || '').trim();
const googleClientSecret = (process.env.GOOGLE_CLIENT_SECRET || '').trim();

/**
* NextAuth configuration (Google OAuth by default).
*
* Required env to activate login-gating: NEXTAUTH_SECRET, NEXTAUTH_URL,
* GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET.
* Also accepts NextAuth's common GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET names.
* GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET.
* Optional: AUTH_ALLOWED_EMAIL_DOMAIN restricts sign-in to a single domain
* (e.g. `yourcompany.com` → only *@yourcompany.com).
*
* To use a different identity provider, swap GoogleProvider here — the gating
* in proxy.ts is provider-agnostic (it only checks for a valid JWT session).
*/
function buildProviders(): NextAuthOptions['providers'] {
if (process.env.NODE_ENV === 'production') {
if (
process.env.GOOGLE_OAUTH_CLIENT_ID ||
process.env.GOOGLE_OAUTH_CLIENT_SECRET
) {
console.error(
'[auth] Deprecated Google OAuth env vars detected. Use GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in Vercel production.',
);
}
}

if (!googleClientId || !googleClientSecret) {
if (process.env.NODE_ENV === 'production') {
console.error(
'[auth] Google OAuth client id/secret missing — set GOOGLE_OAUTH_CLIENT_ID / GOOGLE_OAUTH_CLIENT_SECRET or GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET.',
'[auth] Google OAuth client id/secret missing — set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in Vercel production.',
);
}
}
Expand Down
Loading