Initial Commit
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import type { SessionResolver } from "@suaveplan/api-auth";
|
||||
import type { Principal } from "@suaveplan/api-types/principal";
|
||||
import { LazyBetterAuth } from "../db/database.js";
|
||||
import { AUTH_BA_CODES, BetterAuthError } from "../error-codes/error-codes.js";
|
||||
import { getBetterAuthInstruments } from "../instruments/instruments.js";
|
||||
import { defaultBetterAuthFactory } from "../oauth/default-factory.js";
|
||||
import { parseBetterAuthOptions } from "../options/options.js";
|
||||
import type { BetterAuthOptions } from "../options/options.types.js";
|
||||
import { mapBetterAuthUserToPrincipal } from "../principal/map-user-to-principal.js";
|
||||
|
||||
/**
|
||||
* Construct a `SessionResolver` that asks Better-Auth's `auth.api.getSession`
|
||||
* to materialise the current session from the request's cookie/bearer, then
|
||||
* maps the resulting `(user, session)` view onto a `Principal` per
|
||||
* `@suaveplan/api-types`. Returns `null` when Better-Auth reports no
|
||||
* session — the host's resolver chain then advances to the next adapter
|
||||
* (typically `api-auth-anonymous`).
|
||||
*
|
||||
* Operators MAY share the underlying Better-Auth instance with
|
||||
* `createBetterAuthProvider(opts)` by passing the same options bag — the
|
||||
* factories memoise per-options so both sides resolve through one pool.
|
||||
* Wiring only the session adapter is the documented "Better-Auth for
|
||||
* session reads, Clerk for account lifecycle" topology and works without
|
||||
* standing up the provider routes.
|
||||
*/
|
||||
export function createBetterAuthSession(
|
||||
rawOptions: BetterAuthOptions
|
||||
): SessionResolver {
|
||||
const opts = parseBetterAuthOptions(rawOptions);
|
||||
const factory = opts.betterAuthFactory ?? defaultBetterAuthFactory;
|
||||
const lazy = new LazyBetterAuth(opts, factory);
|
||||
const map = opts.principalMapping ?? mapBetterAuthUserToPrincipal;
|
||||
let disposed = false;
|
||||
|
||||
return {
|
||||
async resolve(req: Request): Promise<Principal | null> {
|
||||
if (disposed) return null;
|
||||
const instruments = getBetterAuthInstruments();
|
||||
const started = nowMs();
|
||||
let outcome: "resolved" | "null" | "error" = "null";
|
||||
try {
|
||||
const auth = await lazy.get();
|
||||
const getSession = auth.api.getSession;
|
||||
if (getSession === undefined) {
|
||||
return null;
|
||||
}
|
||||
const sessionView = await getSession({
|
||||
headers: req.headers,
|
||||
});
|
||||
if (sessionView === null) {
|
||||
return null;
|
||||
}
|
||||
let principal: Principal;
|
||||
try {
|
||||
principal = map(sessionView);
|
||||
} catch (err) {
|
||||
instruments.principalMappingErrorsTotal.add(1);
|
||||
outcome = "error";
|
||||
throw err instanceof BetterAuthError
|
||||
? err
|
||||
: new BetterAuthError(
|
||||
AUTH_BA_CODES.PRINCIPAL_MAPPING_FAILED,
|
||||
"principalMapping threw during session resolution",
|
||||
{ cause: err }
|
||||
);
|
||||
}
|
||||
outcome = "resolved";
|
||||
return principal;
|
||||
} catch (err) {
|
||||
outcome = "error";
|
||||
if (err instanceof BetterAuthError) {
|
||||
throw err;
|
||||
}
|
||||
throw new BetterAuthError(
|
||||
AUTH_BA_CODES.INVALID_SESSION,
|
||||
"Better-Auth session resolution failed",
|
||||
{ cause: err }
|
||||
);
|
||||
} finally {
|
||||
const duration = nowMs() - started;
|
||||
instruments.sessionsResolvedTotal.add(1, { outcome });
|
||||
instruments.sessionResolveDurationMs.record(duration, {
|
||||
outcome,
|
||||
});
|
||||
}
|
||||
},
|
||||
async dispose(): Promise<void> {
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
lazy.reset();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/* istanbul ignore next -- `performance` is universally present on Node 18+ + Bun; the Date.now() fallback exists only as a defensive guard for stripped-down runtimes. */
|
||||
function nowMs(): number {
|
||||
return typeof performance === "object" &&
|
||||
typeof performance.now === "function"
|
||||
? performance.now()
|
||||
: Date.now();
|
||||
}
|
||||
Reference in New Issue
Block a user