Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
# 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
```ts
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):
```ts
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.