@@ -9,6 +9,7 @@ import dbConnect from "@/lib/db";
99import User , { IUser } from "@/models/User" ;
1010import { verifyRecaptcha } from "@/lib/recaptcha" ;
1111import { checkRateLimit , getClientIp } from "@/lib/rate-limit" ;
12+ import { getAuth as getAdminAuth } from "@/lib/firebase-admin" ;
1213
1314// Configure route
1415export const dynamic = "force-dynamic" ;
@@ -75,7 +76,10 @@ export async function POST(request: Request) {
7576 {
7677 success : false ,
7778 message : "Too many requests. Please try again later." ,
78- error : { code : "rate_limit_exceeded" , message : "Rate limit exceeded" } ,
79+ error : {
80+ code : "rate_limit_exceeded" ,
81+ message : "Rate limit exceeded" ,
82+ } ,
7983 } ,
8084 { status : 429 } ,
8185 ) ;
@@ -105,17 +109,78 @@ export async function POST(request: Request) {
105109 const recaptchaToken = formData . get ( "recaptcha_token" ) as string | null ;
106110 const captcha = await verifyRecaptcha ( recaptchaToken , "register" ) ;
107111 if ( ! captcha . ok ) {
108- console . warn ( "[register] reCAPTCHA rejected:" , captcha . reason , captcha . score ) ;
112+ console . warn (
113+ "[register] reCAPTCHA rejected:" ,
114+ captcha . reason ,
115+ captcha . score ,
116+ ) ;
109117 return NextResponse . json (
110118 {
111119 success : false ,
112120 message : "Security check failed. Please try again." ,
113- error : { code : "recaptcha_failed" , message : "reCAPTCHA verification failed" } ,
121+ error : {
122+ code : "recaptcha_failed" ,
123+ message : "reCAPTCHA verification failed" ,
124+ } ,
114125 } ,
115126 { status : 400 } ,
116127 ) ;
117128 }
118129
130+ const isGoogle = formData . get ( "auth_provider" ) === "google" ;
131+ let googleUid : string | undefined ;
132+ let googleEmail : string | undefined ;
133+ if ( isGoogle ) {
134+ const idToken = formData . get ( "id_token" ) as string | null ;
135+ if ( ! idToken ) {
136+ return NextResponse . json (
137+ {
138+ success : false ,
139+ message : "Missing Google sign-in token" ,
140+ error : {
141+ code : "missing_id_token" ,
142+ message : "Missing Google sign-in token" ,
143+ } ,
144+ } ,
145+ { status : 400 } ,
146+ ) ;
147+ }
148+ try {
149+ const decoded = await getAdminAuth ( ) . verifyIdToken ( idToken ) ;
150+ googleUid = decoded . uid ;
151+ googleEmail = decoded . email ?. toLowerCase ( ) ;
152+ } catch ( tokenError ) {
153+ console . error (
154+ "[register] Google ID token verification failed:" ,
155+ tokenError ,
156+ ) ;
157+ return NextResponse . json (
158+ {
159+ success : false ,
160+ message : "Invalid Google sign-in token. Please sign in again." ,
161+ error : {
162+ code : "invalid_id_token" ,
163+ message : "Invalid Google sign-in token" ,
164+ } ,
165+ } ,
166+ { status : 401 } ,
167+ ) ;
168+ }
169+ if ( ! googleEmail ) {
170+ return NextResponse . json (
171+ {
172+ success : false ,
173+ message : "Your Google account has no email address." ,
174+ error : {
175+ code : "google_no_email" ,
176+ message : "Google account has no email address" ,
177+ } ,
178+ } ,
179+ { status : 400 } ,
180+ ) ;
181+ }
182+ }
183+
119184 // Helper to convert File to base64
120185 const fileToBase64 = async ( file : File ) : Promise < string > => {
121186 const arrayBuffer = await file . arrayBuffer ( ) ;
@@ -125,7 +190,10 @@ export async function POST(request: Request) {
125190
126191 // Extract fields
127192 const name = formData . get ( "name" ) as string ;
128- const email = formData . get ( "email" ) as string ;
193+ // For Google, the email comes from the verified ID token, never the client.
194+ const email = isGoogle
195+ ? ( googleEmail as string )
196+ : ( formData . get ( "email" ) as string ) ;
129197 const password = formData . get ( "password" ) as string ;
130198 const discord_username = formData . get ( "discord_username" ) as string ;
131199 const phone = formData . get ( "phone" ) as string ;
@@ -166,7 +234,8 @@ export async function POST(request: Request) {
166234 errors . email = "Invalid email format" ;
167235 }
168236
169- if ( ! password || ! validatePassword ( password ) ) {
237+ // Google users have no password — Firebase handles their credential.
238+ if ( ! isGoogle && ( ! password || ! validatePassword ( password ) ) ) {
170239 errors . password =
171240 "Password must be at least 8 characters and contain uppercase, lowercase, number, and special character" ;
172241 }
@@ -268,40 +337,62 @@ export async function POST(request: Request) {
268337 ) ;
269338 }
270339
271- // Create Firebase Auth user
272- let firebaseUser ;
273- try {
274- const userCredential = await createUserWithEmailAndPassword (
275- auth ,
276- email ,
277- password ,
278- ) ;
279- firebaseUser = userCredential . user ;
280-
281- // Send email verification
282- await sendEmailVerification ( firebaseUser ) ;
283- } catch ( firebaseError : any ) {
284- if ( firebaseError . code === "auth/email-already-in-use" ) {
340+ if ( isGoogle ) {
341+ const existingUid = await User . findOne ( { uid : googleUid } ) ;
342+ if ( existingUid ) {
285343 return NextResponse . json (
286344 {
287345 success : false ,
288- message : "Email already exists" ,
289- error : { code : "email_exists" , message : "Email already exists" } ,
346+ message :
347+ "This Google account is already registered. Please log in." ,
348+ error : {
349+ code : "already_registered" ,
350+ message : "Account already exists" ,
351+ } ,
290352 } ,
291353 { status : 409 } ,
292354 ) ;
293355 }
294- if ( firebaseError . code === "auth/weak-password" ) {
295- return NextResponse . json (
296- {
297- success : false ,
298- message : "Password is too weak" ,
299- error : { code : "weak_password" , message : "Password is too weak" } ,
300- } ,
301- { status : 400 } ,
356+ }
357+
358+ // Resolve the Firebase uid. Email/password users are created here (and sent
359+ // a verification email); Google users already exist in Firebase with a
360+ // verified email, so we reuse the uid from their ID token and skip both.
361+ let uid : string ;
362+ if ( isGoogle ) {
363+ uid = googleUid ! ;
364+ } else {
365+ try {
366+ const userCredential = await createUserWithEmailAndPassword (
367+ auth ,
368+ email ,
369+ password ,
302370 ) ;
371+ await sendEmailVerification ( userCredential . user ) ;
372+ uid = userCredential . user . uid ;
373+ } catch ( firebaseError : any ) {
374+ if ( firebaseError . code === "auth/email-already-in-use" ) {
375+ return NextResponse . json (
376+ {
377+ success : false ,
378+ message : "Email already exists" ,
379+ error : { code : "email_exists" , message : "Email already exists" } ,
380+ } ,
381+ { status : 409 } ,
382+ ) ;
383+ }
384+ if ( firebaseError . code === "auth/weak-password" ) {
385+ return NextResponse . json (
386+ {
387+ success : false ,
388+ message : "Password is too weak" ,
389+ error : { code : "weak_password" , message : "Password is too weak" } ,
390+ } ,
391+ { status : 400 } ,
392+ ) ;
393+ }
394+ throw firebaseError ;
303395 }
304- throw firebaseError ;
305396 }
306397
307398 // Upload files to Cloudinary if provided
@@ -336,7 +427,7 @@ export async function POST(request: Request) {
336427
337428 // Create user in MongoDB
338429 const newUser = new User ( {
339- uid : firebaseUser . uid ,
430+ uid,
340431 name : name . trim ( ) ,
341432 email : email . toLowerCase ( ) . trim ( ) ,
342433 phone : phone . trim ( ) ,
@@ -353,17 +444,19 @@ export async function POST(request: Request) {
353444 isLooking : Boolean ( isLooking ) ,
354445 role : "user" ,
355446 teamCode : undefined ,
447+ authProvider : isGoogle ? "google" : "password" ,
356448 } ) ;
357449
358450 await newUser . save ( ) ;
359451
360452 return NextResponse . json (
361453 {
362454 message : "Registration successful" ,
363- uid : firebaseUser . uid ,
364- status : "pending_verification" ,
455+ uid,
456+ // Google emails arrive verified, so there's nothing pending for them.
457+ status : isGoogle ? "active" : "pending_verification" ,
365458 user : {
366- uid : firebaseUser . uid ,
459+ uid,
367460 email : newUser . email ,
368461 name : newUser . name ,
369462 isAdmin : false ,
0 commit comments