nestjs-slightly-better-auth 1.9.2 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -948,7 +948,7 @@ Better Auth treats a process with `NODE_ENV=test` or a truthy `TEST` variable (V
948
948
 
949
949
  The kits verify extension units against the invariants of the [reviewed specification](docs/design/design-v7.md) (§4 and §14.1). Each kit returns `ConformanceCase[]` (`{ id, title, skip?, run() }`) that `runConformance(cases, { describe, it })` registers with Vitest, Jest or `node:test`. Assertions use `node:assert/strict`; the kits import no test runner.
950
950
 
951
- Applicability comes from the unit, not from the options a harness passes. A platform that declares `capabilities.http2` needs the `http2` option. The platform kit's trusted-hop rows need `trustOneProxy`, and its microservice rows of `H-no-adapter` need `microservice`, which returns the transport options to boot with (for example `{ transport: Transport.TCP, options: { host: "127.0.0.1", port: 0 } }`); the kit does not load the optional `@nestjs/microservices` peer itself. The production-mode rows set `process.env.NODE_ENV` to `production` while they run and restore it afterwards, so run the kit's cases sequentially. A transport with a lineage needs `invokeTwice`, one that nests handlers (`defaultAccessFor`) needs `invokeGraph`, and one that claims the kit's federation reference resolver needs `invokeEntities`. A transport whose browser leg is a connection's handshake, shared by the connection's messages (the WebSocket transports), runs the `T-ws-origin-*` cases instead of the `T-csrf-*` cases and needs `invokeConnection`, plus `authenticateConnection` when the app provides `WS_CONNECTION_AUTH`. Such a case fails when the helper is missing, and `T-selection` fails `expectBrowserLeg: false` for a transport that describes a browser leg. A transport that nests handlers needs `graphqlContexts` to list `'static'` and `'cached'` for `T-stale-context`, whose cached-context row runs where the browser leg follows each operation; the custom-context rows of `T-subscription-origin` run for the listed `'fresh'` and `'plain-request'` contexts. The `T-subscription-*` cases run when `invocationShapes` includes `'subscriptions'` and need `invokeConnection` with its `connectionParams` option. `T-inherited-handler` runs with `invokeInherited`; harnesses of transports that address a handler by a name one inherited method shares across subclasses (an RPC pattern, a GraphQL root field) omit it, and the case is skipped with the reason. A case whose optional capability is absent is skipped with the reason. The kits read these members from unit objects before running; for classes and `defineExtension()` definitions they boot once and read the resolved unit. A case known to be inapplicable registers with its reason in the test name. A case that finds out at run time resolves with `{ skipped: reason }`, which `runConformance` reports through the runner's `context.skip(reason)` (Vitest, `node:test`; Jest has no runtime skip, so the test passes).
951
+ Applicability comes from the unit, not from the options a harness passes. A platform that declares `capabilities.http2` needs the `http2` option. The platform kit's trusted-hop rows need `trustOneProxy`, which trusts one forwarding hop on a created application (Express `app.set("trust proxy", 1)`), or `createTrustingHttpAdapter` (and `http2.createTrustingHttpAdapter` for their HTTP/2 rows), which returns a fresh adapter that already trusts it, for platforms that fix proxy trust at construction (Fastify `new FastifyAdapter({ trustProxy: "loopback" })`; the kit's requests come from loopback). Its microservice rows of `H-no-adapter` need `microservice`, which returns the transport options to boot with (for example `{ transport: Transport.TCP, options: { host: "127.0.0.1", port: 0 } }`); the kit does not load the optional `@nestjs/microservices` peer itself. The production-mode rows set `process.env.NODE_ENV` to `production` while they run and restore it afterwards, so run the kit's cases sequentially. A transport with a lineage needs `invokeTwice`, one that nests handlers (`defaultAccessFor`) needs `invokeGraph`, and one that claims the kit's federation reference resolver needs `invokeEntities`. A transport whose browser leg is a connection's handshake, shared by the connection's messages (the WebSocket transports), runs the `T-ws-origin-*` cases instead of the `T-csrf-*` cases and needs `invokeConnection`, plus `authenticateConnection` when the app provides `WS_CONNECTION_AUTH`. Such a case fails when the helper is missing, and `T-selection` fails `expectBrowserLeg: false` for a transport that describes a browser leg. A transport that nests handlers needs `graphqlContexts` to list `'static'` and `'cached'` for `T-stale-context`, whose cached-context row runs where the browser leg follows each operation; the custom-context rows of `T-subscription-origin` run for the listed `'fresh'` and `'plain-request'` contexts. The `T-subscription-*` cases run when `invocationShapes` includes `'subscriptions'` and need `invokeConnection` with its `connectionParams` option. `T-inherited-handler` runs with `invokeInherited`; harnesses of transports that address a handler by a name one inherited method shares across subclasses (an RPC pattern, a GraphQL root field) omit it, and the case is skipped with the reason. A case whose optional capability is absent is skipped with the reason. The kits read these members from unit objects before running; for classes and `defineExtension()` definitions they boot once and read the resolved unit. A case known to be inapplicable registers with its reason in the test name. A case that finds out at run time resolves with `{ skipped: reason }`, which `runConformance` reports through the runner's `context.skip(reason)` (Vitest, `node:test`; Jest has no runtime skip, so the test passes).
952
952
 
953
953
  ```ts
954
954
  import { ExpressAdapter } from "@nestjs/platform-express";
@@ -994,9 +994,23 @@ The principal-source kit passes each credential factory the instance the case ru
994
994
 
995
995
  Every case boots real Nest applications or modules with a real Better Auth instance that includes `conformanceProbePlugin()`. The probe counts and faults every operation of the instance's database adapter, so storage outages reach endpoints that would swallow them, and records the `Set-Cookie` lines each call produces. `createConformanceAuth({ plugins })` builds such an instance: memory adapter, `testUtils()`, `bearer()`, the probe plugin, `nestjs()` last, and `session.updateAge: 0` and origin checks on (`advanced.disableOriginCheck: false` and `advanced.disableCSRFCheck: false`), whatever `options` says. The principal-source and policy kits take that instance as `auth`; the principal-source kit rejects an instance without `session.updateAge: 0`. The transport kit creates its own instance, with the organization plugin and the organizations its `org` fixture names, and passes it to your `createApp` harness together with the kit's API-key source in `principals`, a `logger` to boot with, and the `globalScope`, `appEnhancers`, `forwardDirectCalls`, `fieldResolverEnhancers`, `fieldResolverCoverage`, `federation` and `graphqlContext` settings of each boot. Apply every fixture decorator, including the kit's `SetMetadata()` marker, which maps the transport's claims to fixtures, and the class decorators of `inherited.subclasses` and `graph.classDecorators`. On a connection-shaped browser leg, the kit's cookie credential carries the instance's base URL, `http://localhost:3000`, as `Origin`, as a browser page of that origin does; the `T-ws-origin-*` cases pass their own `Origin`, `Host` and forwarding headers, which the harness sends in the handshake.
996
996
 
997
- The policy kit judges principals through the real evaluator, and cases that need route planning, acceptance or principal resolution send in-process requests through the real `BetterAuthGuard`. Pass the sources of the requirement's other principal kinds in `sources` (for example `apiKeyPrincipal()`). Unit-specific cases run when the requirement contains the built-in admin policy (`Z-admin-*`), an organization policy (`Z-org-ref-types` and the organization row of `Z-apikey-quota-per-request`) or the API-key permission policy (its `Z-apikey-quota-per-request` row), and skip with the reason otherwise. They use the instance's configuration where it matters: `session.cookieCache.enabled` for the warm-cache rows and `apiKey({ enableSessionForAPIKeys: true })` for the API-key session and quota rows. The kit builds the instances that need another configuration (`customSession` role shapes, `adminUserIds`, a dynamic `baseURL`) with `createConformanceAuth()`.
997
+ The policy kit judges principals through the real evaluator, and cases that need route planning, acceptance or principal resolution send in-process requests through the real `BetterAuthGuard`. Pass the sources of the requirement's other principal kinds in `sources` (for example `apiKeyPrincipal()`). Unit-specific cases run when the requirement contains the built-in admin policy (`Z-admin-*`), an organization policy (`Z-org-ref-types` and the organization row of `Z-apikey-quota-per-request`) or the API-key permission policy (its `Z-apikey-quota-per-request` row), and skip with the reason otherwise. They use the instance's configuration where it matters: `session.cookieCache.enabled` for the warm-cache rows and `apiKey({ enableSessionForAPIKeys: true })` for the API-key session and quota rows. The kit builds the instances that need another configuration (`customSession` role shapes, `adminUserIds`, a dynamic `baseURL`, an instance without `apiKey()`) with `variant(overrides)`, which defaults to `createConformanceAuth(overrides)`. A storage job passes a variant that builds the same instance on its own database: add what `createConformanceAuth()` adds (`testUtils()`, `bearer()`, `conformanceProbePlugin()`, `nestjs()` last, `session.updateAge: 0` and `advanced.disableOriginCheck: false`), and migrate the schema before returning it.
998
998
 
999
- This repository runs the platform kit against Express and Fastify, including the Fastify HTTP/2 adapter for the HTTP/2 rows; the transport kit against the built-in HTTP transport, an in-process reference transport, Socket.IO and `ws` (each with and without a principal TTL), the RPC transport over TCP (standalone and hybrid), gRPC, NATS, RabbitMQ, MQTT, Redis and Kafka, and the Apollo (Express and Fastify) and Mercurius transports over HTTP and over graphql-ws (with and without a subscription principal TTL; Mercurius also without `mercuriusSubscriptionContext()`), with federation 2 subgraphs and a federation 1 run of `T-reference-resolver`; the principal-source kit against the session source (its `S-bridge-*` rows on Express and Fastify), the API-key source with user-owned and organization-owned keys, and the design's JWT source sketch with derived and explicit claims; and the policy kit against the admin, organization (also with `customSession`), API-key and fresh-session policies. gRPC skips `T-error-shape`, because it delivers only a status and a message. The kit reads each call's browser leg while its invocation runs, because the GraphQL transports reject a completed request. Mutation tests confirm that each of these defects fails the intended case: a platform merging `Set-Cookie`, handing Better Auth no client IP, ignoring its proxy trust, dropping the request host or stripping forwarding headers, a kernel that unbinds hooks on `close()`, reports taking over a closed binding or keeps a microservice's binding open, a transport sharing invocation state across aliases, a transport without a browser leg on safe operations, a `defineExtension()` transport with a lineage or nesting and no helper, a WebSocket transport that enforces its handshake like a safe read, exempts handshakes that carry a token, builds its leg URL from the bare handshake path or from forwarded headers, or answers internal errors with their cause, a GraphQL transport whose aliases share one invocation, whose lineage gives every field of an operation one position, whose field resolvers do not inherit their root's access, or that serves reference resolvers without field guards, a GraphQL harness that omits `invokeGraph`, `invokeEntities` or `invokeConnection`, a transport that claims every context type, runs no metadata canary, claims classes without handlers or drops the `Host` header without declaring hostless calls, a planner that caches plans by handler function, ignores `@SkipOriginCheck()` or skips the origin check of safe operations, an HTTP transport whose invocation is not the handler's request (Fastify), a WebSocket transport whose messages on one socket share their invocation, a GraphQL transport that treats a completed request as live, ignores `connectionParams` credentials, treats socket operations as safe reads, exempts `connectionParams` tokens, serves inherited forwarding fields without field guards, omits `W_FIELD_RESOLVER_INHERITS` or `I_CLASS_METADATA_OPERATIONS_ONLY`, a planner that applies class-level acceptance to field resolvers or records a reading on a form-mode nested public field, a source throwing its denial, a source dropping its refresh cookie, a source turning a storage outage into a 401, API-key sources that report every failure as an invalid key, verify once per call, run without the outage probe or latency signal, probe on every invalid key, touch key rows, or map organization keys to users, a session source that trusts hook answers on authoritative routes, a source that wraps errors without the request's credentials, JWT sources that expect the basePath URL as issuer or deny a key-set outage, sources that drop the host or forwarded headers, a route planner that forwards every handler's direct calls, a `forwardForeignCookies()` without a declaration check or without forwarding, a policy widening delegated principals, a policy turning a lost session or a storage outage into a denial, an organization policy coercing non-string inputs or repeating its Better Auth call, admin policies that admit API keys by default, lack fresh identity or hostless calls, ignore a key owner's ban, judge the session's role or trust the stored user row, and boot validation or route planning that ignore request-scoped dependencies or a class policy's `requires` field.
999
+ `deliveries` also sends the transport-specific rows through real transports. A `PolicyDelivery` has these members:
1000
+
1001
+ - `name` names the delivery in case titles.
1002
+ - `createApp(handlers, auth, { principals, logger })` boots an app with `globalGuard` that exposes each kit handler (a `FixtureHandler`, as in `transportConformance`) under its name: a gateway message with explicit coverage, a message pattern or a GraphQL query field. The kit's only named input, `orgId`, keeps its JSON type; a GraphQL harness declares it as a JSON scalar argument.
1003
+ - `invoke(app, handler, headers, input)` sends one logical request.
1004
+ - Connection transports add `connect(app, headers)`, which opens one connection whose `invoke()` sends one message, or one subscription operation for GraphQL, after the previous one answered. They also add `connectionPrincipalTtlMs`, the transport's `principalTtlMs` or `subscriptionPrincipalTtlMs`.
1005
+
1006
+ The kit sends the probe plugin's trusted `Origin` with every delivered request. For each delivery it adds these cases, titled `<name>: …`:
1007
+
1008
+ - `Z-admin-rejects-api-key-session`
1009
+ - `Z-org-ref-types`, with junk `orgId` values in the transport's payload
1010
+ - `Z-apikey-quota-per-request`, the API-key session setup
1011
+ - `Z-admin-banned` on one connection. It runs when `connect` and a positive `connectionPrincipalTtlMs` are present, and first checks that a default-freshness handler reuses the connection's principal after sign-out.
1012
+
1013
+ This repository runs the platform kit against Express and Fastify, including the Fastify HTTP/2 adapter for the HTTP/2 rows; the transport kit against the built-in HTTP transport, an in-process reference transport, Socket.IO and `ws` (each with and without a principal TTL), the RPC transport over TCP (standalone and hybrid), gRPC, NATS, RabbitMQ, MQTT, Redis and Kafka, and the Apollo (Express and Fastify) and Mercurius transports over HTTP and over graphql-ws (with and without a subscription principal TTL; Mercurius also without `mercuriusSubscriptionContext()`), with federation 2 subgraphs and a federation 1 run of `T-reference-resolver`; the principal-source kit against the session source (its `S-bridge-*` rows on Express and Fastify), the API-key source with user-owned and organization-owned keys, and the design's JWT source sketch with derived and explicit claims; and the policy kit against the admin, organization (also with `customSession`), API-key and fresh-session policies. The policy kit's delivery rows run over Socket.IO and `ws` with a 300 s principal TTL, over RPC over TCP (standalone and hybrid), and over the Apollo (Express and Fastify) and Mercurius transports over HTTP and over graphql-ws with a 300 s subscription principal TTL. `Z-admin-null-role` also runs on PGlite with Better Auth's Kysely adapter and migrations. gRPC skips `T-error-shape`, because it delivers only a status and a message. The kit reads each call's browser leg while its invocation runs, because the GraphQL transports reject a completed request. Mutation tests confirm that each of these defects fails the intended case: a platform merging `Set-Cookie`, handing Better Auth no client IP, ignoring its proxy trust, dropping the request host or stripping forwarding headers, a kernel that unbinds hooks on `close()`, reports taking over a closed binding or keeps a microservice's binding open, a transport sharing invocation state across aliases, a transport without a browser leg on safe operations, a `defineExtension()` transport with a lineage or nesting and no helper, a WebSocket transport that enforces its handshake like a safe read, exempts handshakes that carry a token, builds its leg URL from the bare handshake path or from forwarded headers, or answers internal errors with their cause, a GraphQL transport whose aliases share one invocation, whose lineage gives every field of an operation one position, whose field resolvers do not inherit their root's access, or that serves reference resolvers without field guards, a GraphQL harness that omits `invokeGraph`, `invokeEntities` or `invokeConnection`, a transport that claims every context type, runs no metadata canary, claims classes without handlers or drops the `Host` header without declaring hostless calls, a planner that caches plans by handler function, ignores `@SkipOriginCheck()` or skips the origin check of safe operations, an HTTP transport whose invocation is not the handler's request (Fastify), a WebSocket transport whose messages on one socket share their invocation, a GraphQL transport that treats a completed request as live, ignores `connectionParams` credentials, treats socket operations as safe reads, exempts `connectionParams` tokens, serves inherited forwarding fields without field guards, omits `W_FIELD_RESOLVER_INHERITS` or `I_CLASS_METADATA_OPERATIONS_ONLY`, a planner that applies class-level acceptance to field resolvers or records a reading on a form-mode nested public field, a source throwing its denial, a source dropping its refresh cookie, a source turning a storage outage into a 401, API-key sources that report every failure as an invalid key, verify once per call, run without the outage probe or latency signal, probe on every invalid key, touch key rows, or map organization keys to users, a session source that trusts hook answers on authoritative routes, a source that wraps errors without the request's credentials, JWT sources that expect the basePath URL as issuer or deny a key-set outage, sources that drop the host or forwarded headers, a route planner that forwards every handler's direct calls, a `forwardForeignCookies()` without a declaration check or without forwarding, a policy widening delegated principals, a policy turning a lost session or a storage outage into a denial, an organization policy coercing non-string inputs (also over `ws`) or repeating its Better Auth call, admin policies that admit API keys by default, lack fresh identity (also over RPC) or hostless calls, ignore a key owner's ban, judge the session's role or trust the stored user row (also on PGlite), a request scope that reuses a connection's principal for authoritative handlers (Socket.IO, graphql-ws), a WebSocket transport that declares a principal TTL it does not apply, a graphql-ws delivery that opens a new socket for each operation, and boot validation or route planning that ignore request-scoped dependencies or a class policy's `requires` field.
1000
1014
 
1001
1015
  ## Develop in this monorepo
1002
1016