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
4 changes: 0 additions & 4 deletions src/app/api/auth/session/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ export async function GET(req: NextRequest) {
const res = NextResponse.json({
success: true,
user: profile || null,
tokens: {
access_token: currentAccessToken,
refresh_token: currentRefreshToken,
},
});

if (currentAccessToken) {
Expand Down
8 changes: 5 additions & 3 deletions src/components/entry/EntryScanner.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
import { fetchSessionFromDatabase, getSupabase } from '@/lib/supabase';
import { fetchSessionFromDatabase, getCookie, getSupabase } from '@/lib/supabase';
import { DESK_RE, REJECT_REASONS, REJECT_REASON_LABELS, type RejectReason } from '@/lib/entry-shared';
import styles from './EntryScanner.module.css';

Expand Down Expand Up @@ -89,7 +89,8 @@ async function currentToken(): Promise<string | null> {
const { data } = await getSupabase().auth.getSession();
if (data.session?.access_token) return data.session.access_token;
} catch {}
return (await fetchSessionFromDatabase()).tokens?.access_token ?? null;
await fetchSessionFromDatabase();
return getCookie('inn_access_token');
}

/** After a 401: force a refresh (the auto-refresh timer is throttled on phones), else the server-side refresh. */
Expand All @@ -98,7 +99,8 @@ async function refreshedToken(): Promise<string | null> {
const { data, error } = await getSupabase().auth.refreshSession();
if (!error && data.session?.access_token) return data.session.access_token;
} catch {}
return (await fetchSessionFromDatabase()).tokens?.access_token ?? null;
await fetchSessionFromDatabase();
return getCookie('inn_access_token');
}

const offline = () => typeof navigator !== 'undefined' && navigator.onLine === false;
Expand Down
4 changes: 2 additions & 2 deletions src/components/entry/SessionRestore.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { useEffect } from 'react';
import { fetchSessionFromDatabase } from '@/lib/supabase';
import { fetchSessionFromDatabase, getCookie } from '@/lib/supabase';
import styles from './EntryScanner.module.css';

const LOGIN_URL = '/?auth=login&required=scan'; // the home page returns to /admin/scan after login
Expand All @@ -25,7 +25,7 @@ export default function SessionRestore() {
}
fetchSessionFromDatabase()
.then((s) => {
if (s.tokens?.access_token) window.location.reload();
if (s.user || getCookie('inn_access_token')) window.location.reload();
else window.location.replace(LOGIN_URL);
})
.catch(() => window.location.replace(LOGIN_URL));
Expand Down
9 changes: 5 additions & 4 deletions src/components/innovision/AdminDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React, { useState, useEffect, useCallback } from 'react';
import type { UserProfile, Registration } from '@/lib/supabase';
import { getSupabase, fetchSessionFromDatabase } from '@/lib/supabase';
import { getSupabase, fetchSessionFromDatabase, getCookie } from '@/lib/supabase';
import { csvCell, GENDER_OPTIONS, safeHttpUrl } from '@/lib/validation';

const genderLabel = (value: string) => GENDER_OPTIONS.find((g) => g.value === value)?.label ?? value;
Expand Down Expand Up @@ -97,10 +97,11 @@ export default function AdminDashboard({
}
}

// Fallback 3: Query server session (auto-refreshes tokens if expired)
// Fallback 3: Query server session (auto-refreshes tokens in cookies if expired)
const dbAuth = await fetchSessionFromDatabase();
if (dbAuth.tokens?.access_token) {
return dbAuth.tokens.access_token;
if (dbAuth.user) {
const cookieToken = getCookie('inn_access_token');
if (cookieToken) return cookieToken;
}
} catch (e) {
console.warn('getAuthToken error:', e);
Expand Down
9 changes: 6 additions & 3 deletions src/components/innovision/Innovision.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
cleanAuthUrl,
saveSessionToDatabase,
fetchSessionFromDatabase,
getCookie,
purgeLocalStorageTokens,
type UserProfile,
type Registration,
Expand Down Expand Up @@ -1666,11 +1667,13 @@ export class Innovision extends Component<Props, State> {
// 3. Fall back to Server/Database session via cookies (ZERO localStorage)
const dbAuth = await fetchSessionFromDatabase();
if (dbAuth.user) {
if (dbAuth.tokens?.access_token) {
const cookieAccessToken = getCookie('inn_access_token');
const cookieRefreshToken = getCookie('inn_refresh_token');
if (cookieAccessToken) {
try {
await supabase.auth.setSession({
access_token: dbAuth.tokens.access_token,
refresh_token: dbAuth.tokens.refresh_token || '',
access_token: cookieAccessToken,
refresh_token: cookieRefreshToken || '',
});
} catch (e) {
console.warn('setSession failed:', e);
Expand Down
14 changes: 7 additions & 7 deletions src/lib/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export interface GalleryPhoto {
export type GalleryItem = GalleryPhoto;

// Cookie-based storage adapter for Supabase Client: ZERO tokens or keys ever touch browser localStorage
function getCookie(name: string): string | null {
export function getCookie(name: string): string | null {
if (typeof document === 'undefined') return null;
const nameEQ = encodeURIComponent(name) + '=';
const ca = document.cookie.split(';');
Expand Down Expand Up @@ -382,23 +382,23 @@ export async function saveSessionToDatabase(session: Session | null): Promise<bo
}

/**
* Fetches the user session directly from the database using the HttpOnly cookie.
* Fetches the user session directly from the database using cookies.
* Tokens are kept strictly in cookies and never returned in the API response payload.
*/
export async function fetchSessionFromDatabase(): Promise<{
user: UserProfile | null;
tokens: { access_token: string; refresh_token?: string } | null;
}> {
try {
const res = await fetch('/api/auth/session');
if (!res.ok) return { user: null, tokens: null };
if (!res.ok) return { user: null };
const data = await res.json();
if (data.success && data.user) {
return { user: data.user, tokens: data.tokens || null };
return { user: data.user };
}
return { user: null, tokens: null };
return { user: null };
} catch (err) {
console.warn('fetchSessionFromDatabase error:', err);
return { user: null, tokens: null };
return { user: null };
}
}

Expand Down
Loading