From 60396dce2ff91a7137b68d9e4ea50181a47a0da0 Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Mon, 20 Oct 2025 11:51:38 -0400 Subject: [PATCH 1/8] debugging registration errors with new login fixes --- services/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/auth.service.ts b/services/auth.service.ts index 03b08f3f9..0230c6489 100644 --- a/services/auth.service.ts +++ b/services/auth.service.ts @@ -40,7 +40,7 @@ export class AuthService { try { return await ApiClient.post(`${this.BASE_PATH}/auth/register/`, credentials); } catch (error: any) { - const { status } = error.message; + const status = error instanceof ApiError ? error.status : undefined; const data = error instanceof ApiError ? error.errors : {}; const errorMsg = Object.values(data as Record)?.[0]?.[0]; throw new AuthError(errorMsg || 'Registration failed', status); From e83877fdf23a5ceea8100933ac7bee704166a49d Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Mon, 20 Oct 2025 12:08:56 -0400 Subject: [PATCH 2/8] readding failed code for testing login_fixes --- app/api/auth/[...nextauth]/auth.config.ts | 33 ++++++++++++++------ app/auth/error/page.tsx | 18 ++++++++--- app/auth/signin/page.tsx | 36 +++++++++++++--------- components/Auth/AuthContent.tsx | 3 ++ components/Auth/screens/Login.tsx | 33 +++++++++----------- components/Auth/screens/SelectProvider.tsx | 32 +++++++++---------- services/auth.service.ts | 12 ++------ 7 files changed, 96 insertions(+), 71 deletions(-) diff --git a/app/api/auth/[...nextauth]/auth.config.ts b/app/api/auth/[...nextauth]/auth.config.ts index 06b80ef1a..dca4a1300 100644 --- a/app/api/auth/[...nextauth]/auth.config.ts +++ b/app/api/auth/[...nextauth]/auth.config.ts @@ -77,7 +77,7 @@ export const authOptions: NextAuthOptions = { error: '/auth/error', }, callbacks: { - async signIn({ user, account, profile }) { + async signIn({ user, account, profile, email, credentials }) { if (account?.type === 'oauth') { try { // Log OAuth token state for debugging @@ -141,25 +141,40 @@ export const authOptions: NextAuthOptions = { return true; } catch (error) { - // Log detailed error information + const errorType = error instanceof Error ? error.message : 'AuthenticationFailed'; console.error('[Auth] Google OAuth error', { - error: error instanceof Error ? error.message : 'Unknown error', + error: errorType, errorType: error instanceof Error ? error.constructor.name : typeof error, stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }); - - // Preserve specific error messages for better debugging - if (error instanceof Error) { - throw new Error(error.message); - } - throw new Error('AuthenticationFailed'); + throw new Error(errorType); } } return true; }, + async redirect({ url, baseUrl }) { + if (url.includes('/auth/error')) { + const urlObj = new URL(url, baseUrl); + const error = urlObj.searchParams.get('error'); + + if ( + error && + (error === 'OAuthSignin' || error === 'OAuthCallback' || error === 'AccessDenied') + ) { + urlObj.searchParams.set('error', 'OAuthAccountNotLinked'); + } + + return urlObj.toString(); + } + + if (url.startsWith('/')) return `${baseUrl}${url}`; + if (new URL(url).origin === baseUrl) return url; + return baseUrl; + }, + async jwt({ token, user, account }) { if (account && user) { return { diff --git a/app/auth/error/page.tsx b/app/auth/error/page.tsx index 8f6169abd..8601696e8 100644 --- a/app/auth/error/page.tsx +++ b/app/auth/error/page.tsx @@ -1,13 +1,23 @@ 'use client'; -import { useSearchParams } from 'next/navigation'; -import { Suspense } from 'react'; +import { useEffect, Suspense } from 'react'; +import { useSearchParams, useRouter } from 'next/navigation'; function ErrorContent() { const searchParams = useSearchParams(); - const error = searchParams?.get('error'); + const router = useRouter(); - return null; // This page won't render anything, it just redirects + useEffect(() => { + const error = searchParams?.get('error'); + const callbackUrl = searchParams?.get('callbackUrl') || '/'; + + const params = new URLSearchParams({ callbackUrl }); + if (error) params.set('error', error); + + router.replace(`/auth/signin?${params}`); + }, [searchParams, router]); + + return null; } export default function ErrorPage() { diff --git a/app/auth/signin/page.tsx b/app/auth/signin/page.tsx index db6662475..f26356ba2 100644 --- a/app/auth/signin/page.tsx +++ b/app/auth/signin/page.tsx @@ -2,15 +2,31 @@ import { useSearchParams } from 'next/navigation'; import AuthContent from '@/components/Auth/AuthContent'; -import { useRouter } from 'next/navigation'; import Image from 'next/image'; import { Suspense } from 'react'; function SignInContent() { - const router = useRouter(); const searchParams = useSearchParams(); - const error = searchParams?.get('error'); - const callbackUrl = searchParams?.get('callbackUrl') || '/'; + const errorCode = searchParams?.get('error'); + let callbackUrl = searchParams?.get('callbackUrl') || '/'; + //this is to help prevent redirecting issues when using the auth modal after an error occurs + if (callbackUrl.startsWith('http')) { + try { + const url = new URL(callbackUrl); + callbackUrl = url.pathname + url.search + url.hash; + } catch {} + } + + if (callbackUrl.includes('/auth/')) { + callbackUrl = '/'; + } + + let error = null; + if (errorCode === 'OAuthAccountNotLinked') { + error = 'Enter email and password to login to your account.'; + } else if (errorCode) { + error = 'An error occurred during authentication. Please try again.'; + } return (
@@ -35,17 +51,7 @@ function SignInContent() {
- {error && ( -
-

{error}

-
- )} - - router.push(callbackUrl)} - showHeader={false} - /> +
diff --git a/components/Auth/AuthContent.tsx b/components/Auth/AuthContent.tsx index da79bc052..a8c9d9891 100644 --- a/components/Auth/AuthContent.tsx +++ b/components/Auth/AuthContent.tsx @@ -14,6 +14,7 @@ interface AuthContentProps { initialScreen?: AuthScreen; showHeader?: boolean; modalView?: boolean; + callbackUrl?: string; } export default function AuthContent({ @@ -23,6 +24,7 @@ export default function AuthContent({ initialScreen = 'SELECT_PROVIDER', showHeader = true, modalView = false, + callbackUrl, }: AuthContentProps) { const [screen, setScreen] = useState(initialScreen); const [email, setEmail] = useState(''); @@ -48,6 +50,7 @@ export default function AuthContent({ setError, showHeader, modalView, + callbackUrl, }; return ( diff --git a/components/Auth/screens/Login.tsx b/components/Auth/screens/Login.tsx index aa3695b53..a71b8b7e7 100644 --- a/components/Auth/screens/Login.tsx +++ b/components/Auth/screens/Login.tsx @@ -14,6 +14,7 @@ interface Props extends BaseScreenProps { setIsLoading: (loading: boolean) => void; onSuccess?: () => void; modalView?: boolean; + callbackUrl?: string; } export default function Login({ @@ -27,6 +28,7 @@ export default function Login({ onBack, onForgotPassword, modalView = false, + callbackUrl, }: Props) { const [password, setPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); @@ -45,28 +47,23 @@ export default function Login({ setError(null); try { - // This endpoint will return a CredentialsSignin error with no description. - // Currently we try to login with email and password + fetch the user's data separately, - // because the current endpoint only returns a token - // So, we show "Invalid email or password" error - const result = await signIn('credentials', { - email, - password, - redirect: false, - }); - - if (result?.error) { - setError('Invalid email or password'); + if (callbackUrl) { + await signIn('credentials', { email, password, callbackUrl }); } else { - setIsRedirecting(true); // Set redirecting state before navigation - onSuccess?.(); - onClose(); + const result = await signIn('credentials', { email, password, redirect: false }); + + if (result?.error) { + setError('Invalid email or password'); + } else { + setIsRedirecting(true); + onSuccess?.(); + onClose(); + } } - } catch (err) { + } catch { setError('Login failed'); } finally { - if (!isRedirecting) { - // Only reset loading if we're not redirecting + if (!isRedirecting && !callbackUrl) { setIsLoading(false); } } diff --git a/components/Auth/screens/SelectProvider.tsx b/components/Auth/screens/SelectProvider.tsx index a61310a5e..04d3fbd60 100644 --- a/components/Auth/screens/SelectProvider.tsx +++ b/components/Auth/screens/SelectProvider.tsx @@ -33,6 +33,20 @@ export default function SelectProvider({ const emailInputRef = useAutoFocus(true); const { referralCode } = useReferral(); + const getCallbackUrl = () => { + const searchParams = new URLSearchParams(globalThis.location.search); + return searchParams.get('callbackUrl') || '/'; + }; + + const initiateGoogleSignIn = (callbackUrl: string) => { + const finalUrl = referralCode + ? new URL('/referral/join/apply-referral-code', globalThis.location.origin).toString() + + `?refr=${referralCode}&redirect=${encodeURIComponent(callbackUrl)}` + : callbackUrl; + + signIn('google', { callbackUrl: finalUrl }); + }; + const handleCheckAccount = async (e?: React.FormEvent) => { e?.preventDefault(); if (!isValidEmail(email)) { @@ -48,7 +62,7 @@ export default function SelectProvider({ if (response.exists) { if (response.auth === 'google') { - signIn('google', { callbackUrl: '/' }); + initiateGoogleSignIn(getCallbackUrl()); } else if (response.is_verified) { onContinue(); } else { @@ -68,21 +82,7 @@ export default function SelectProvider({ AnalyticsService.logEvent(LogEvent.AUTH_VIA_GOOGLE_INITIATED).catch((error) => { console.error('Analytics failed:', error); }); - - const searchParams = new URLSearchParams(window.location.search); - const originalCallbackUrl = searchParams.get('callbackUrl') || '/'; - - let finalCallbackUrl = originalCallbackUrl; - - if (referralCode) { - // Create referral application URL with referral code and redirect as URL parameters - const referralUrl = new URL('/referral/join/apply-referral-code', window.location.origin); - referralUrl.searchParams.set('refr', referralCode); - referralUrl.searchParams.set('redirect', originalCallbackUrl); - finalCallbackUrl = referralUrl.toString(); - } - - signIn('google', { callbackUrl: finalCallbackUrl }); + initiateGoogleSignIn(getCallbackUrl()); }; return ( diff --git a/services/auth.service.ts b/services/auth.service.ts index 0230c6489..c72537564 100644 --- a/services/auth.service.ts +++ b/services/auth.service.ts @@ -171,16 +171,10 @@ export class AuthService { timestamp: new Date().toISOString(), }); - switch (response.status) { - case 401: - throw new Error('AuthenticationFailed'); - case 403: - throw new Error('AccessDenied'); - case 409: - throw new Error('Verification'); - default: - throw new Error('AuthenticationFailed'); + if (response.status === 400 || response.status === 403 || response.status === 409) { + throw new Error('OAuthAccountNotLinked'); } + throw new Error('AuthenticationFailed'); } const data = await response.json(); From f07d92d8213e311d5c2739b3cb17aa0d3589c503 Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Mon, 20 Oct 2025 12:51:35 -0400 Subject: [PATCH 3/8] readding in console to test registration failures on staging temporarily --- next.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/next.config.js b/next.config.js index f3b522a02..eda765ff8 100644 --- a/next.config.js +++ b/next.config.js @@ -92,9 +92,9 @@ const nextConfig = { }, }, compress: true, - // compiler: { - // removeConsole: process.env.VERCEL_ENV === 'production', - // }, + compiler: { + removeConsole: process.env.VERCEL_ENV === 'production', + }, turbopack: { rules: { '*.svg': { From 329b516b60d9c661ad293c07b9d81ff41955575e Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Mon, 20 Oct 2025 13:44:53 -0400 Subject: [PATCH 4/8] removing the console logging --- next.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/next.config.js b/next.config.js index eda765ff8..f3b522a02 100644 --- a/next.config.js +++ b/next.config.js @@ -92,9 +92,9 @@ const nextConfig = { }, }, compress: true, - compiler: { - removeConsole: process.env.VERCEL_ENV === 'production', - }, + // compiler: { + // removeConsole: process.env.VERCEL_ENV === 'production', + // }, turbopack: { rules: { '*.svg': { From d24e26dae932c45357e579eb2ddcb5a43c28392f Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Thu, 23 Oct 2025 10:47:39 -0400 Subject: [PATCH 5/8] [Login Errors] Resolving Registration Error --- services/auth.service.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/services/auth.service.ts b/services/auth.service.ts index c72537564..c054ca4e4 100644 --- a/services/auth.service.ts +++ b/services/auth.service.ts @@ -40,10 +40,16 @@ export class AuthService { try { return await ApiClient.post(`${this.BASE_PATH}/auth/register/`, credentials); } catch (error: any) { - const status = error instanceof ApiError ? error.status : undefined; - const data = error instanceof ApiError ? error.errors : {}; - const errorMsg = Object.values(data as Record)?.[0]?.[0]; - throw new AuthError(errorMsg || 'Registration failed', status); + if (error instanceof ApiError) { + const data = error.errors || {}; + const errorMsg = Object.values(data as Record)?.[0]?.[0]; + throw new AuthError(errorMsg || 'Registration failed', error.status); + } + + // Handle non-ApiError exceptions (network errors, etc.) + const errorMsg = error instanceof Error ? error.message : 'Registration failed'; + console.error('[AuthService] Registration error:', error); + throw new AuthError(errorMsg, undefined); } } From 801690b66afe5c7793d8996e343034531549459d Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Thu, 23 Oct 2025 14:07:15 -0400 Subject: [PATCH 6/8] [Login Fixes] Minor Optimization for Standardization --- app/api/auth/[...nextauth]/auth.config.ts | 12 +++++++++++- services/auth.service.ts | 7 ++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/api/auth/[...nextauth]/auth.config.ts b/app/api/auth/[...nextauth]/auth.config.ts index dca4a1300..c95a76d31 100644 --- a/app/api/auth/[...nextauth]/auth.config.ts +++ b/app/api/auth/[...nextauth]/auth.config.ts @@ -4,6 +4,16 @@ import CredentialsProvider from 'next-auth/providers/credentials'; import { AuthService } from '@/services/auth.service'; import { AuthSharingService } from '@/services/auth-sharing.service'; +export class NextAuthError extends Error { + constructor( + message: string, + public readonly code: string + ) { + super(message); + this.name = 'NextAuthError'; + } +} + // Debug flag - set to true to enable detailed authentication logging const DEBUG_AUTH = true; @@ -148,7 +158,7 @@ export const authOptions: NextAuthOptions = { stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }); - throw new Error(errorType); + throw new NextAuthError(errorType, 'OAUTH_ERROR'); } } diff --git a/services/auth.service.ts b/services/auth.service.ts index c054ca4e4..e3923f822 100644 --- a/services/auth.service.ts +++ b/services/auth.service.ts @@ -42,7 +42,12 @@ export class AuthService { } catch (error: any) { if (error instanceof ApiError) { const data = error.errors || {}; - const errorMsg = Object.values(data as Record)?.[0]?.[0]; + const errorValues = Object.values(data as Record)?.[0]; + const errorMsg = Array.isArray(errorValues) + ? errorValues[0] + : typeof errorValues === 'string' + ? errorValues + : 'Registration failed'; throw new AuthError(errorMsg || 'Registration failed', error.status); } From 92958cd8074219fc0288d48ba6f30de3d246beb8 Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Thu, 23 Oct 2025 16:06:02 -0400 Subject: [PATCH 7/8] [Login Fixes] Attempting fix at verbage issue on redirect --- app/api/auth/[...nextauth]/auth.config.ts | 15 ++++++++++++++- app/auth/signin/page.tsx | 10 +++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/api/auth/[...nextauth]/auth.config.ts b/app/api/auth/[...nextauth]/auth.config.ts index c95a76d31..f4dc82a4e 100644 --- a/app/api/auth/[...nextauth]/auth.config.ts +++ b/app/api/auth/[...nextauth]/auth.config.ts @@ -158,6 +158,13 @@ export const authOptions: NextAuthOptions = { stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }); + + // Return false for OAuthAccountNotLinked to trigger consistent error flow + // This ensures the error is properly handled by the redirect callback + if (errorType === 'OAuthAccountNotLinked') { + return false; + } + throw new NextAuthError(errorType, 'OAUTH_ERROR'); } } @@ -170,9 +177,15 @@ export const authOptions: NextAuthOptions = { const urlObj = new URL(url, baseUrl); const error = urlObj.searchParams.get('error'); + // Map various OAuth error codes to OAuthAccountNotLinked + // This handles environment-specific error code variations if ( error && - (error === 'OAuthSignin' || error === 'OAuthCallback' || error === 'AccessDenied') + (error === 'OAuthSignin' || + error === 'OAuthCallback' || + error === 'AccessDenied' || + error === 'Callback' || + error === 'OAuthCreateAccount') ) { urlObj.searchParams.set('error', 'OAuthAccountNotLinked'); } diff --git a/app/auth/signin/page.tsx b/app/auth/signin/page.tsx index f26356ba2..2742a7f14 100644 --- a/app/auth/signin/page.tsx +++ b/app/auth/signin/page.tsx @@ -22,7 +22,15 @@ function SignInContent() { } let error = null; - if (errorCode === 'OAuthAccountNotLinked') { + // Map various OAuth error codes to the account linking message + if ( + errorCode === 'OAuthAccountNotLinked' || + errorCode === 'OAuthSignin' || + errorCode === 'OAuthCallback' || + errorCode === 'AccessDenied' || + errorCode === 'Callback' || + errorCode === 'OAuthCreateAccount' + ) { error = 'Enter email and password to login to your account.'; } else if (errorCode) { error = 'An error occurred during authentication. Please try again.'; From e6ea8bdd7a5e3b3113c5ce63e3e8a207be53ade5 Mon Sep 17 00:00:00 2001 From: Michael Canova Date: Thu, 23 Oct 2025 16:11:28 -0400 Subject: [PATCH 8/8] [Login Fixes] Updating Error Code list for proper verbage --- app/api/auth/[...nextauth]/auth.config.ts | 3 ++- app/auth/signin/page.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/api/auth/[...nextauth]/auth.config.ts b/app/api/auth/[...nextauth]/auth.config.ts index f4dc82a4e..eceacb260 100644 --- a/app/api/auth/[...nextauth]/auth.config.ts +++ b/app/api/auth/[...nextauth]/auth.config.ts @@ -185,7 +185,8 @@ export const authOptions: NextAuthOptions = { error === 'OAuthCallback' || error === 'AccessDenied' || error === 'Callback' || - error === 'OAuthCreateAccount') + error === 'OAuthCreateAccount' || + error === 'AuthenticationFailed') ) { urlObj.searchParams.set('error', 'OAuthAccountNotLinked'); } diff --git a/app/auth/signin/page.tsx b/app/auth/signin/page.tsx index 2742a7f14..fd175a85c 100644 --- a/app/auth/signin/page.tsx +++ b/app/auth/signin/page.tsx @@ -29,7 +29,8 @@ function SignInContent() { errorCode === 'OAuthCallback' || errorCode === 'AccessDenied' || errorCode === 'Callback' || - errorCode === 'OAuthCreateAccount' + errorCode === 'OAuthCreateAccount' || + errorCode === 'AuthenticationFailed' ) { error = 'Enter email and password to login to your account.'; } else if (errorCode) {