first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
# middleware/require-auth
## Purpose
`requireAuth()` is the canonical Hono middleware factory for gating a
route or sub-route on an authenticated principal. It reads
`c.get("user")` and, if the value is `null` (or absent), throws
`AuthError(AUTH_UNAUTHENTICATED)` carrying `context.statusCode: 401`
so the `@suaveplan/api` error-handler middleware translates it to an
HTTP 401 response.
## Features
- Depends only on the typed `c.get("user")` slot installed by
`sessionMiddleware` — no dynamic service lookup, no per-request
Better-Auth call.
- Single failure mode: `AUTH_UNAUTHENTICATED` with `statusCode: 401`
attached to the error context.
- Idempotent — applying the middleware twice in the same chain costs
one type narrow + one truthy check.
## Usage: Basic
```ts
import { requireAuth } from "@suaveplan/api-plugin-auth/middleware/require-auth";
app.use("/private/*", requireAuth());
app.get("/private/me", (c) => c.json(c.get("user")));
```
## Usage: Advanced
Compose `requireAuth` with `requireRole` and `requireEmailVerified` to
build a sliding scale of access controls. Each middleware throws its
own typed `AuthError`, so the error handler can emit specific OpenAPI
4xx responses per stage:
```ts
import {
requireAuth,
requireRole,
requireEmailVerified,
} from "@suaveplan/api-plugin-auth/middleware";
app.use("/admin/*", requireAuth(), requireRole("admin"));
app.use("/billing/*", requireAuth(), requireEmailVerified());
```
## API Reference
### `function requireAuth(): MiddlewareHandler`
Returns a Hono middleware handler. Throws
`AuthError(AUTH_UNAUTHENTICATED, "Authentication required",
{ context: { statusCode: 401 } })` when `c.get("user")` is null or
undefined. Calls `next()` otherwise.
## Implementation Notes
- The check uses `=== null || === undefined` rather than a truthy
check so a fully-populated `Principal` with falsy values (e.g.
`email: ""` after the principal mapping normalised it out) still
passes. `Principal` objects are never falsy.
- Throwing — not returning a 401 response directly — keeps the error
shape consistent with `requireRole` and `requireEmailVerified`; all
three flow through the `@suaveplan/api` error-handler, which both
serialises the error envelope and emits the OTel span event.
- The `statusCode: 401` field is read by the error-handler middleware
installed by `@suaveplan/api`; the auth plugin does not own status
mapping itself.