Migrate authentication to Better Auth
Replace the hand-rolled JWT auth with Better Auth 1.6.25 httpOnly cookie sessions, validated against the database on every request so revocation, bans and role changes take effect immediately. Backend: - betterAuth.ts wires the Drizzle adapter, magic links, Google sign-in and the admin plugin; auth-schema.ts maps Better Auth's models onto the existing `users` table so user IDs and their foreign keys survive intact. - routes/auth.ts is gone; Better Auth serves the standard endpoints and authExt.ts carries the flows it doesn't cover. - auth.ts shrinks to session resolution and helpers; sessions/revocation in dashboard.ts now read and delete `auth_sessions` rows directly. - Schema adds the Better Auth core + admin columns (email_verified, image, banned, ban_reason, ban_expires), with migrations and tests. - rateLimit.ts resolves client IPs spoof-resistantly: proxy headers are only honoured from loopback/RFC1918 peers plus TRUSTED_PROXIES. - passwordPolicy.ts centralises password validation. - Bump drizzle-orm, drizzle-kit and better-sqlite3 to versions compatible with Better Auth. Frontend: - auth-client.ts plus a reworked AuthContext and api/client.ts move to cookie-based sessions; no more bearer tokens in requests or middleware. photo-api: - Validate Better Auth session cookies against the shared auth_sessions table instead of verifying JWTs; JWT_SECRET is no longer needed for user auth, and PHOTO_VIEW_SECRET now signs gallery view tokens. BETTER_AUTH_SECRET and BETTER_AUTH_URL are required in production; the deprecated JWT_SECRET stays only as the photo-api view-token fallback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4afa5d6fa0
commit
733d2459df
@@ -48,7 +48,7 @@ export default function GalleryClient({ slug, eventSlug, initial }: GalleryClien
|
||||
// viewer's own auth token attached.
|
||||
useEffect(() => {
|
||||
if (initial) return;
|
||||
if (authLoading) return; // wait so the Bearer token is available
|
||||
if (authLoading) return; // wait until the session state has resolved
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
const fetcher = eventSlug
|
||||
@@ -354,6 +354,45 @@ export default function GalleryClient({ slug, eventSlug, initial }: GalleryClien
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Call to action: send attendees to their dashboard, everyone else to
|
||||
the next event. Auth state comes from the same useAuth() the gate
|
||||
pages use. */}
|
||||
<div className="bg-brand-navy">
|
||||
<div className="container-page px-4 py-12 md:py-16 text-center">
|
||||
<h2 className="font-heading font-bold text-2xl md:text-3xl text-white">
|
||||
{user
|
||||
? es
|
||||
? '¿Listo para lo que sigue?'
|
||||
: 'Ready for what’s next?'
|
||||
: es
|
||||
? '¿Te gustó lo que viste?'
|
||||
: 'Liked what you saw?'}
|
||||
</h2>
|
||||
<p className="mt-2 max-w-xl mx-auto text-white/80 text-sm md:text-base">
|
||||
{user
|
||||
? es
|
||||
? 'Revisa tus entradas y próximos eventos en tu panel.'
|
||||
: 'Check your tickets and upcoming events from your dashboard.'
|
||||
: es
|
||||
? 'Únete a nuestro próximo evento y sé parte de las próximas fotos.'
|
||||
: 'Join our next event and be part of the next set of photos.'}
|
||||
</p>
|
||||
<div className="mt-6">
|
||||
<Link href={user ? '/dashboard' : '/next'}>
|
||||
<Button size="lg">
|
||||
{user
|
||||
? es
|
||||
? 'Ir a mi panel'
|
||||
: 'Go to dashboard'
|
||||
: es
|
||||
? 'Únete al próximo evento'
|
||||
: 'Join the next event'}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user