Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions app/api/auth/clear/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createServerClient } from '@supabase/ssr'
import { NextRequest, NextResponse } from 'next/server'
import { cookies } from 'next/headers'

export async function POST(request: NextRequest) {
try {
const cookieStore = await cookies()

const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options)
})
},
},
}
)

// Sign out from Supabase
await supabase.auth.signOut()

// Manually clear all Supabase-related cookies
const allCookies = cookieStore.getAll()
const supabaseCookies = allCookies.filter(cookie =>
cookie.name.includes('sb-') || cookie.name.includes('supabase')
)

// Create response that clears cookies
const response = NextResponse.redirect(new URL('/auth/login', request.url))

// Clear each Supabase cookie explicitly
supabaseCookies.forEach(cookie => {
response.cookies.set(cookie.name, '', {
expires: new Date(0),
path: '/',
domain: undefined,
httpOnly: false,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax'
})
})

// Also clear common Supabase cookie patterns
const commonCookieNames = [
'sb-access-token',
'sb-refresh-token',
'supabase-auth-token',
'supabase.auth.token'
]

commonCookieNames.forEach(cookieName => {
response.cookies.set(cookieName, '', {
expires: new Date(0),
path: '/',
domain: undefined,
httpOnly: false,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax'
})
})

return response
} catch (error) {
console.error('Error clearing auth:', error)
return NextResponse.json({ error: 'Failed to clear auth' }, { status: 500 })
}
}
92 changes: 92 additions & 0 deletions app/auth/debug/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export default async function AuthDebugPage() {
const cookieStore = await cookies()

const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options)
})
},
},
}
)

const { data: { session }, error } = await supabase.auth.getSession()
const { data: { user } } = await supabase.auth.getUser()

const allCookies = cookieStore.getAll()
const supabaseCookies = allCookies.filter(cookie =>
cookie.name.includes('sb-') || cookie.name.includes('supabase')
)

return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-6">Authentication Debug Information</h1>

<div className="space-y-6">
<div>
<h2 className="text-lg font-semibold mb-2">Server-side Session</h2>
<pre className="bg-gray-100 p-4 rounded overflow-auto">
{JSON.stringify({ session: !!session, user: !!user, error }, null, 2)}
</pre>
</div>

<div>
<h2 className="text-lg font-semibold mb-2">Session Details</h2>
<pre className="bg-gray-100 p-4 rounded overflow-auto">
{JSON.stringify({
sessionExists: !!session,
userExists: !!user,
userId: user?.id,
userEmail: user?.email,
sessionExpiry: session?.expires_at,
accessTokenExists: !!session?.access_token,
refreshTokenExists: !!session?.refresh_token,
errorMessage: error?.message
}, null, 2)}
</pre>
</div>

<div>
<h2 className="text-lg font-semibold mb-2">All Cookies</h2>
<pre className="bg-gray-100 p-4 rounded overflow-auto text-sm">
{JSON.stringify(allCookies.map(c => ({ name: c.name, value: c.value.substring(0, 50) + '...' })), null, 2)}
</pre>
</div>

<div>
<h2 className="text-lg font-semibold mb-2">Supabase Cookies</h2>
<pre className="bg-gray-100 p-4 rounded overflow-auto">
{JSON.stringify(supabaseCookies.map(c => ({
name: c.name,
hasValue: !!c.value,
valueLength: c.value.length
})), null, 2)}
</pre>
</div>

<div>
<h2 className="text-lg font-semibold mb-2">Clear Auth Action</h2>
<form action="/api/auth/clear" method="POST">
<button
type="submit"
className="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
>
Clear All Auth Cookies
</button>
</form>
</div>
</div>
</div>
)
}
45 changes: 34 additions & 11 deletions app/auth/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default function LoginPage() {
signIn,
signInWithGoogle,
signInWithApple,
clearStaleAuthData,
isGoogleAuthEnabled,
isAppleAuthEnabled
} = useAuth()
Expand Down Expand Up @@ -98,9 +99,20 @@ export default function LoginPage() {
}
}

const handleClearAuthData = async () => {
try {
await clearStaleAuthData()
setError('')
// Optionally refresh the page to ensure clean state
window.location.reload()
} catch (error: unknown) {
setError(error instanceof Error ? error.message : 'Failed to clear auth data')
}
}

return (
<div className="min-h-screen flex items-center justify-center bg-background">
<div className="max-w-md w-full space-y-8">
<div className="min-h-screen flex items-center justify-center bg-background px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8 p-6 sm:p-8 border border-border rounded-lg shadow-sm bg-card">
<div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-foreground">
Sign in to LocalLoop
Expand All @@ -113,21 +125,21 @@ export default function LoginPage() {
</p>
</div>

<form className="mt-8 space-y-6" onSubmit={handleEmailLogin}>
<form className="mt-6 space-y-6" onSubmit={handleEmailLogin}>
{error && (
<div className="bg-red-50 dark:bg-red-950 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-300 px-4 py-3 rounded">
<div className="bg-red-50 dark:bg-red-950 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-300 px-4 py-3 rounded-md">
{error}
</div>
)}

<div className="rounded-md shadow-sm -space-y-px">
<div className="space-y-4">
<div>
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="relative block w-full px-3 py-2 border border-border placeholder-muted-foreground text-foreground bg-background rounded-t-md focus:outline-none focus:ring-primary focus:border-primary"
className="block w-full px-4 py-3 border border-border placeholder-muted-foreground text-foreground bg-background rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary text-base"
placeholder="Email address"
/>
</div>
Expand All @@ -137,7 +149,7 @@ export default function LoginPage() {
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="relative block w-full px-3 py-2 border border-border placeholder-muted-foreground text-foreground bg-background rounded-b-md focus:outline-none focus:ring-primary focus:border-primary"
className="block w-full px-4 py-3 border border-border placeholder-muted-foreground text-foreground bg-background rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary text-base"
placeholder="Password"
/>
</div>
Expand All @@ -153,7 +165,7 @@ export default function LoginPage() {
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50"
className="group relative w-full flex justify-center py-3 px-4 border border-transparent text-base font-medium rounded-md text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 min-h-[44px]"
>
{loading ? 'Signing in...' : 'Sign in'}
</button>
Expand All @@ -169,13 +181,13 @@ export default function LoginPage() {
</div>
</div>

<div className="mt-6 grid grid-cols-2 gap-3">
<div className="mt-6 grid grid-cols-1 sm:grid-cols-2 gap-3">
{/* Google Auth Button */}
<button
onClick={handleGoogleLogin}
type="button"
disabled={!isGoogleAuthEnabled}
className={`w-full inline-flex justify-center py-2 px-4 border rounded-md shadow-sm text-sm font-medium transition-colors ${isGoogleAuthEnabled
className={`w-full inline-flex justify-center py-3 px-4 border rounded-md shadow-sm text-base font-medium transition-colors min-h-[44px] ${isGoogleAuthEnabled
? 'border-border bg-background text-muted-foreground hover:bg-accent'
: 'border-border bg-muted text-muted-foreground cursor-not-allowed'
}`}
Expand All @@ -188,7 +200,7 @@ export default function LoginPage() {
onClick={handleAppleLogin}
type="button"
disabled={!isAppleAuthEnabled}
className={`w-full inline-flex justify-center items-center py-2 px-4 border rounded-md shadow-sm text-sm font-medium transition-colors ${isAppleAuthEnabled
className={`w-full inline-flex justify-center items-center py-3 px-4 border rounded-md shadow-sm text-base font-medium transition-colors min-h-[44px] ${isAppleAuthEnabled
? 'border-border bg-background text-muted-foreground hover:bg-accent'
: 'border-border bg-muted text-muted-foreground cursor-not-allowed relative'
}`}
Expand All @@ -211,6 +223,17 @@ export default function LoginPage() {
)}
</div>
</form>

{/* Debug: Clear stale auth data button */}
<div className="mt-6 pt-4 border-t border-border">
<button
onClick={handleClearAuthData}
type="button"
className="w-full text-xs text-muted-foreground hover:text-foreground underline decoration-dotted underline-offset-4 transition-colors"
>
Having login issues? Clear authentication data
</button>
</div>
</div>
</div>
)
Expand Down
22 changes: 11 additions & 11 deletions app/auth/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export default function SignupPage() {
}

return (
<div className="min-h-screen flex items-center justify-center bg-background">
<div className="max-w-md w-full space-y-8">
<div className="min-h-screen flex items-center justify-center bg-background px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8 p-6 sm:p-8 border border-border rounded-lg shadow-sm bg-card">
<div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-foreground">
Create your LocalLoop account
Expand All @@ -77,21 +77,21 @@ export default function SignupPage() {
</p>
</div>

<form className="mt-8 space-y-6" onSubmit={handleEmailSignup}>
<form className="mt-6 space-y-6" onSubmit={handleEmailSignup}>
{error && (
<div className="bg-red-50 dark:bg-red-950 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-300 px-4 py-3 rounded">
<div className="bg-red-50 dark:bg-red-950 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-300 px-4 py-3 rounded-md">
{error}
</div>
)}

<div className="rounded-md shadow-sm -space-y-px">
<div className="space-y-4">
<div>
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="relative block w-full px-3 py-2 border border-border placeholder-muted-foreground text-foreground bg-background rounded-t-md focus:outline-none focus:ring-primary focus:border-primary"
className="block w-full px-4 py-3 border border-border placeholder-muted-foreground text-foreground bg-background rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary text-base"
placeholder="Email address"
/>
</div>
Expand All @@ -101,7 +101,7 @@ export default function SignupPage() {
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="relative block w-full px-3 py-2 border border-border placeholder-muted-foreground text-foreground bg-background rounded-b-md focus:outline-none focus:ring-primary focus:border-primary"
className="block w-full px-4 py-3 border border-border placeholder-muted-foreground text-foreground bg-background rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary text-base"
placeholder="Password"
/>
</div>
Expand All @@ -111,7 +111,7 @@ export default function SignupPage() {
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50"
className="group relative w-full flex justify-center py-3 px-4 border border-transparent text-base font-medium rounded-md text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 min-h-[44px]"
>
{loading ? 'Creating account...' : 'Create account'}
</button>
Expand All @@ -127,13 +127,13 @@ export default function SignupPage() {
</div>
</div>

<div className="mt-6 grid grid-cols-2 gap-3">
<div className="mt-6 grid grid-cols-1 sm:grid-cols-2 gap-3">
{/* Google Auth Button */}
<button
onClick={handleGoogleLogin}
type="button"
disabled={!isGoogleAuthEnabled}
className={`w-full inline-flex justify-center py-2 px-4 border rounded-md shadow-sm text-sm font-medium transition-colors ${isGoogleAuthEnabled
className={`w-full inline-flex justify-center py-3 px-4 border rounded-md shadow-sm text-base font-medium transition-colors min-h-[44px] ${isGoogleAuthEnabled
? 'border-border bg-background text-muted-foreground hover:bg-accent'
: 'border-border bg-muted text-muted-foreground cursor-not-allowed'
}`}
Expand All @@ -146,7 +146,7 @@ export default function SignupPage() {
onClick={handleAppleLogin}
type="button"
disabled={!isAppleAuthEnabled}
className={`w-full inline-flex justify-center items-center py-2 px-4 border rounded-md shadow-sm text-sm font-medium transition-colors ${isAppleAuthEnabled
className={`w-full inline-flex justify-center items-center py-3 px-4 border rounded-md shadow-sm text-base font-medium transition-colors min-h-[44px] ${isAppleAuthEnabled
? 'border-border bg-background text-muted-foreground hover:bg-accent'
: 'border-border bg-muted text-muted-foreground cursor-not-allowed relative'
}`}
Expand Down
Loading
Loading