Skip to content
Open
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
86 changes: 74 additions & 12 deletions src/app/api/auth/salt/route.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,75 @@
import { NextResponse } from 'next/server';
import { createServiceRoleClient } from '@/lib/supabase/service-role.js';
import { createClient } from '@supabase/supabase-js';

function getServiceRoleClient() {
return createServiceRoleClient();
}

// Anon client for JWT validation only
const supabaseClient = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);

/**
* Authenticate caller and return their auth user.
* Accepts either a Bearer token (Authorization header) or Supabase cookies.
*/
async function authenticateUser(request) {
try {
const authHeader = request.headers.get('authorization');
if (authHeader?.startsWith('Bearer ')) {
const token = authHeader.substring(7);
const { data: { user }, error } = await supabaseClient.auth.getUser(token);
if (!error && user) return { user };
}

// Fall back to cookies
const cookieHeader = request.headers.get('cookie') ?? '';
const cookies = Object.fromEntries(
cookieHeader.split('; ').map(c => {
const [name, ...rest] = c.split('=');
return [name, rest.join('=')];
})
);

let accessToken = null;
for (const [name, value] of Object.entries(cookies)) {
if (name.includes('auth-token')) {
try {
const raw = value.startsWith('base64-')
? Buffer.from(value.slice(7), 'base64').toString('utf-8')
: decodeURIComponent(value);
const parsed = JSON.parse(raw);
if (parsed.access_token) { accessToken = parsed.access_token; break; }
} catch {}
}
}
if (!accessToken) return { user: null, error: 'No authentication found' };

const { data: { user }, error } = await supabaseClient.auth.getUser(accessToken);
if (error || !user) return { user: null, error: 'Invalid token' };
return { user };
} catch (err) {
return { user: null, error: err.message };
}
}

export async function GET(request) {
const url = new URL(request.url);
try {
const phone = url.searchParams.get('phone');
if (!phone) {
return NextResponse.json({ error: 'Missing phone parameter' }, { status: 400 });
// Require authentication: callers may only fetch their OWN salt.
const { user, error: authError } = await authenticateUser(request);
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

// Look up the authenticated user's own record instead of an arbitrary phone number.
const { data, error } = await getServiceRoleClient()
.from('users')
.select('salt')
.eq('phone_number', phone)
.eq('auth_user_id', user.id)
.single();

if (error) {
Expand All @@ -34,27 +86,37 @@ export async function GET(request) {

export async function POST(request) {
try {
const { phone, salt } = await request.json();
// Require authentication: callers may only set their OWN salt.
const { user, error: authError } = await authenticateUser(request);
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

if (!phone || !salt) {
return NextResponse.json({ error: 'Missing phone or salt' }, { status: 400 });
const { salt } = await request.json();
if (!salt) {
return NextResponse.json({ error: 'Missing salt' }, { status: 400 });
}

// Only store salt if user doesn't already have one
// Verify the authenticated user has a record and look up their phone.
const { data: existing } = await getServiceRoleClient()
.from('users')
.select('salt')
.eq('phone_number', phone)
.select('salt, phone_number')
.eq('auth_user_id', user.id)
.single();

if (existing?.salt) {
if (!existing) {
return NextResponse.json({ error: 'User record not found' }, { status: 404 });
}

// Only store salt if user doesn't already have one (idempotent).
if (existing.salt) {
return NextResponse.json({ salt: existing.salt, existing: true });
}

const { error } = await getServiceRoleClient()
.from('users')
.update({ salt })
.eq('phone_number', phone);
.eq('auth_user_id', user.id);

if (error) {
console.error('Salt POST update error:', error);
Expand Down