# Capability Spec Delta: api-auth-provider (api-auth-provider) **Package**: `@suaveplan/api-auth-provider` **Status**: PROPOSED > Every requirement carries a `**Verified by:**` line. ## ADDED Requirements ### Requirement: AuthProviderAdapter contract The package SHALL export `AuthProviderAdapter` interface: `pathPrefix: string` (default `/auth`), `routes(router: Router, ctx: PluginContext): void` to register provider routes, `lifecycle: { install, start, stop, uninstall }` hooks matching the generic plugin lifecycle. The adapter MUST be generic over `Caps extends CapabilityMap`. #### Scenario: every documented field present - **WHEN** `expectTypeOf()` is checked - **THEN** `pathPrefix`, `routes`, and the four lifecycle hooks are declared **Verified by:** `src/provider/provider.spec-conformance.test.ts::"AuthProviderAdapter declares pathPrefix routes lifecycle"` --- ### Requirement: `import z from "zod"` everywhere Every Zod import MUST use the default form. #### Scenario: no named import of z - **WHEN** the test greps `import z` from `"zod"` - **THEN** zero matches **Verified by:** `tests/zod-import-form.test.ts::"every src file uses default-import z"` --- ### Requirement: Canonical provider route surface The package SHALL export `PROVIDER_ROUTE_SURFACE` listing the canonical paths a provider OWNS (relative to `pathPrefix`): `/register`, `/login`, `/logout`, `/refresh`, `/oauth/:provider/start`, `/oauth/:provider/callback`, `/verify-email/:token`, `/forgot-password`, `/reset-password`. Providers MAY implement a subset; the host does not require every path. #### Scenario: surface enumerates the documented paths - **WHEN** the test inspects `PROVIDER_ROUTE_SURFACE` - **THEN** every documented path is listed **Verified by:** `src/route-surface/route-surface.test.ts::"canonical surface lists every documented path"` --- ### Requirement: Path-ownership registry The package SHALL export `PathOwnership` — a runtime registry. `register(provider)` claims `provider.pathPrefix`. A second `register(provider2)` with a different provider MUST throw `AUTH_PROVIDER.MULTIPLE_PROVIDERS_REGISTERED`. Re-registering the SAME provider instance MUST be a no-op (idempotent). #### Scenario: double registration with different provider rejected - **WHEN** provider A is registered AND provider B is registered - **THEN** the second call throws `AUTH_PROVIDER.MULTIPLE_PROVIDERS_REGISTERED` #### Scenario: re-registering same provider is idempotent - **WHEN** provider A is registered twice - **THEN** the second call returns without throwing **Verified by:** `src/path-ownership/path-ownership.test.ts::"double registration rejected"` AND `src/path-ownership/path-ownership.test.ts::"re-registering same provider is idempotent"` --- ### Requirement: Collision detector The package SHALL export `detectAuthPathCollisions({ provider, plugins }): CollisionReport`. A plugin route whose prefix overlaps with `provider.pathPrefix` AND is not in `publicRoutes` MUST be reported with code `AUTH_PROVIDER.PATH_COLLISION` AND `details.plugin` AND `details.path`. Plugin routes outside the auth prefix MUST NOT be reported. #### Scenario: overlap reported - **WHEN** provider owns `/auth` AND plugin declares route `/auth/custom` - **THEN** the report includes `{ code: "AUTH_PROVIDER.PATH_COLLISION", details: { plugin: ..., path: "/auth/custom" } }` #### Scenario: non-overlap not reported - **WHEN** provider owns `/auth` AND plugin declares route `/billing/invoices` - **THEN** the report is empty #### Scenario: configurable prefix shifts collision domain - **WHEN** provider's `pathPrefix === "/myauth"` AND plugin declares route `/auth/x` - **THEN** the report is empty AND a plugin route `/myauth/x` would be reported **Verified by:** `src/collision-detector/collision-detector.test.ts::"overlap with auth prefix reported"` AND `src/collision-detector/collision-detector.test.ts::"non-overlap not reported"` AND `src/collision-detector/collision-detector.test.ts::"configurable prefix shifts collision domain"` --- ### Requirement: CapabilityId augmentation contributes "auth.provider" The package SHALL augment the `CapabilityId` union to include `"auth.provider"`. #### Scenario: auth.provider assignable to CapabilityId - **WHEN** `expectTypeOf<"auth.provider">().toMatchTypeOf()` is checked - **THEN** the assertion passes **Verified by:** `src/capability-id/capability-id.spec-conformance.test.ts::"auth.provider is a valid CapabilityId after augmentation"` --- ### Requirement: Error codes registry frozen The package SHALL export `AUTH_PROVIDER_CODES` containing exactly: `PATH_COLLISION`, `MULTIPLE_PROVIDERS_REGISTERED`, `PROVIDER_LIFECYCLE_FAILED`, `OAUTH_CALLBACK_INVALID_STATE`. #### Scenario: every code reachable - **WHEN** the test enumerates `AUTH_PROVIDER_CODES` - **THEN** every code has a production throw site **Verified by:** `src/error-codes/error-codes.spec-conformance.test.ts::"every AUTH_PROVIDER_CODES entry thrown by production path"` --- ### Requirement: Conformance harness for providers The package SHALL export `runAuthProviderConformance({ factory, label })`. Every concrete provider in Stages 6/8 MUST pass it. #### Scenario: harness covers path ownership, route surface, lifecycle, collision - **WHEN** the harness runs against a stub provider - **THEN** test categories exist for each documented behavior **Verified by:** `src/testing/conformance.test.ts::"harness covers path ownership, route surface, lifecycle, collision"`