3.7 KiB
provider — AuthProviderAdapter contract + path-prefix validation
(no submodules)
Purpose
The provider module is the type-level + thin-runtime heart of
@suaveplan/api-auth-provider. It declares the abstract AuthProviderAdapter
interface every concrete account-lifecycle provider (Better-Auth, the rolled
reference, future Cognito/Auth0 bridges) structurally implements, and ships
the Zod-validated authPathPrefix constructor that asserts a deployment's
configured prefix is a legal segment-aligned mount point. By splitting the
contract from any concrete provider we keep this package optional: a
hosted-IdP deployment installs the session resolver (api-auth) only and
never pulls in a provider implementation.
Features
AuthProviderAdapter<Caps>interface withid, configurablepathPrefix,routes(router, ctx)registration callback, and the four-hookAuthProviderLifecyclequartet (install/start/stop/uninstall).AuthPathPrefixopaque brand — protects against accidental string substitution at the install boundary.AuthPathPrefixSchema— a Zod schema enforcing leading slash, no trailing slash (except/), no path parameters, no whitespace.authPathPrefix(value)constructor — returns the branded value or throwsAUTH_PROVIDER.PROVIDER_LIFECYCLE_FAILEDwith a structuredreason.isAuthPathPrefix(value)predicate — non-throwing variant for tests.DEFAULT_AUTH_PATH_PREFIXconstant ("/auth").
Usage: Basic
import {
authPathPrefix,
DEFAULT_AUTH_PATH_PREFIX,
type AuthProviderAdapter,
} from "@suaveplan/api-auth-provider/provider";
const myProvider: AuthProviderAdapter = {
id: "rolled",
pathPrefix: authPathPrefix(DEFAULT_AUTH_PATH_PREFIX),
routes(router) {
router.post("/login", { auth: null }, async () => new Response());
},
lifecycle: {
install: async () => {},
start: async () => {},
stop: async () => {},
uninstall: async () => {},
},
};
Usage: Advanced
A deployment that mounts its provider under a non-standard prefix MUST run
the input through authPathPrefix so the value is validated before the host
records the claim in the path-ownership registry.
import { authPathPrefix } from "@suaveplan/api-auth-provider/provider";
const prefix = authPathPrefix(process.env.AUTH_PREFIX ?? "/auth/v2");
// Throws AUTH_PROVIDER.PROVIDER_LIFECYCLE_FAILED on input like
// "/auth/v2/" or "/auth/:tenant".
API Reference
interface AuthProviderAdapter<Caps>— provider contract.interface AuthProviderLifecycle<Caps>— install/start/stop/uninstall.type AuthPathPrefix— branded prefix string.const AuthPathPrefixSchema: ZodType— validation schema.function authPathPrefix(value: string): AuthPathPrefix— throwing constructor.function isAuthPathPrefix(value: string): boolean— predicate.const DEFAULT_AUTH_PATH_PREFIX = "/auth"— host default.
Implementation Notes
The brand is purely a TypeScript construct (unique symbol) — at runtime
authPathPrefix("/auth") returns the string unchanged. The schema's order of
refinements is significant: the leading-slash check fires first so callers
see the most actionable error message. Whitespace is rejected before the
parameter check because shell-supplied configuration is the typical source of
both classes of error. The constructor surfaces failures as
PROVIDER_LIFECYCLE_FAILED (not a hypothetical INVALID_PREFIX) because the
install lifecycle is the only legitimate call site — keeping the code small
makes the error registry tighter and the metadata table denser.