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
45 changes: 23 additions & 22 deletions app/api/auth/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,40 @@ export async function POST(req) {
if (!email || !password) {
return new Response(JSON.stringify({ success: false, message: 'Email and password are required' }), { status: 400 })
}
if (!captchaToken) {
return new Response(JSON.stringify({ success: false, message: 'Captcha token missing' }), { status: 400 })
}

if (action === 'signup') {
if (!captchaToken) {
return new Response(JSON.stringify({ success: false, message: 'Captcha token missing' }), { status: 400 })
}

// Verify Turnstile token
const verifyRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
secret: process.env.TURNSTILE_SECRET_KEY,
response: captchaToken,
}),
})

const verifyData = await verifyRes.json()
if (!verifyData.success) {
return new Response(JSON.stringify({ success: false, message: 'Captcha verification failed' }), { status: 400 })
}
// Verify Turnstile token for both signup and login
const verifyRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
secret: process.env.TURNSTILE_SECRET_KEY,
response: captchaToken,
}),
})
const verifyData = await verifyRes.json()
if (!verifyData.success) {
return new Response(JSON.stringify({ success: false, message: 'Captcha verification failed' }), { status: 400 })
}

if (action === 'signup') {
// Create Supabase user
const { user, error } = await supabase.auth.admin.createUser({ email, password })
if (error) {
return new Response(JSON.stringify({ success: false, message: error.message }), { status: 400 })
}

return new Response(JSON.stringify({ success: true, message: 'Signup successful! Check your email.' }), { status: 200 })
}

// Login stays frontend-only
else if (action === 'login') {
return new Response(JSON.stringify({ success: false, message: 'Use frontend login with anon key' }), { status: 400 })
// Authenticate user using Supabase admin API (signInWithPassword)
const { data, error } = await supabase.auth.admin.signInWithPassword({ email, password })
if (error) {
return new Response(JSON.stringify({ success: false, message: error.message }), { status: 400 })
}
return new Response(JSON.stringify({ success: true, message: 'Login successful.' }), { status: 200 })
}

// Invalid action
Expand Down
49 changes: 24 additions & 25 deletions app/login/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,27 @@ export default function LoginPage() {
setError('')

try {
if (!captchaToken) throw new Error('Please complete captcha')

const res = await fetch('/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email,
password,
captchaToken,
action: isLogin ? 'login' : 'signup'
}),
})

const data = await res.json()
if (!data.success) throw new Error(data.message || 'Action failed')

if (isLogin) {
// Login using frontend anon key only, no captcha
const { error } = await supabase.auth.signInWithPassword({ email, password })
if (error) throw error
router.push('/dashboard')
} else {
// Signup via API route with Turnstile
if (!captchaToken) throw new Error('Please complete captcha')

const res = await fetch('/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, captchaToken, action: 'signup' }),
})

const data = await res.json()
if (!data.success) throw new Error(data.message || 'Signup failed')

alert(data.message)
setIsLogin(true) // switch to login after signup
setIsLogin(true)
}
} catch (err) {
setError(err.message || 'Something went wrong')
Expand Down Expand Up @@ -141,15 +142,13 @@ export default function LoginPage() {
</div>
)}

{/* Turnstile only for signup */}
{!isLogin && (
<div className="flex justify-center">
<Turnstile
siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
onSuccess={(token) => setCaptchaToken(token)}
/>
</div>
)}
{/* Turnstile for both login and signup */}
<div className="flex justify-center">
<Turnstile
siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
onSuccess={(token) => setCaptchaToken(token)}
/>
</div>

<button
onClick={handleAuth}
Expand Down
Loading