implement better-auth auth with postgres and route protection

This commit is contained in:
2026-02-24 13:32:43 -05:00
parent fd168f607c
commit 52a4ab38d3
31 changed files with 1202 additions and 89 deletions

121
lib/server/auth-session.ts Normal file
View File

@@ -0,0 +1,121 @@
import { headers } from 'next/headers';
import { ensureAuthSchema } from '@/lib/auth';
import { asErrorMessage, jsonError } from '@/lib/server/http';
type RecordValue = Record<string, unknown>;
export type AuthenticatedUser = {
id: string;
email: string;
name: string | null;
image: string | null;
role?: string | string[];
};
export type AuthenticatedSession = {
user: AuthenticatedUser;
session: RecordValue | null;
raw: RecordValue;
};
const UNAUTHORIZED_SESSION: AuthenticatedSession = {
user: {
id: '',
email: '',
name: null,
image: null
},
session: null,
raw: {}
};
function asRecord(value: unknown): RecordValue | null {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return null;
}
return value as RecordValue;
}
function asString(value: unknown) {
return typeof value === 'string' && value.trim().length > 0 ? value : null;
}
function asNullableString(value: unknown) {
return typeof value === 'string' ? value : null;
}
function normalizeRole(value: unknown) {
if (typeof value === 'string') {
return value;
}
if (Array.isArray(value)) {
const roles = value.filter((entry): entry is string => typeof entry === 'string');
return roles.length > 0 ? roles : undefined;
}
return undefined;
}
function normalizeSession(rawSession: unknown): AuthenticatedSession | null {
const root = asRecord(rawSession);
if (!root) {
return null;
}
const rootSession = asRecord(root.session);
const userRecord = asRecord(root.user) ?? asRecord(rootSession?.user);
if (!userRecord) {
return null;
}
const id = asString(userRecord.id);
const email = asString(userRecord.email);
if (!id || !email) {
return null;
}
return {
user: {
id,
email,
name: asNullableString(userRecord.name),
image: asNullableString(userRecord.image),
role: normalizeRole(userRecord.role)
},
session: rootSession,
raw: root
};
}
export async function getAuthenticatedSession() {
const auth = await ensureAuthSchema();
const session = await auth.api.getSession({
headers: await headers()
});
return normalizeSession(session);
}
export async function requireAuthenticatedSession() {
try {
const session = await getAuthenticatedSession();
if (!session) {
return {
session: UNAUTHORIZED_SESSION,
response: jsonError('Unauthorized', 401)
};
}
return {
session,
response: null
};
} catch (error) {
return {
session: UNAUTHORIZED_SESSION,
response: jsonError(asErrorMessage(error, 'Authentication subsystem is unavailable.'), 500)
};
}
}