TikTok V5 + Security
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { getAuthCookieOptions } from '@/lib/cookieConfig';
|
||||
import { signUserId } from '@/lib/session';
|
||||
import {
|
||||
appendRedirectParam,
|
||||
GOOGLE_OAUTH_STATE_COOKIE_NAME,
|
||||
@@ -27,14 +28,14 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
// If no code, redirect to Google OAuth
|
||||
if (!code) {
|
||||
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
||||
|
||||
if (!googleClientId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Google Client ID not configured' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
||||
|
||||
if (!googleClientId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Google Client ID not configured' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/google`;
|
||||
const scope = 'openid email profile';
|
||||
@@ -85,58 +86,58 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
||||
const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
||||
|
||||
if (!googleClientId || !googleClientSecret) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Google OAuth not configured' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/google`;
|
||||
|
||||
// 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: googleClientId,
|
||||
client_secret: googleClientSecret,
|
||||
|
||||
if (!googleClientId || !googleClientSecret) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Google OAuth not configured' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/google`;
|
||||
|
||||
// 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: googleClientId,
|
||||
client_secret: googleClientSecret,
|
||||
redirect_uri: redirectUri,
|
||||
grant_type: 'authorization_code',
|
||||
}),
|
||||
});
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
throw new Error('Failed to exchange code for tokens');
|
||||
}
|
||||
|
||||
const tokens = await tokenResponse.json();
|
||||
|
||||
// Get user info from Google
|
||||
const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.access_token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!userInfoResponse.ok) {
|
||||
throw new Error('Failed to get user info');
|
||||
}
|
||||
|
||||
const userInfo = await userInfoResponse.json();
|
||||
|
||||
// Check if user exists in database
|
||||
let user = await db.user.findUnique({
|
||||
where: { email: userInfo.email },
|
||||
});
|
||||
|
||||
const isNewUser = !user;
|
||||
|
||||
// Create user if they don't exist
|
||||
});
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
throw new Error('Failed to exchange code for tokens');
|
||||
}
|
||||
|
||||
const tokens = await tokenResponse.json();
|
||||
|
||||
// Get user info from Google
|
||||
const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.access_token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!userInfoResponse.ok) {
|
||||
throw new Error('Failed to get user info');
|
||||
}
|
||||
|
||||
const userInfo = await userInfoResponse.json();
|
||||
|
||||
// Check if user exists in database
|
||||
let user = await db.user.findUnique({
|
||||
where: { email: userInfo.email },
|
||||
});
|
||||
|
||||
const isNewUser = !user;
|
||||
|
||||
// Create user if they don't exist
|
||||
if (!user) {
|
||||
const onboardingStartedAt = new Date();
|
||||
user = await db.user.create({
|
||||
@@ -158,58 +159,58 @@ export async function GET(request: NextRequest) {
|
||||
signupFirstSeenAt: firstTouch?.signupFirstSeenAt ? new Date(firstTouch.signupFirstSeenAt) : onboardingStartedAt,
|
||||
},
|
||||
});
|
||||
|
||||
// Create Account entry for the OAuth provider
|
||||
await db.account.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
type: 'oauth',
|
||||
provider: 'google',
|
||||
providerAccountId: userInfo.sub || userInfo.id,
|
||||
access_token: tokens.access_token,
|
||||
refresh_token: tokens.refresh_token,
|
||||
expires_at: tokens.expires_in ? Math.floor(Date.now() / 1000) + tokens.expires_in : null,
|
||||
token_type: tokens.token_type,
|
||||
scope: tokens.scope,
|
||||
id_token: tokens.id_token,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Update existing account tokens
|
||||
const existingAccount = await db.account.findUnique({
|
||||
where: {
|
||||
provider_providerAccountId: {
|
||||
provider: 'google',
|
||||
providerAccountId: userInfo.sub || userInfo.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existingAccount) {
|
||||
await db.account.update({
|
||||
where: { id: existingAccount.id },
|
||||
data: {
|
||||
access_token: tokens.access_token,
|
||||
refresh_token: tokens.refresh_token,
|
||||
expires_at: tokens.expires_in ? Math.floor(Date.now() / 1000) + tokens.expires_in : null,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Create Account entry if it doesn't exist
|
||||
await db.account.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
type: 'oauth',
|
||||
provider: 'google',
|
||||
providerAccountId: userInfo.sub || userInfo.id,
|
||||
access_token: tokens.access_token,
|
||||
refresh_token: tokens.refresh_token,
|
||||
expires_at: tokens.expires_in ? Math.floor(Date.now() / 1000) + tokens.expires_in : null,
|
||||
token_type: tokens.token_type,
|
||||
scope: tokens.scope,
|
||||
id_token: tokens.id_token,
|
||||
},
|
||||
});
|
||||
|
||||
// Create Account entry for the OAuth provider
|
||||
await db.account.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
type: 'oauth',
|
||||
provider: 'google',
|
||||
providerAccountId: userInfo.sub || userInfo.id,
|
||||
access_token: tokens.access_token,
|
||||
refresh_token: tokens.refresh_token,
|
||||
expires_at: tokens.expires_in ? Math.floor(Date.now() / 1000) + tokens.expires_in : null,
|
||||
token_type: tokens.token_type,
|
||||
scope: tokens.scope,
|
||||
id_token: tokens.id_token,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Update existing account tokens
|
||||
const existingAccount = await db.account.findUnique({
|
||||
where: {
|
||||
provider_providerAccountId: {
|
||||
provider: 'google',
|
||||
providerAccountId: userInfo.sub || userInfo.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existingAccount) {
|
||||
await db.account.update({
|
||||
where: { id: existingAccount.id },
|
||||
data: {
|
||||
access_token: tokens.access_token,
|
||||
refresh_token: tokens.refresh_token,
|
||||
expires_at: tokens.expires_in ? Math.floor(Date.now() / 1000) + tokens.expires_in : null,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Create Account entry if it doesn't exist
|
||||
await db.account.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
type: 'oauth',
|
||||
provider: 'google',
|
||||
providerAccountId: userInfo.sub || userInfo.id,
|
||||
access_token: tokens.access_token,
|
||||
refresh_token: tokens.refresh_token,
|
||||
expires_at: tokens.expires_in ? Math.floor(Date.now() / 1000) + tokens.expires_in : null,
|
||||
token_type: tokens.token_type,
|
||||
scope: tokens.scope,
|
||||
id_token: tokens.id_token,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +228,7 @@ export async function GET(request: NextRequest) {
|
||||
const redirectUrl = new URL(`${process.env.NEXT_PUBLIC_APP_URL}${onboardingTarget}`);
|
||||
|
||||
const response = NextResponse.redirect(redirectUrl.toString());
|
||||
response.cookies.set('userId', user.id, getAuthCookieOptions());
|
||||
response.cookies.set('userId', signUserId(user.id), getAuthCookieOptions());
|
||||
response.cookies.delete(GOOGLE_OAUTH_STATE_COOKIE_NAME);
|
||||
response.cookies.delete(POST_AUTH_REDIRECT_COOKIE_NAME);
|
||||
response.cookies.delete(ATTRIBUTION_COOKIE_NAME);
|
||||
|
||||
Reference in New Issue
Block a user