implement better-auth auth with postgres and route protection
This commit is contained in:
121
lib/server/auth-session.ts
Normal file
121
lib/server/auth-session.ts
Normal 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)
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user