Files
suaveplan/packages/api-stack/api/api-plugin-auth/src/services/auth-service.md
2026-05-27 23:29:53 +01:00

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

  • AuthService interface with three methods: getCurrentPrincipal(c), getBetterAuthInstance(), and getSession(headers).
  • createAuthService constructor wired to a Better-Auth instance and consumed exclusively by createAuthPlugin's onInit.
  • Side-effect module augmentation in ./registry-augmentation.ts so importing anything from @suaveplan/api-plugin-auth makes ctx.getService("authService") type-narrow to AuthService | 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 — reads c.get("user"); returns null for anonymous requests.
  • getBetterAuthInstance(): Auth — returns the Better-Auth instance constructed by createAuthPlugin.
  • 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 for getBetterAuthInstance().handler rather than re-constructing the Better-Auth client.
  • BetterAuthSessionResult is intentionally loose because Better-Auth plugins (organisations, passkeys, twoFactor) extend the session row with additional fields. The auth plugin's own Principal mapping 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.