import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import { UserInfoQuery } from '@/utils/query';
import * as db from '@/utils/db';
export const dynamic = 'force-dynamic';
const handler = NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
authorization: {
params: {
prompt: "select_account",
access_type: "offline",
response_type: "code"
}
}
}),
],
debug: process.env.NODE_ENV === 'production',
useSecureCookies: process.env.NODE_ENV === 'production',
cookies: {
sessionToken: {
name: `next-auth.session-token`,
options: {
httpOnly: true,
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production' ? true : false,
domain: process.env.NODE_ENV === 'production' ? 'assistsales.vercel.app' : undefined
}
},
callbackUrl: {
name: `next-auth.callback-url`,
options: {
httpOnly: true,
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production' ? true : false,
domain: process.env.NODE_ENV === 'production' ? 'assistsales.vercel.app' : undefined
}
},
csrfToken: {
name: `next-auth.csrf-token`,
options: {
httpOnly: true,
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production' ? true : false,
domain: process.env.NODE_ENV === 'production' ? 'assistsales.vercel.app' : undefined
}
},
pkceCodeVerifier: {
name: 'next-auth.pkce.code_verifier',
options: {
httpOnly: true,
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
path: '/',
secure: process.env.NODE_ENV === 'production' ? true : false,
domain: process.env.NODE_ENV === 'production' ? 'assistsales.vercel.app' : undefined
}
}
},
callbacks: {
async signIn({ user, account, profile }) {
if (account?.provider === 'google' && profile) {
try {
if (!user.email) {
return false;
}
const now = new Date();
try {
const dbCheck = await db.query('SELECT NOW() as time');
if (!dbCheck.rows?.length) {
console.error('Database connection check failed');
return false;
}
} catch (dbError) {
console.error('Database connection error:', dbError);
return false;
}
let existingUser;
try {
existingUser = await UserInfoQuery.getUserByEmail(user.email as string);
} catch (getUserError) {
console.error('Error getting user:', getUserError);
return false;
}
if (!existingUser) {
try {
const userData = {
user_name: user.name || (profile as any).name || 'Google User',
email: user.email as string,
passwordhash: 'google_oauth_user',
phone: (profile as any).phone_number || undefined,
profile_image: user.image || undefined,
last_login_at: now,
created_at: now,
modified_at: now,
};
const insertQuery = `
INSERT INTO user_info (
id, user_name, email, passwordhash, phone, profile_image,
last_login_at, created_at, modified_at
) VALUES (
gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8
)
RETURNING *
`;
const insertParams = [
userData.user_name,
userData.email,
userData.passwordhash,
userData.phone,
userData.profile_image,
userData.last_login_at,
userData.created_at,
userData.modified_at
];
const result = await db.query(insertQuery, insertParams);
if (result.rows.length === 0) {
console.error('User creation failed');
return false;
}
} catch (createError) {
console.error('Error creating user:', createError);
try {
const fallbackUserData = {
user_name: user.name || (profile as any).name || 'Google User',
email: user.email as string,
passwordhash: 'google_oauth_user',
phone: (profile as any).phone_number || undefined,
profile_image: user.image || undefined,
last_login_at: now,
created_at: now,
modified_at: now,
};
const createdUser = await UserInfoQuery.createUser(fallbackUserData);
if (!createdUser) {
console.error('Fallback user creation failed');
return false;
}
} catch (fallbackError) {
console.error('Fallback creation error:', fallbackError);
return false;
}
}
} else {
try {
const updateData = {
user_name: user.name || existingUser.user_name,
profile_image: user.image || existingUser.profile_image,
};
await UserInfoQuery.updateUserInfo(existingUser.id, updateData);
const loginQuery = 'UPDATE user_info SET last_login_at = $1 WHERE id = $2 RETURNING *';
const loginResult = await db.query(loginQuery, [now, existingUser.id]);
if (loginResult.rows.length === 0) {
console.error('Login time update failed');
// 업데이트 실패해도 로그인은 성공으로 처리
}
} catch (updateError) {
console.error('Error updating user:', updateError);
// 업데이트 실패해도 로그인은 성공으로 처리
}
}
return true;
} catch (error) {
console.error('Sign in process error:', error);
return false;
}
}
return true;
},
async session({ session, token }) {
if (session.user?.email) {
try {
const dbUser = await UserInfoQuery.getUserByEmail(session.user.email);
if (!dbUser) {
console.error('User not found in database');
throw new Error('User not found');
}
session.user.id = String(dbUser.id);
if (!session.user.name && dbUser.user_name) {
session.user.name = dbUser.user_name;
}
if (!session.user.image && dbUser.profile_image) {
session.user.image = dbUser.profile_image;
}
return session;
} catch (error) {
console.error('Session callback error:', error);
// 세션 데이터 업데이트 실패 시에도 기본 세션 정보는 유지
return session;
}
}
return session;
}
},
pages: {
signIn: '/login',
error: '/login',
signOut: '/',
},
events: {
signOut: async () => {
console.log('로그아웃 이벤트가 발생했습니다.');
},
},
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60, // 30일
},
secret: process.env.NEXTAUTH_SECRET,
});
export { handler as GET, handler as POST };
I’m a person who can do development like vibecoding.
I’m trying to create the program I want with my cursor recently, and I’m having a problem implementing Google login, so I’m asking.
It works fine on http://localhost:3000, but Google login does not work on https://mydomain.vercel.app.
We also double-checked the setting of the vercel environment variable,
I also checked redirecturi on google console.
I think it’s a matter of cookies, so I modified the options.
Symptoms 1.
As soon as the page is loaded, the error “Unexpected token ‘<‘, “<!DOCTYPE”…is not valid JSON” appears.
Symptoms 2.
When I try to log in, I go to the login page, but when I click Google login, I go to the first page and I don’t log in.
- The next-auth.callback-url, next-auth.csrf-token, and next-auth.session-token are not created in the cookie at this time.
- In addition, there is no Google related content in session storage.
Things I’ve done before
- Modify cookie options for route.ts in […nextauth]
- Modifying NEXTAUTH_URL to the changed domain by changing the domain in vercel
- Apply environmental variables after creating GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET for production separately
- Adding middleware.ts for CORS settings
What could be the problem? So far I have no idea about how fix it.
Plz give me an advice.