test8 / src /app /api /auth /google /callback /route.ts
simikkk's picture
Upload 93 files
eaab0a9 verified
Raw
History Blame Contribute Delete
4.49 kB
import { NextRequest, NextResponse } from 'next/server';
import { withDb } from '@/lib/api-handler';
import { db } from '@/lib/db';
import { createSession, hashPassword, generateApiKey, SESSION_COOKIE_NAME } from '@/lib/auth';
import { logActivity } from '@/lib/activity';
import crypto from 'crypto';
async function handler(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');
if (!code) {
return NextResponse.redirect(new URL('/?error=no_code', request.url));
}
const clientId = process.env.GOOGLE_CLIENT_ID;
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
const redirectUri = `${process.env.NEXT_PUBLIC_BASE_URL || ''}/api/auth/google/callback`;
if (!clientId || !clientSecret) {
return NextResponse.redirect(new URL('/?error=not_configured', request.url));
}
// ── Exchange code for tokens ──────────────────────────────────────
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
code,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri,
grant_type: 'authorization_code',
}),
});
if (!tokenResponse.ok) {
console.error('[GOOGLE_TOKEN_ERROR]', await tokenResponse.text());
return NextResponse.redirect(new URL('/?error=token_exchange_failed', request.url));
}
const tokens = await tokenResponse.json() as { access_token: string };
// ── Fetch user info from Google ───────────────────────────────────
const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
if (!userInfoResponse.ok) {
console.error('[GOOGLE_USER_ERROR]', await userInfoResponse.text());
return NextResponse.redirect(new URL('/?error=user_fetch_failed', request.url));
}
const googleUser = await userInfoResponse.json() as {
email: string;
name?: string;
picture?: string;
};
if (!googleUser.email) {
return NextResponse.redirect(new URL('/?error=no_email', request.url));
}
// ── Find or create user ───────────────────────────────────────────
let user = await db.user.findUnique({ where: { email: googleUser.email } });
if (!user) {
const randomPw = crypto.randomBytes(32).toString('hex');
const hashedPw = await hashPassword(randomPw);
const apiKey = generateApiKey();
user = await db.user.create({
data: {
email: googleUser.email,
name: googleUser.name || googleUser.email.split('@')[0],
password: hashedPw,
apiKey,
plan: 'free',
},
});
logActivity(user.id, 'signup', 'Account created via Google', user.email);
}
// ── Create session ────────────────────────────────────────────────
const token = await createSession(user.id);
const forwardedProto = request.headers.get('x-forwarded-proto');
const isSecure = forwardedProto === 'https' || process.env.NODE_ENV === 'production';
const host = request.headers.get('host') || '';
const domain = host.includes('localhost') ? undefined : `.${host.split(':')[0]}`;
// ── Redirect to / with cookie ─────────────────────────────────────
const response = NextResponse.redirect(new URL('/', request.url));
response.cookies.set(SESSION_COOKIE_NAME, token, {
httpOnly: true,
secure: isSecure,
sameSite: 'lax',
path: '/',
maxAge: 7 * 24 * 60 * 60,
...(domain ? { domain } : {}),
});
logActivity(user.id, 'login', 'Logged in via Google', 'Signed in with Google OAuth');
return response;
} catch (error) {
console.error('[GOOGLE_CALLBACK_ERROR]', error);
return NextResponse.redirect(new URL('/?error=internal', request.url));
}
}
export const GET = withDb(handler);