3.6 KiB
services/auth-service
Purpose
The auth-service module owns the AuthService interface, its
constructor (createAuthService), and the side-effect module
augmentation that adds the authService slot to
@suaveplan/api-plugin/service-registry's ServiceRegistry. Together
they let sibling plugins resolve the current principal and the
underlying Better-Auth instance with full type safety — no casts at the
call site.
Features
AuthServiceinterface with three methods:getCurrentPrincipal(c),getBetterAuthInstance(), andgetSession(headers).createAuthServiceconstructor wired to a Better-Auth instance and consumed exclusively bycreateAuthPlugin'sonInit.- Side-effect module augmentation in
./registry-augmentation.tsso importing anything from@suaveplan/api-plugin-authmakesctx.getService("authService")type-narrow toAuthService | undefined.
Usage: Basic
import { createAuthService } from "@suaveplan/api-plugin-auth/services/auth-service";
const service = createAuthService({ auth });
const principal = service.getCurrentPrincipal(c);
Usage: Advanced
From a sibling plugin's onInit, resolve the service and use it to
authenticate a stand-alone request (e.g. a queued job that carries a
serialised cookie jar):
import { definePlugin } from "@suaveplan/api-plugin";
import type { Principal } from "@suaveplan/api-plugin-auth/principal/principal";
export const vaultPlugin = definePlugin({
name: "vault",
version: "0.1.0",
dependencies: ["auth"],
async onInit(ctx) {
const authService = ctx.getService("authService");
if (!authService) {
throw new Error("authService not registered — is the auth plugin enabled?");
}
const headers = new Headers(savedCookieJar);
const result = await authService.getSession(headers);
if (result === null) return;
// result.user / result.session are typed via BetterAuthSessionResult
},
});
API Reference
interface AuthService
getCurrentPrincipal(c: Context): Principal | null— readsc.get("user"); returnsnullfor anonymous requests.getBetterAuthInstance(): Auth— returns the Better-Auth instance constructed bycreateAuthPlugin.getSession(headers: Headers): Promise<BetterAuthSessionResult>— resolves a Better-Auth session from arbitrary request headers, useful outside the request scope.
type BetterAuthSessionResult
{ user: Record<string, unknown>; session: Record<string, unknown> } | null.
interface CreateAuthServiceOptions
{ auth: Auth }.
function createAuthService(options): AuthService
Pure constructor — wraps the Better-Auth instance with the three-method service shape.
Module augmentation
./registry-augmentation.ts declares
declare module "@suaveplan/api-plugin/service-registry" { interface ServiceRegistry { authService: AuthService } }. Side-effect import.
Implementation Notes
- The service holds a reference to the Better-Auth instance — call
sites that need the raw handler (e.g. the Hono catch-all route
installed by
createAuthPlugin) reach forgetBetterAuthInstance().handlerrather than re-constructing the Better-Auth client. BetterAuthSessionResultis intentionally loose because Better-Auth plugins (organisations, passkeys, twoFactor) extend thesessionrow with additional fields. The auth plugin's ownPrincipalmapping decides which of those fields surface to consumers.- Session resolution is asynchronous; downstream plugins that want a
synchronous principal-read use
getCurrentPrincipal(c)from inside a Hono handler instead.