feat(auth) : sync Sudloh profiles and validate sessions

This commit is contained in:
2026-10-06 00:48:29 +07:00 Unverified
parent d6153be50d
commit c856904c12
16 changed files with 520 additions and 32 deletions
+18 -3
View File
@@ -19,6 +19,7 @@ import { isAuthorizedAdmin, type SessionUserLike } from "./authorization";
import { HttpError } from "@/lib/security/http";
import { consumeRateLimit } from "@/lib/security/rate-limit";
import { sendAuthEmail } from "./email";
import { bindSudlohSession, validateSudlohSession } from "./sudloh";
function required(name: string): string {
const value = process.env[name];
@@ -63,6 +64,11 @@ function createAuth() {
},
transaction: true,
}),
...(oidcOnly ? { databaseHooks: { session: { create: { after: async (
session: { id: string; userId: string }, context: { path: string } | null,
) => {
if (context?.path.endsWith("/callback/sudloh")) await bindSudlohSession(session.userId, session.id);
} } } } } : {}),
baseURL: required("BETTER_AUTH_URL"),
trustedOrigins: process.env.BETTER_AUTH_TRUSTED_ORIGINS
?.split(",")
@@ -125,6 +131,7 @@ function createAuth() {
requireIdTokenVerification: true,
requireEmailVerification: true,
disableProviderLogout: true,
overrideUserInfo: true,
}] })] : []),
...(!oidcOnly ? [emailOTP({
sendVerificationOnSignUp: true,
@@ -159,13 +166,22 @@ export interface AdminSession {
session: { id: string };
}
export const getVerifiedSession = cache(async () => {
if (!hasAuthConfiguration()) return null;
const session = await getAuth().api.getSession({ headers: await headers() });
if (!session?.session) return null;
if (isSudlohOidcEnabled() && process.env.SUDLOH_OIDC_ONLY === "true" &&
!await validateSudlohSession(session.user.id, session.session.id)) return null;
return session;
});
export const getAdminSession = cache(async (): Promise<AdminSession | null> => {
// Public pages can be prerendered without the runtime auth secret. In that
// case the header simply omits the admin link; auth routes still fail loudly
// through getAuth() when authentication is actually used.
if (!hasAuthConfiguration()) return null;
const session = await getAuth().api.getSession({ headers: await headers() });
const session = await getVerifiedSession();
if (
!session?.session ||
!isAuthorizedAdmin(session.user)
@@ -192,8 +208,7 @@ export async function requireAdmin(): Promise<AdminSession> {
}
export const getCustomerSession = cache(async () => {
if (!hasAuthConfiguration()) return null;
const session = await getAuth().api.getSession({ headers: await headers() });
const session = await getVerifiedSession();
if (!session?.session) return null;
return { user: session.user, session: { id: session.session.id } };
});