nestjs-slightly-better-auth 1.3.0 → 1.4.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.
Files changed (58) hide show
  1. package/README.md +113 -3
  2. package/dist/admin.cjs +1 -1
  3. package/dist/admin.mjs +1 -1
  4. package/dist/api-key.cjs +7 -7
  5. package/dist/api-key.cjs.map +1 -1
  6. package/dist/api-key.mjs +2 -2
  7. package/dist/{auth-decorators-CJUQDwAQ.cjs → auth-decorators-B7xasVk_.cjs} +5 -134
  8. package/dist/auth-decorators-B7xasVk_.cjs.map +1 -0
  9. package/dist/{auth-decorators-BmUNYTFd.mjs → auth-decorators-DUmQ-n_w.mjs} +8 -125
  10. package/dist/auth-decorators-DUmQ-n_w.mjs.map +1 -0
  11. package/dist/auth-guard-CdV9G47g.cjs +136 -0
  12. package/dist/auth-guard-CdV9G47g.cjs.map +1 -0
  13. package/dist/auth-guard-D4tzhp9u.mjs +125 -0
  14. package/dist/auth-guard-D4tzhp9u.mjs.map +1 -0
  15. package/dist/{auth-module-boCEWXr1.cjs → auth-module-CbGPeq4K.cjs} +24 -22
  16. package/dist/{auth-module-boCEWXr1.cjs.map → auth-module-CbGPeq4K.cjs.map} +1 -1
  17. package/dist/{auth-module-DXSvwVXS.mjs → auth-module-CfCGtasi.mjs} +5 -3
  18. package/dist/{auth-module-DXSvwVXS.mjs.map → auth-module-CfCGtasi.mjs.map} +1 -1
  19. package/dist/{principal-readings-CpF4q6b-.cjs → auth-scope-interceptor-DU57SvDf.cjs} +1 -206
  20. package/dist/auth-scope-interceptor-DU57SvDf.cjs.map +1 -0
  21. package/dist/{principal-readings-BKeyOxwJ.mjs → auth-scope-interceptor-ctnmljij.mjs} +3 -190
  22. package/dist/auth-scope-interceptor-ctnmljij.mjs.map +1 -0
  23. package/dist/express.cjs +12 -3
  24. package/dist/express.cjs.map +1 -1
  25. package/dist/express.d.cts.map +1 -1
  26. package/dist/express.d.mts.map +1 -1
  27. package/dist/express.mjs +12 -3
  28. package/dist/express.mjs.map +1 -1
  29. package/dist/graphql.cjs +447 -0
  30. package/dist/graphql.cjs.map +1 -0
  31. package/dist/graphql.d.cts +38 -0
  32. package/dist/graphql.d.cts.map +1 -0
  33. package/dist/graphql.d.mts +38 -0
  34. package/dist/graphql.d.mts.map +1 -0
  35. package/dist/graphql.mjs +441 -0
  36. package/dist/graphql.mjs.map +1 -0
  37. package/dist/index.cjs +10 -9
  38. package/dist/index.mjs +4 -3
  39. package/dist/organization.cjs +1 -1
  40. package/dist/organization.mjs +1 -1
  41. package/dist/principal-readings-K8zeZbEl.cjs +208 -0
  42. package/dist/principal-readings-K8zeZbEl.cjs.map +1 -0
  43. package/dist/principal-readings-O66hlCdW.mjs +191 -0
  44. package/dist/principal-readings-O66hlCdW.mjs.map +1 -0
  45. package/dist/testing/conformance.cjs +11 -10
  46. package/dist/testing/conformance.cjs.map +1 -1
  47. package/dist/testing/conformance.mjs +4 -3
  48. package/dist/testing/conformance.mjs.map +1 -1
  49. package/dist/testing.cjs +10 -9
  50. package/dist/testing.cjs.map +1 -1
  51. package/dist/testing.mjs +2 -1
  52. package/dist/testing.mjs.map +1 -1
  53. package/dist/websockets.d.mts +1 -1
  54. package/package.json +39 -1
  55. package/dist/auth-decorators-BmUNYTFd.mjs.map +0 -1
  56. package/dist/auth-decorators-CJUQDwAQ.cjs.map +0 -1
  57. package/dist/principal-readings-BKeyOxwJ.mjs.map +0 -1
  58. package/dist/principal-readings-CpF4q6b-.cjs.map +0 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  A NestJS integration for [Better Auth](https://www.better-auth.com), built on an independently reviewed specification.
8
8
 
9
- > **Scope.** The package covers HTTP, WebSocket and RPC authentication. HTTP support comprises the Nest authentication kernel, the Better Auth construction plugin, the Express and Fastify platforms, and the admin, organization and API-key authorization units. The `./websockets` entry authenticates Socket.IO and raw `ws` gateways, and the `./microservices` entry authenticates Nest microservice messages. The `./testing` entry provides consumer testing helpers and `./testing/conformance` reusable conformance kits. The GraphQL transport is not included; it arrives in a later minor version behind its own entry point. The [changelog](CHANGELOG.md) records the version that introduces each entry point.
9
+ > **Scope.** The package covers HTTP, GraphQL, WebSocket and RPC authentication: the Nest authentication kernel, the Better Auth construction plugin, the Express and Fastify platforms, the admin, organization and API-key authorization units, the Apollo and Mercurius transports of the `./graphql` entry, the Socket.IO and raw `ws` gateway transports of the `./websockets` entry and the Nest microservice transport of the `./microservices` entry. The `./testing` entry provides consumer testing helpers and `./testing/conformance` reusable conformance kits. The [changelog](CHANGELOG.md) records the version that introduces each entry point.
10
10
 
11
11
  ## Install
12
12
 
@@ -16,6 +16,15 @@ pnpm add nestjs-slightly-better-auth better-auth
16
16
 
17
17
  Peer dependencies: `@nestjs/common` and `@nestjs/core` 12, `better-auth` 1.7.5 or newer, `reflect-metadata` and `rxjs`. Node.js 24.11 or newer. The `./testing` and `./testing/conformance` entries also need the optional peer `@nestjs/testing` 12; the root entry never loads it or a test runner.
18
18
 
19
+ The `./graphql` entry has two optional peer dependencies, `@nestjs/graphql` ^14.0.2 and `graphql` ^16.14.2, plus the Nest driver of your GraphQL server:
20
+
21
+ ```sh
22
+ # Apollo on Express
23
+ pnpm add @nestjs/graphql graphql @nestjs/apollo @apollo/server @as-integrations/express5
24
+ # Mercurius on Fastify
25
+ pnpm add @nestjs/graphql graphql @nestjs/mercurius mercurius @nestjs/platform-fastify
26
+ ```
27
+
19
28
  The `./websockets` entry also needs the optional peer `@nestjs/websockets` 12 and the Nest adapter for your socket library:
20
29
 
21
30
  ```sh
@@ -41,12 +50,109 @@ The `./express` and `./fastify` entries connect native Nest applications to Bett
41
50
 
42
51
  Express preserves native controller parsing for unconditional routes that overlap the auth mount. Body-capable controller routes that also depend on host or non-URI version conditions are rejected at startup with `CONDITIONAL_ROUTE_SHADOW`; move them outside the auth mount. The integration checks the effective Express router flags because changing application settings after router creation does not change existing route matching. This is a tested Express 5 compatibility boundary, not an inspection of the router stack.
43
52
 
44
- Fastify does not expose its configured `trustProxy` value through a public inspection API. The boot summary therefore reports proxy trust as `unknown`, and diagnostics that depend on the setting are unavailable. Client IP resolution still uses the native Fastify request. Configure proxy trust on your Nest Fastify adapter and verify it against your deployment topology.
53
+ The Express platform recognizes a request by Node object identity: an `http.IncomingMessage` or stream request whose `res` is the `http.ServerResponse` bound to it. Objects built from request data never qualify. Requests dispatched with light-my-request's `inject(app.getHttpAdapter().getInstance(), ...)` are supported. `inject()` re-parents Express's shared request and response prototypes for the whole process, and a listening Express server in the same process then fails inside light-my-request, so keep injected and listening tests in separate test files.
45
54
 
46
- The GraphQL integration has separate implementation and end-to-end acceptance gates. It will use an optional entry point so applications install only the transports they use.
55
+ Fastify does not expose its configured `trustProxy` value through a public inspection API. The boot summary therefore reports proxy trust as `unknown`, and diagnostics that depend on the setting are unavailable. Client IP resolution still uses the native Fastify request. Configure proxy trust on your Nest Fastify adapter and verify it against your deployment topology.
47
56
 
48
57
  Start with the [design workspace](docs/design/README.md), [reviewed specification](docs/design/design-v7.md), [review ledger](docs/design/ledger.md) and [implementation plan](../../docs/superpowers/plans/2026-09-21-better-auth.md). Independent Better Auth, NestJS and security reviews approved the final v7 snapshot after resolving the round-6 and round-7 findings. The remaining entry points are tracked in [issue #534](https://github.com/thilllon/nestjs-kit/issues/534).
49
58
 
59
+ ## GraphQL
60
+
61
+ The `./graphql` entry provides `apolloTransport()` for `@nestjs/apollo` and `mercuriusTransport()` for `@nestjs/mercurius`. Queries, mutations, subscriptions and federation reference resolvers use the same access decorators, principal parameters and authorization units as controllers. Only this entry imports `@nestjs/graphql` and `graphql`.
62
+
63
+ Create the Better Auth instance with the construction plugin. The example stores data in PostgreSQL through `pg`, which the commands above do not install; add it with `pnpm add pg` or pass another [Better Auth database](https://www.better-auth.com/docs/concepts/database):
64
+
65
+ ```ts
66
+ // auth.ts
67
+ import { betterAuth } from "better-auth";
68
+ import { nestjs } from "nestjs-slightly-better-auth/plugin";
69
+ import { Pool } from "pg";
70
+
71
+ export const auth = betterAuth({
72
+ database: new Pool({ connectionString: process.env.DATABASE_URL }),
73
+ emailAndPassword: { enabled: true },
74
+ plugins: [nestjs()],
75
+ });
76
+ ```
77
+
78
+ Register the GraphQL module and the transport that matches its driver:
79
+
80
+ ```ts
81
+ // app.module.ts
82
+ import { ApolloDriver, type ApolloDriverConfig } from "@nestjs/apollo";
83
+ import { Module } from "@nestjs/common";
84
+ import { GraphQLModule, Query, Resolver } from "@nestjs/graphql";
85
+ import {
86
+ type AuthPrincipal,
87
+ BetterAuthModule,
88
+ CurrentPrincipal,
89
+ Public,
90
+ } from "nestjs-slightly-better-auth";
91
+ import { expressPlatform } from "nestjs-slightly-better-auth/express";
92
+ import { apolloTransport } from "nestjs-slightly-better-auth/graphql";
93
+ import { auth } from "./auth";
94
+
95
+ @Resolver()
96
+ export class ViewerResolver {
97
+ @Query(() => String)
98
+ viewer(@CurrentPrincipal() principal: AuthPrincipal) {
99
+ return principal.userId;
100
+ }
101
+
102
+ @Query(() => String)
103
+ @Public()
104
+ health() {
105
+ return "ok";
106
+ }
107
+ }
108
+
109
+ @Module({
110
+ imports: [
111
+ GraphQLModule.forRoot<ApolloDriverConfig>({
112
+ driver: ApolloDriver,
113
+ autoSchemaFile: true,
114
+ subscriptions: { "graphql-ws": true },
115
+ fieldResolverEnhancers: ["guards", "filters"],
116
+ }),
117
+ BetterAuthModule.forRoot({
118
+ auth,
119
+ platforms: [expressPlatform()],
120
+ transports: [apolloTransport()],
121
+ }),
122
+ ],
123
+ providers: [ViewerResolver],
124
+ })
125
+ export class AppModule {}
126
+ ```
127
+
128
+ For Mercurius, use `MercuriusDriver`, `fastifyPlatform()` from `nestjs-slightly-better-auth/fastify` and `mercuriusTransport()`. `mercuriusSubscriptionContext()` returns a Mercurius `subscription.context` function that keeps the upgrade request and its original browser headers with every socket operation; the socket tests cover Mercurius with and without it. A custom GraphQL `context` must be a function that returns a fresh object for every operation. For Apollo HTTP operations, it keeps the platform request at `context.req`: the Express request, or the Fastify request that the context function receives as its first argument, not that request's `raw` `IncomingMessage`. For Apollo socket operations, it either leaves `context.req` unset, so Nest assigns the graphql-ws context, or keeps graphql-ws's `extra` at `context.extra` and the connection parameters at `context.connectionParams`; the upgrade request alone is not recognized. An operation whose context carries neither fails closed with `AUTH_MISCONFIGURED` (`GRAPHQL_CONTEXT_UNRECOGNIZED`). Startup fails with `GRAPHQL_STATIC_CONTEXT` for a static context object and with `GRAPHQL_DRIVER_MISMATCH` when the transport does not match the configured driver.
129
+
130
+ ### Operations and fields
131
+
132
+ Each root field of an operation is authorized independently, so aliases and batched operations can share one session read while receiving different organization decisions. A denied field produces a GraphQL error whose `extensions` contain `code`, `statusCode` and, where applicable, `reason`. Authentication infrastructure failures surface as `Internal server error` with `code: "INTERNAL_SERVER_ERROR"` and `reason` `AUTH_UNAVAILABLE` or `AUTH_MISCONFIGURED`; the underlying error never reaches the client. Better Auth's browser-origin check applies to HTTP mutations and to every socket operation.
133
+
134
+ Field resolvers without access decorators inherit the principal of their actual ancestor operation, including across named instances. Protecting field and reference resolvers requires Nest to run guards on them: set `fieldResolverEnhancers: ["guards", "filters"]`, and add `"interceptors"` when those fields use scoped service readers. With `"guards"` enabled, Nest runs every global guard once per field invocation, and the boot advice `W_FIELD_GUARDS_MULTIPLY_GLOBAL_ENHANCERS` names the affected guards and interceptors. A field resolver that declares access metadata without field guards fails startup with `FIELD_RESOLVER_UNGUARDED`; `fieldResolverCoverage: "warn" | "off"` relaxes that check. Without `"filters"`, Nest does not route field errors through its exception pipeline, so they are not logged; the library then emits `W_FIELD_EXCEPTION_FILTERS_DISABLED`. With filters enabled, repeated infrastructure and reader errors produce one Nest ERROR per logical request while every affected field receives a safe error.
135
+
136
+ ### Subscriptions and socket operations
137
+
138
+ Socket operations authenticate each operation independently from the WebSocket upgrade request and never refresh the session. Browser-origin checks always use the original upgrade headers, before any session read, so connection parameters cannot replace the browser's `Origin`. The platform's own request predicate classifies HTTP operations first, and Apollo recognizes a socket operation only through the graphql-ws connection and its WebSocket. A client-sent `Upgrade: websocket` header therefore never selects the socket path: an HTTP operation keeps the platform client IP, response cookie forwarding and the completed-request check, and an unrecognized context still fails closed. Mercurius treats an operation whose context carries the route's own Fastify reply as HTTP before any socket detection, and otherwise identifies sockets only through its subscription context, so neither request headers nor client fields spread into a custom context select the socket path.
139
+
140
+ By default, `authorization` and `cookie` values in the `connection_init` payload replace the matching upgrade headers. `connectionParamHeaders` replaces that list of keys, matched case-insensitively. A selected value that is not a string or is not a valid header value rejects the operation with `UNAUTHENTICATED` and `reason: "MALFORMED_CREDENTIALS"`, without falling back to another credential and without echoing the value in errors or logs.
141
+
142
+ `subscriptionCredentials(connectionContext)` replaces this mapping. The `HeadersInit` it returns becomes the operation's credentials, and `host`, `x-forwarded-host` and `x-forwarded-proto` are copied from the upgrade request when absent. Returning `undefined` presents no credentials, and `connectionParamHeaders` is not consulted. Invalid header values reject with `MALFORMED_CREDENTIALS`; errors thrown by the function are application errors, not credential denials.
143
+
144
+ `subscriptionPrincipalTtlMs` reuses a connection's principal for that many milliseconds. The default `0` resolves the principal for every socket operation. A positive value delays the effect of session revocation on open connections by up to the TTL; routes with `@RequireAuth({ authoritative: true })` always resolve a fresh principal.
145
+
146
+ ### Federation
147
+
148
+ Both transports support `ApolloFederationDriver` and `MercuriusFederationDriver` with schema-first schemas and code-first federation versions 1 and 2. `@ResolveReference()` resolvers are always checked: without field guards, startup fails with `FEDERATION_FIELD_GUARDS_REQUIRED` or `REFERENCE_RESOLVER_UNGUARDED`, and `federationCoverage` relaxes the check.
149
+
150
+ Federation schema generation in `@nestjs/graphql` loads `@apollo/subgraph`, which the install commands above do not include. Federation applications must install a compatible version, for example `pnpm add @apollo/subgraph@^2.15.1`; otherwise startup fails before authentication runs. `@apollo/subgraph` 2.15 requires `@nestjs/graphql` 14.0.2 or newer, as described under [Supported versions](#supported-versions).
151
+
152
+ ### Supported versions
153
+
154
+ The native GraphQL tests run Nest 12.0.3 with `@nestjs/graphql`, `@nestjs/apollo` and `@nestjs/mercurius` 14.0.2, `graphql` 16.14.2, `@apollo/server` 5.5.1, `mercurius` 16.10.0, `@mercuriusjs/federation` 5.1.1 and `@apollo/subgraph` 2.15.1. Code-first federation with `@apollo/subgraph` 2.15 requires `@nestjs/graphql` 14.0.2 or newer. With `@nestjs/graphql` 14.0.1, schema generation fails before authentication runs: federation 1 cannot load the subgraph directives module that 2.15 removed, and federation 2 fails with `TypeError: doc.definitions is not iterable`. The `@nestjs/graphql` peer range therefore starts at 14.0.2.
155
+
50
156
  ## WebSocket gateways
51
157
 
52
158
  The `./websockets` entry authenticates Socket.IO and raw `ws` gateways. Register `socketIoTransport()` or `wsTransport()` in the module's `transports`, and decorate every gateway with `@UseBetterAuth()` so both the guard and the invocation scope run for its message handlers. Startup fails with `GATEWAY_UNGUARDED` when a message handler lacks that coverage; `@Public()` opts a gateway or handler out, and the transports' `gatewayCoverage` option downgrades the check to `"warn"` or `"off"`. Message handlers use the same access, session and authorization decorators as controllers.
@@ -299,6 +405,10 @@ The shared toolchain uses Node.js LTS, pnpm, tsdown, TypeScript and Vitest. Buil
299
405
 
300
406
  [Contributing](../../CONTRIBUTING.md) explains repository checks and PRs. See [package instructions](AGENTS.md) and [import provenance](docs/provenance.md) before working with the historical material.
301
407
 
408
+ ## Examples
409
+
410
+ The [Express](../../examples/better-auth-express/README.md) and [Fastify](../../examples/better-auth-fastify/README.md) example applications register a default and a named instance, guard routes and read the signed-in user through a principal parameter. They start without external services.
411
+
302
412
  ## Releases
303
413
 
304
414
  Versions come from Changesets, and publication runs from the monorepo's release workflow with npm trusted publishing and provenance; see [release automation](../../docs/releases.md). New entry points are additive, so they ship as minor versions.
package/dist/admin.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_auth_errors = require("./auth-errors-1EdQ36D8.cjs");
3
- const require_auth_decorators = require("./auth-decorators-CJUQDwAQ.cjs");
3
+ const require_auth_decorators = require("./auth-decorators-B7xasVk_.cjs");
4
4
  let better_auth_api = require("better-auth/api");
5
5
  //#region src/admin.ts
6
6
  const adminPermissionPolicy = {
package/dist/admin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as BetterAuthConfigurationError } from "./auth-errors-B6jLBpQu.mjs";
2
- import { u as Require } from "./auth-decorators-BmUNYTFd.mjs";
2
+ import { u as Require } from "./auth-decorators-DUmQ-n_w.mjs";
3
3
  import { isAPIError } from "better-auth/api";
4
4
  //#region src/admin.ts
5
5
  const adminPermissionPolicy = {
package/dist/api-key.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_auth_errors = require("./auth-errors-1EdQ36D8.cjs");
3
- const require_principal_readings = require("./principal-readings-CpF4q6b-.cjs");
4
- const require_auth_decorators = require("./auth-decorators-CJUQDwAQ.cjs");
3
+ const require_auth_scope_interceptor = require("./auth-scope-interceptor-DU57SvDf.cjs");
4
+ const require_auth_decorators = require("./auth-decorators-B7xasVk_.cjs");
5
5
  let better_auth_plugins_access = require("better-auth/plugins/access");
6
6
  //#region src/api-key.ts
7
7
  const API_KEY_PRINCIPAL_KIND = "api-key";
@@ -56,7 +56,7 @@ function apiKeyPrincipal(options = {}) {
56
56
  requires: { plugins: ["api-key"] },
57
57
  appliesTo: (request) => request.headers.has(header),
58
58
  async resolve(request) {
59
- if (!request.headers.has(header)) return require_principal_readings.absent();
59
+ if (!request.headers.has(header)) return require_auth_scope_interceptor.absent();
60
60
  return request.memo(verificationMemo, async () => {
61
61
  const key = request.headers.get(header);
62
62
  const headers = new Headers();
@@ -88,7 +88,7 @@ function apiKeyPrincipal(options = {}) {
88
88
  const references = typeof options.references === "function" ? options.references({ configId }) : options.references ?? "user";
89
89
  const permissions = verified.permissions ?? null;
90
90
  const grant = (0, better_auth_plugins_access.role)(permissions ?? {});
91
- return require_principal_readings.authenticated({
91
+ return require_auth_scope_interceptor.authenticated({
92
92
  kind: API_KEY_PRINCIPAL_KIND,
93
93
  source: "better-auth:api-key",
94
94
  keyId: verified.id,
@@ -106,7 +106,7 @@ function apiKeyPrincipal(options = {}) {
106
106
  const code = result.error?.code;
107
107
  if (code === "RATE_LIMITED" || code === "USAGE_EXCEEDED") {
108
108
  const retry = result.error?.details?.tryAgainIn;
109
- return require_principal_readings.rejected(require_auth_errors.AuthFailures.rejected({
109
+ return require_auth_scope_interceptor.rejected(require_auth_errors.AuthFailures.rejected({
110
110
  status: 429,
111
111
  reason: code,
112
112
  ...code === "RATE_LIMITED" && typeof retry === "number" ? { retryAfterSeconds: retry / 1e3 } : {}
@@ -116,7 +116,7 @@ function apiKeyPrincipal(options = {}) {
116
116
  if (slowMs !== false && performance.now() - started > slowMs) throw new require_auth_errors.BetterAuthInfrastructureError(/* @__PURE__ */ new Error("API-key verification exceeded the outage threshold"));
117
117
  await probe(request.auth);
118
118
  }
119
- if (code === "INVALID_API_KEY" || code === "KEY_NOT_FOUND" || code === "KEY_DISABLED" || code === "KEY_EXPIRED") return require_principal_readings.rejected(require_auth_errors.AuthFailures.rejected({
119
+ if (code === "INVALID_API_KEY" || code === "KEY_NOT_FOUND" || code === "KEY_DISABLED" || code === "KEY_EXPIRED") return require_auth_scope_interceptor.rejected(require_auth_errors.AuthFailures.rejected({
120
120
  status: 401,
121
121
  reason: code
122
122
  }));
@@ -124,7 +124,7 @@ function apiKeyPrincipal(options = {}) {
124
124
  } catch (error) {
125
125
  const failure = require_auth_errors.normalizeThrown(error, "source", "better-auth:api-key", [key]);
126
126
  if ("sessionLoss" in failure) throw new require_auth_errors.BetterAuthInfrastructureError(error, { secrets: [key] });
127
- return require_principal_readings.rejected(failure);
127
+ return require_auth_scope_interceptor.rejected(failure);
128
128
  }
129
129
  });
130
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-key.cjs","names":["BetterAuthConfigurationError","absent","BetterAuthInfrastructureError","role","authenticated","rejected","AuthFailures","normalizeThrown","Require"],"sources":["../src/api-key.ts"],"sourcesContent":["import { role } from \"better-auth/plugins/access\";\nimport type {\n AuthHandle,\n AuthPrincipalBase,\n AuthorizationPolicy,\n PrincipalDelegation,\n PrincipalSource,\n Requirement,\n} from \"./auth-contracts.js\";\nimport { Require } from \"./auth-decorators.js\";\nimport {\n AuthFailures,\n BetterAuthConfigurationError,\n BetterAuthInfrastructureError,\n normalizeThrown,\n} from \"./auth-errors.js\";\nimport { absent, authenticated, rejected } from \"./principal-resolver.js\";\n\nexport const API_KEY_PRINCIPAL_KIND = \"api-key\" as const;\nexport interface ApiKeyPrincipal extends AuthPrincipalBase {\n readonly kind: \"api-key\";\n readonly keyId: string;\n readonly configId: string | null;\n readonly referenceId: string;\n readonly userId: string | null;\n readonly organizationId: string | null;\n readonly permissions: Readonly<Record<string, readonly string[]>> | null;\n readonly delegation: PrincipalDelegation;\n}\n// The public module identity survives declaration bundling in both output formats.\ndeclare module \"nestjs-slightly-better-auth\" {\n interface PrincipalKinds {\n \"api-key\": ApiKeyPrincipal;\n }\n}\nexport interface ApiKeyPrincipalOptions {\n header?: string;\n configId?: string;\n references?:\n | \"user\"\n | \"organization\"\n | ((key: { readonly configId: string | null }) => \"user\" | \"organization\");\n /**\n * Default true: slow INVALID_API_KEY results (>500 ms) throw infrastructure errors,\n * then fast failures share one read/no-op-write probe per second per instance.\n * false disables both checks; { slowMs: false } keeps only the probe.\n * Set slowMs below pool lock/statement timeouts and above custom hashing costs.\n * Fast failures confined to one row, or secondary storage without database fallback,\n * may still look like invalid keys. A statement-level trigger can observe the zero-row write.\n */\n outageProbe?: boolean | { readonly slowMs?: number | false };\n acceptance?: \"default\" | \"explicit\";\n}\ninterface VerifiedKey {\n id: string;\n configId?: string | null;\n referenceId: string;\n permissions?: Record<string, readonly string[]> | null;\n}\ninterface Verification {\n valid: boolean;\n key?: VerifiedKey | null;\n error?: { code?: string; details?: { tryAgainIn?: number } } | null;\n}\ninterface KeyApi {\n verifyApiKey(input: {\n body: { key: string; configId?: string };\n headers: Headers;\n }): Promise<Verification>;\n}\nexport function apiKeyPrincipal(\n options: ApiKeyPrincipalOptions = {},\n): PrincipalSource<ApiKeyPrincipal> {\n const header = options.header ?? \"x-api-key\";\n const slowMs =\n typeof options.outageProbe === \"object\"\n ? (options.outageProbe.slowMs ?? 500)\n : 500;\n if (\n !header.trim() ||\n (slowMs !== false && (!Number.isFinite(slowMs) || slowMs < 0))\n ) {\n throw new BetterAuthConfigurationError(\n \"INVALID_API_KEY_OPTIONS\",\n \"API-key header must be nonempty and slowMs must be finite and nonnegative.\",\n );\n }\n const verificationMemo = Symbol(\"api-key-verification\");\n const probes = new WeakMap<\n object,\n { at: number; pending: boolean; result: Promise<void> }\n >();\n async function probe(auth: AuthHandle): Promise<void> {\n const previous = probes.get(auth.instance);\n if (previous && (previous.pending || Date.now() - previous.at < 1000)) {\n return previous.result;\n }\n const result = (async () => {\n const context = await auth.context();\n // The logical key column is string-valued for default, UUID and serial IDs.\n const where = [{ field: \"key\", value: crypto.randomUUID() }];\n await context.adapter.findOne({ model: \"apikey\", where });\n await context.adapter.updateMany({\n model: \"apikey\",\n where,\n update: { updatedAt: new Date() },\n });\n })();\n const state = { at: Date.now(), pending: true, result };\n probes.set(auth.instance, state);\n void result.then(\n () => {\n state.pending = false;\n },\n () => {\n state.pending = false;\n },\n );\n return result;\n }\n return {\n id: \"better-auth:api-key\",\n kinds: [API_KEY_PRINCIPAL_KIND],\n acceptance: options.acceptance ?? \"explicit\",\n delegates: true,\n credentialHeaders: [header],\n effects: { consumesQuota: true, writes: true },\n requires: { plugins: [\"api-key\"] },\n appliesTo: (request) => request.headers.has(header),\n async resolve(request) {\n if (!request.headers.has(header)) {\n return absent();\n }\n return request.memo(verificationMemo, async () => {\n const key = request.headers.get(header)!;\n const headers = new Headers();\n for (const name of [\"host\", \"x-forwarded-host\", \"x-forwarded-proto\"]) {\n const value = request.headers.get(name);\n if (value !== null) {\n headers.set(name, value);\n }\n }\n const started = performance.now();\n try {\n const result = await request.auth.run(\n {\n cookies: request.cookies,\n inbound: () => request.headers,\n internal: true,\n },\n () =>\n (request.auth.api as unknown as KeyApi).verifyApiKey({\n body: {\n key,\n ...(options.configId === undefined\n ? {}\n : { configId: options.configId }),\n },\n headers,\n }),\n );\n if (result.valid) {\n const verified = result.key;\n if (\n !verified ||\n typeof verified.id !== \"string\" ||\n typeof verified.referenceId !== \"string\"\n ) {\n throw new BetterAuthInfrastructureError(\n new Error(\"Malformed API-key verification result\"),\n );\n }\n const configId = verified.configId ?? null;\n const references =\n typeof options.references === \"function\"\n ? options.references({ configId })\n : (options.references ?? \"user\");\n const permissions = verified.permissions ?? null;\n const grant = role(permissions ?? {});\n return authenticated({\n kind: API_KEY_PRINCIPAL_KIND,\n source: \"better-auth:api-key\",\n keyId: verified.id,\n configId,\n referenceId: verified.referenceId,\n userId: references === \"user\" ? verified.referenceId : null,\n organizationId:\n references === \"organization\" ? verified.referenceId : null,\n permissions,\n delegation: {\n description: \"api-key permissions\",\n allows: (requested) => grant.authorize(requested).success,\n },\n });\n }\n const code = result.error?.code;\n if (code === \"RATE_LIMITED\" || code === \"USAGE_EXCEEDED\") {\n const retry = result.error?.details?.tryAgainIn;\n return rejected(\n AuthFailures.rejected({\n status: 429,\n reason: code,\n ...(code === \"RATE_LIMITED\" && typeof retry === \"number\"\n ? { retryAfterSeconds: retry / 1000 }\n : {}),\n }),\n );\n }\n if (code === \"INVALID_API_KEY\" && options.outageProbe !== false) {\n if (slowMs !== false && performance.now() - started > slowMs) {\n throw new BetterAuthInfrastructureError(\n new Error(\"API-key verification exceeded the outage threshold\"),\n );\n }\n await probe(request.auth);\n }\n if (\n code === \"INVALID_API_KEY\" ||\n code === \"KEY_NOT_FOUND\" ||\n code === \"KEY_DISABLED\" ||\n code === \"KEY_EXPIRED\"\n ) {\n return rejected(\n AuthFailures.rejected({ status: 401, reason: code }),\n );\n }\n throw new BetterAuthInfrastructureError(\n new Error(\n `Unexpected API-key verification error: ${code ?? \"missing code\"}`,\n ),\n );\n } catch (error) {\n const failure = normalizeThrown(\n error,\n \"source\",\n \"better-auth:api-key\",\n [key],\n );\n if (\"sessionLoss\" in failure) {\n throw new BetterAuthInfrastructureError(error, { secrets: [key] });\n }\n return rejected(failure);\n }\n });\n },\n };\n}\nconst apiKeyPermissionPolicy: AuthorizationPolicy<\n Record<string, string[]>,\n ApiKeyPrincipal\n> = {\n id: \"better-auth:api-key/permission\",\n requires: { plugins: [\"api-key\"], principals: [API_KEY_PRINCIPAL_KIND] },\n validate(permissions) {\n if (\n !Object.keys(permissions).length ||\n Object.values(permissions).some(\n (actions) => !Array.isArray(actions) || !actions.length,\n )\n ) {\n throw new BetterAuthConfigurationError(\n \"EMPTY_PERMISSIONS\",\n \"API-key permissions must contain a resource and actions.\",\n );\n }\n },\n evaluate(permissions, context) {\n return context.principal.delegation.allows(permissions)\n ? { effect: \"allow\" }\n : { effect: \"deny\", reason: \"MISSING_PERMISSION\" };\n },\n};\nexport function apiKeyPermission(\n permissions: Record<string, string[]>,\n): Requirement<Record<string, string[]>> {\n return { policy: apiKeyPermissionPolicy, params: permissions };\n}\nexport function RequireApiKeyPermission(\n permissions: Record<string, string[]>,\n): ClassDecorator & MethodDecorator {\n return Require(apiKeyPermission(permissions));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"api-key.cjs","names":["BetterAuthConfigurationError","absent","BetterAuthInfrastructureError","role","authenticated","rejected","AuthFailures","normalizeThrown","Require"],"sources":["../src/api-key.ts"],"sourcesContent":["import { role } from \"better-auth/plugins/access\";\nimport type {\n AuthHandle,\n AuthPrincipalBase,\n AuthorizationPolicy,\n PrincipalDelegation,\n PrincipalSource,\n Requirement,\n} from \"./auth-contracts.js\";\nimport { Require } from \"./auth-decorators.js\";\nimport {\n AuthFailures,\n BetterAuthConfigurationError,\n BetterAuthInfrastructureError,\n normalizeThrown,\n} from \"./auth-errors.js\";\nimport { absent, authenticated, rejected } from \"./principal-resolver.js\";\n\nexport const API_KEY_PRINCIPAL_KIND = \"api-key\" as const;\nexport interface ApiKeyPrincipal extends AuthPrincipalBase {\n readonly kind: \"api-key\";\n readonly keyId: string;\n readonly configId: string | null;\n readonly referenceId: string;\n readonly userId: string | null;\n readonly organizationId: string | null;\n readonly permissions: Readonly<Record<string, readonly string[]>> | null;\n readonly delegation: PrincipalDelegation;\n}\n// The public module identity survives declaration bundling in both output formats.\ndeclare module \"nestjs-slightly-better-auth\" {\n interface PrincipalKinds {\n \"api-key\": ApiKeyPrincipal;\n }\n}\nexport interface ApiKeyPrincipalOptions {\n header?: string;\n configId?: string;\n references?:\n | \"user\"\n | \"organization\"\n | ((key: { readonly configId: string | null }) => \"user\" | \"organization\");\n /**\n * Default true: slow INVALID_API_KEY results (>500 ms) throw infrastructure errors,\n * then fast failures share one read/no-op-write probe per second per instance.\n * false disables both checks; { slowMs: false } keeps only the probe.\n * Set slowMs below pool lock/statement timeouts and above custom hashing costs.\n * Fast failures confined to one row, or secondary storage without database fallback,\n * may still look like invalid keys. A statement-level trigger can observe the zero-row write.\n */\n outageProbe?: boolean | { readonly slowMs?: number | false };\n acceptance?: \"default\" | \"explicit\";\n}\ninterface VerifiedKey {\n id: string;\n configId?: string | null;\n referenceId: string;\n permissions?: Record<string, readonly string[]> | null;\n}\ninterface Verification {\n valid: boolean;\n key?: VerifiedKey | null;\n error?: { code?: string; details?: { tryAgainIn?: number } } | null;\n}\ninterface KeyApi {\n verifyApiKey(input: {\n body: { key: string; configId?: string };\n headers: Headers;\n }): Promise<Verification>;\n}\nexport function apiKeyPrincipal(\n options: ApiKeyPrincipalOptions = {},\n): PrincipalSource<ApiKeyPrincipal> {\n const header = options.header ?? \"x-api-key\";\n const slowMs =\n typeof options.outageProbe === \"object\"\n ? (options.outageProbe.slowMs ?? 500)\n : 500;\n if (\n !header.trim() ||\n (slowMs !== false && (!Number.isFinite(slowMs) || slowMs < 0))\n ) {\n throw new BetterAuthConfigurationError(\n \"INVALID_API_KEY_OPTIONS\",\n \"API-key header must be nonempty and slowMs must be finite and nonnegative.\",\n );\n }\n const verificationMemo = Symbol(\"api-key-verification\");\n const probes = new WeakMap<\n object,\n { at: number; pending: boolean; result: Promise<void> }\n >();\n async function probe(auth: AuthHandle): Promise<void> {\n const previous = probes.get(auth.instance);\n if (previous && (previous.pending || Date.now() - previous.at < 1000)) {\n return previous.result;\n }\n const result = (async () => {\n const context = await auth.context();\n // The logical key column is string-valued for default, UUID and serial IDs.\n const where = [{ field: \"key\", value: crypto.randomUUID() }];\n await context.adapter.findOne({ model: \"apikey\", where });\n await context.adapter.updateMany({\n model: \"apikey\",\n where,\n update: { updatedAt: new Date() },\n });\n })();\n const state = { at: Date.now(), pending: true, result };\n probes.set(auth.instance, state);\n void result.then(\n () => {\n state.pending = false;\n },\n () => {\n state.pending = false;\n },\n );\n return result;\n }\n return {\n id: \"better-auth:api-key\",\n kinds: [API_KEY_PRINCIPAL_KIND],\n acceptance: options.acceptance ?? \"explicit\",\n delegates: true,\n credentialHeaders: [header],\n effects: { consumesQuota: true, writes: true },\n requires: { plugins: [\"api-key\"] },\n appliesTo: (request) => request.headers.has(header),\n async resolve(request) {\n if (!request.headers.has(header)) {\n return absent();\n }\n return request.memo(verificationMemo, async () => {\n const key = request.headers.get(header)!;\n const headers = new Headers();\n for (const name of [\"host\", \"x-forwarded-host\", \"x-forwarded-proto\"]) {\n const value = request.headers.get(name);\n if (value !== null) {\n headers.set(name, value);\n }\n }\n const started = performance.now();\n try {\n const result = await request.auth.run(\n {\n cookies: request.cookies,\n inbound: () => request.headers,\n internal: true,\n },\n () =>\n (request.auth.api as unknown as KeyApi).verifyApiKey({\n body: {\n key,\n ...(options.configId === undefined\n ? {}\n : { configId: options.configId }),\n },\n headers,\n }),\n );\n if (result.valid) {\n const verified = result.key;\n if (\n !verified ||\n typeof verified.id !== \"string\" ||\n typeof verified.referenceId !== \"string\"\n ) {\n throw new BetterAuthInfrastructureError(\n new Error(\"Malformed API-key verification result\"),\n );\n }\n const configId = verified.configId ?? null;\n const references =\n typeof options.references === \"function\"\n ? options.references({ configId })\n : (options.references ?? \"user\");\n const permissions = verified.permissions ?? null;\n const grant = role(permissions ?? {});\n return authenticated({\n kind: API_KEY_PRINCIPAL_KIND,\n source: \"better-auth:api-key\",\n keyId: verified.id,\n configId,\n referenceId: verified.referenceId,\n userId: references === \"user\" ? verified.referenceId : null,\n organizationId:\n references === \"organization\" ? verified.referenceId : null,\n permissions,\n delegation: {\n description: \"api-key permissions\",\n allows: (requested) => grant.authorize(requested).success,\n },\n });\n }\n const code = result.error?.code;\n if (code === \"RATE_LIMITED\" || code === \"USAGE_EXCEEDED\") {\n const retry = result.error?.details?.tryAgainIn;\n return rejected(\n AuthFailures.rejected({\n status: 429,\n reason: code,\n ...(code === \"RATE_LIMITED\" && typeof retry === \"number\"\n ? { retryAfterSeconds: retry / 1000 }\n : {}),\n }),\n );\n }\n if (code === \"INVALID_API_KEY\" && options.outageProbe !== false) {\n if (slowMs !== false && performance.now() - started > slowMs) {\n throw new BetterAuthInfrastructureError(\n new Error(\"API-key verification exceeded the outage threshold\"),\n );\n }\n await probe(request.auth);\n }\n if (\n code === \"INVALID_API_KEY\" ||\n code === \"KEY_NOT_FOUND\" ||\n code === \"KEY_DISABLED\" ||\n code === \"KEY_EXPIRED\"\n ) {\n return rejected(\n AuthFailures.rejected({ status: 401, reason: code }),\n );\n }\n throw new BetterAuthInfrastructureError(\n new Error(\n `Unexpected API-key verification error: ${code ?? \"missing code\"}`,\n ),\n );\n } catch (error) {\n const failure = normalizeThrown(\n error,\n \"source\",\n \"better-auth:api-key\",\n [key],\n );\n if (\"sessionLoss\" in failure) {\n throw new BetterAuthInfrastructureError(error, { secrets: [key] });\n }\n return rejected(failure);\n }\n });\n },\n };\n}\nconst apiKeyPermissionPolicy: AuthorizationPolicy<\n Record<string, string[]>,\n ApiKeyPrincipal\n> = {\n id: \"better-auth:api-key/permission\",\n requires: { plugins: [\"api-key\"], principals: [API_KEY_PRINCIPAL_KIND] },\n validate(permissions) {\n if (\n !Object.keys(permissions).length ||\n Object.values(permissions).some(\n (actions) => !Array.isArray(actions) || !actions.length,\n )\n ) {\n throw new BetterAuthConfigurationError(\n \"EMPTY_PERMISSIONS\",\n \"API-key permissions must contain a resource and actions.\",\n );\n }\n },\n evaluate(permissions, context) {\n return context.principal.delegation.allows(permissions)\n ? { effect: \"allow\" }\n : { effect: \"deny\", reason: \"MISSING_PERMISSION\" };\n },\n};\nexport function apiKeyPermission(\n permissions: Record<string, string[]>,\n): Requirement<Record<string, string[]>> {\n return { policy: apiKeyPermissionPolicy, params: permissions };\n}\nexport function RequireApiKeyPermission(\n permissions: Record<string, string[]>,\n): ClassDecorator & MethodDecorator {\n return Require(apiKeyPermission(permissions));\n}\n"],"mappings":"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"}
package/dist/api-key.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as BetterAuthConfigurationError, r as BetterAuthInfrastructureError, t as AuthFailures, u as normalizeThrown } from "./auth-errors-B6jLBpQu.mjs";
2
- import { c as absent, f as rejected, l as authenticated } from "./principal-readings-BKeyOxwJ.mjs";
3
- import { u as Require } from "./auth-decorators-BmUNYTFd.mjs";
2
+ import { a as absent, l as rejected, o as authenticated } from "./auth-scope-interceptor-ctnmljij.mjs";
3
+ import { u as Require } from "./auth-decorators-DUmQ-n_w.mjs";
4
4
  import { role } from "better-auth/plugins/access";
5
5
  //#region src/api-key.ts
6
6
  const API_KEY_PRINCIPAL_KIND = "api-key";
@@ -1,127 +1,10 @@
1
1
  const require_auth_errors = require("./auth-errors-1EdQ36D8.cjs");
2
2
  const require_decorate = require("./decorate-Bvg21vkL.cjs");
3
- const require_principal_readings = require("./principal-readings-CpF4q6b-.cjs");
3
+ const require_auth_scope_interceptor = require("./auth-scope-interceptor-DU57SvDf.cjs");
4
+ const require_auth_guard = require("./auth-guard-CdV9G47g.cjs");
5
+ const require_principal_readings = require("./principal-readings-K8zeZbEl.cjs");
4
6
  let _nestjs_common = require("@nestjs/common");
5
7
  let node_async_hooks = require("node:async_hooks");
6
- //#region src/auth-guard.ts
7
- let BetterAuthGuard = class BetterAuthGuard {
8
- core;
9
- static [require_decorate.AUTH_ENHANCER] = "guard";
10
- [require_decorate.AUTH_ENHANCER] = "guard";
11
- constructor(core) {
12
- this.core = core;
13
- }
14
- canActivate(context) {
15
- return this.core.canActivate(context);
16
- }
17
- };
18
- BetterAuthGuard = require_decorate.__decorate([
19
- (0, _nestjs_common.Injectable)(),
20
- require_decorate.__decorateParam(0, (0, _nestjs_common.Inject)(require_decorate.GUARD_CORE)),
21
- require_decorate.__decorateMetadata("design:paramtypes", [typeof Pick === "undefined" ? Object : Pick])
22
- ], BetterAuthGuard);
23
- var GuardCore = class {
24
- instances;
25
- planner;
26
- transports;
27
- resolver;
28
- evaluator;
29
- scope;
30
- readings;
31
- constructor(instances, planner, transports, resolver, evaluator, scope, readings) {
32
- this.instances = instances;
33
- this.planner = planner;
34
- this.transports = transports;
35
- this.resolver = resolver;
36
- this.evaluator = evaluator;
37
- this.scope = scope;
38
- this.readings = readings;
39
- }
40
- async canActivate(context) {
41
- let transport;
42
- let call;
43
- let plan;
44
- try {
45
- plan = this.planner.forContext(context);
46
- if (plan.access === "inherit") {
47
- transport = this.transports.find(context);
48
- const lineage = transport?.lineage?.(context);
49
- if (lineage && !this.readings.enclosing(lineage, context.getArgs())) {
50
- call = this.transports.describe(context, transport);
51
- throw require_auth_errors.BetterAuthConfigurationError.atRequest("NO_ENCLOSING_DECISION", "An inherited invocation has no enclosing guard decision", { site: plan.site });
52
- }
53
- if (plan.originCheck !== "form") return true;
54
- }
55
- if (plan.access === "public" && plan.originCheck !== "form") {
56
- const lineage = this.transports.find(context)?.lineage?.(context);
57
- if (lineage && !plan.nested) this.readings.recordLineage(lineage, {
58
- outcome: "no-identity",
59
- instance: plan.instance
60
- });
61
- return true;
62
- }
63
- transport = this.transports.select(context);
64
- call = this.transports.describe(context, transport);
65
- const entry = this.instances.get(plan.instance);
66
- if (plan.originCheck !== "off") {
67
- const browser = call.browser;
68
- if (browser) {
69
- if (plan.originCheck === "form" || browser.enforce) {
70
- const failure = await entry.origin.check(browser, plan.originCheck);
71
- if (failure) throw failure;
72
- } else await entry.origin.advisory(browser);
73
- }
74
- }
75
- if (plan.access === "inherit") return true;
76
- if (plan.access === "public") {
77
- if (!plan.nested) this.readings.record(call, {
78
- outcome: "no-identity",
79
- instance: plan.instance
80
- });
81
- return true;
82
- }
83
- const result = await this.resolver.resolve(call, {
84
- auth: entry.handle,
85
- freshness: plan.freshness,
86
- accepts: plan.accepts,
87
- sourceSet: plan.sourceSet
88
- });
89
- this.readings.record(call, {
90
- ...result,
91
- instance: plan.instance
92
- });
93
- if (result.outcome === "rejected") throw result.failure;
94
- if (result.outcome === "absent") {
95
- if (plan.access === "optional") return true;
96
- throw require_auth_errors.AuthFailures.unauthenticated();
97
- }
98
- if (!plan.accepts.has(result.principal.kind)) throw require_auth_errors.AuthFailures.forbidden("PRINCIPAL_NOT_SUPPORTED");
99
- const decision = await this.evaluator.evaluate(plan, result.principal, call, entry, context, transport.id);
100
- if (decision.effect === "deny") throw require_auth_errors.AuthFailures.rejected({
101
- status: decision.status ?? 403,
102
- reason: decision.reason,
103
- message: decision.message,
104
- challenge: decision.challenge
105
- });
106
- return true;
107
- } catch (error) {
108
- if (require_auth_errors.isAuthFailure(error) && transport) {
109
- const mapping = plan && this.instances.get(plan.instance).options.errors?.map;
110
- throw mapping ? mapping(error, context, transport.id) : transport.toException(error, context);
111
- }
112
- if ((require_auth_errors.isConfigurationError(error) || require_auth_errors.isInfrastructureError(error)) && transport) {
113
- const repeated = call ? this.scope.surfaced(call.key, error, {
114
- instance: plan?.instance ?? "default",
115
- site: plan?.site ?? "guard",
116
- lineage: call.lineage
117
- }) : false;
118
- throw transport.toInternalException?.(error, context, { repeated }) ?? error;
119
- }
120
- throw error;
121
- }
122
- }
123
- };
124
- //#endregion
125
8
  //#region src/request-scope.ts
126
9
  const SURFACED = Symbol.for("nestjs-slightly-better-auth:surfaced");
127
10
  function memoKey(instance, ...inputs) {
@@ -265,7 +148,7 @@ const AcceptPrincipals = (...kinds) => (0, _nestjs_common.SetMetadata)(require_d
265
148
  const SkipDefaultRequirements = () => (0, _nestjs_common.SetMetadata)(require_decorate.SKIP_DEFAULT_REQUIREMENTS_METADATA, true);
266
149
  const ForwardAuthCookies = (enabled = true) => (0, _nestjs_common.SetMetadata)(require_decorate.FORWARD_COOKIES_METADATA, enabled);
267
150
  const SkipOriginCheck = () => (0, _nestjs_common.SetMetadata)(require_decorate.SKIP_ORIGIN_CHECK_METADATA, true);
268
- const UseBetterAuth = () => (0, _nestjs_common.applyDecorators)((0, _nestjs_common.SetMetadata)(require_decorate.USE_BETTER_AUTH_METADATA, true), (0, _nestjs_common.UseGuards)(BetterAuthGuard), (0, _nestjs_common.UseInterceptors)(require_principal_readings.BetterAuthScopeInterceptor));
151
+ const UseBetterAuth = () => (0, _nestjs_common.applyDecorators)((0, _nestjs_common.SetMetadata)(require_decorate.USE_BETTER_AUTH_METADATA, true), (0, _nestjs_common.UseGuards)(require_auth_guard.BetterAuthGuard), (0, _nestjs_common.UseInterceptors)(require_auth_scope_interceptor.BetterAuthScopeInterceptor));
269
152
  function Require(...requirements) {
270
153
  return (target, _key, descriptor) => {
271
154
  for (const expression of requirements) append(descriptor?.value ?? target, require_decorate.REQUIREMENTS_METADATA, expression);
@@ -419,12 +302,6 @@ Object.defineProperty(exports, "BeforeDatabase", {
419
302
  return BeforeDatabase;
420
303
  }
421
304
  });
422
- Object.defineProperty(exports, "BetterAuthGuard", {
423
- enumerable: true,
424
- get: function() {
425
- return BetterAuthGuard;
426
- }
427
- });
428
305
  Object.defineProperty(exports, "CurrentPrincipal", {
429
306
  enumerable: true,
430
307
  get: function() {
@@ -437,12 +314,6 @@ Object.defineProperty(exports, "ForwardAuthCookies", {
437
314
  return ForwardAuthCookies;
438
315
  }
439
316
  });
440
- Object.defineProperty(exports, "GuardCore", {
441
- enumerable: true,
442
- get: function() {
443
- return GuardCore;
444
- }
445
- });
446
317
  Object.defineProperty(exports, "OptionalAuth", {
447
318
  enumerable: true,
448
319
  get: function() {
@@ -540,4 +411,4 @@ Object.defineProperty(exports, "requirement", {
540
411
  }
541
412
  });
542
413
 
543
- //# sourceMappingURL=auth-decorators-CJUQDwAQ.cjs.map
414
+ //# sourceMappingURL=auth-decorators-B7xasVk_.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-decorators-B7xasVk_.cjs","names":["AsyncLocalStorage","AuthFailures","isConfigurationError","SetMetadata","applyDecorators","UseGuards","BetterAuthGuard","UseInterceptors","BetterAuthScopeInterceptor","PrincipalReadings","createParamDecorator","BetterAuthConfigurationError"],"sources":["../src/request-scope.ts","../src/auth-decorators.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type {\n AuthorizationDecision,\n InvocationLineage,\n PrincipalReading,\n PrincipalResult,\n RoutePlan,\n TransportCall,\n} from \"./auth-contracts.js\";\nimport {\n AuthFailures,\n type AuthFailure,\n isConfigurationError,\n} from \"./auth-errors.js\";\nimport type { ScopeView } from \"./bridge-protocol.js\";\n\nexport interface ScopeState {\n readonly view: ScopeView;\n readonly call?: TransportCall;\n readonly plan?: RoutePlan;\n readonly reading?: () => PrincipalReading;\n}\nexport interface RequestState {\n readonly principal: Map<string, Promise<PrincipalResult>>;\n readonly policyIo: Map<string, Promise<unknown>>;\n readonly decisions: Map<string, Promise<AuthorizationDecision>>;\n readonly values: Map<symbol, unknown>;\n readonly authorizationCalls: Set<string>;\n readonly origins: Map<string, Promise<AuthFailure | null>>;\n readonly surfaced: Set<string>;\n readonly connections: Map<\n string,\n {\n value: Extract<PrincipalResult, { outcome: \"authenticated\" | \"absent\" }>;\n expiresAt: number;\n }\n >;\n}\n\nconst SURFACED = Symbol.for(\"nestjs-slightly-better-auth:surfaced\");\nexport function memoKey(\n instance: string,\n ...inputs: readonly unknown[]\n): string {\n return JSON.stringify([instance, ...inputs]);\n}\n\n/** Shared by guards and readers that have only the transport's structural lineage. */\nexport function surfacedRecord(\n key: object,\n lineage?: InvocationLineage,\n): Set<string> {\n const carrier = lineage?.carrier ?? key;\n const partition = lineage ? lineage.position.split(\":\", 1)[0]! : \"\";\n let records = Reflect.get(carrier, SURFACED) as\n | Map<string, Set<string>>\n | undefined;\n if (!records) {\n records = new Map();\n Object.defineProperty(carrier, SURFACED, { value: records });\n }\n let record = records.get(partition);\n if (!record) {\n record = new Set();\n records.set(partition, record);\n }\n return record;\n}\n\nexport class RequestScope {\n readonly #storage = new AsyncLocalStorage<ScopeState>();\n readonly #states = new WeakMap<object, RequestState>();\n readonly #valueKeys = new Map<string, Map<symbol, symbol>>();\n readonly #errorIds = new WeakMap<object, number>();\n #nextError = 0;\n\n run<T>(state: ScopeState, fn: () => T): T {\n return this.#storage.run(state, fn);\n }\n\n exit<T>(fn: () => T): T {\n return this.#storage.exit(fn);\n }\n\n current(): ScopeState | undefined {\n return this.#storage.getStore();\n }\n\n stateFor(key: object): RequestState {\n let state = this.#states.get(key);\n if (!state) {\n state = {\n principal: new Map(),\n policyIo: new Map(),\n decisions: new Map(),\n values: new Map(),\n authorizationCalls: new Set(),\n origins: new Map(),\n surfaced: new Set(),\n connections: new Map(),\n };\n this.#states.set(key, state);\n }\n return state;\n }\n\n memoPrincipal(\n call: Pick<TransportCall, \"key\" | \"connection\" | \"principalTtlMs\">,\n input: {\n instance: string;\n freshness: \"default\" | \"authoritative\";\n sourceSet: string;\n },\n compute: () => Promise<PrincipalResult>,\n ): Promise<PrincipalResult> {\n const key = memoKey(input.instance, input.freshness, input.sourceSet);\n const state = this.stateFor(call.key);\n const existing = state.principal.get(key);\n if (existing) {\n return existing;\n }\n const connection =\n call.connection &&\n input.freshness !== \"authoritative\" &&\n (call.principalTtlMs ?? 0) > 0\n ? this.stateFor(call.connection).connections\n : undefined;\n const cached = connection?.get(key);\n const promise = Promise.resolve().then(async () => {\n if (cached && cached.expiresAt > Date.now()) {\n return cached.value;\n }\n connection?.delete(key);\n try {\n const value = await compute();\n if (value.outcome !== \"rejected\") {\n connection?.set(key, {\n value,\n expiresAt: Date.now() + (call.principalTtlMs ?? 0),\n });\n }\n return value;\n } catch (error) {\n connection?.delete(key);\n throw error;\n }\n });\n state.principal.set(key, promise);\n return promise;\n }\n\n memoPolicyIo<T>(\n request: object,\n instance: string,\n concreteKey: string,\n compute: () => Promise<T>,\n limit: number | false = 100,\n ): Promise<T> {\n const state = this.stateFor(request);\n const key = memoKey(instance, concreteKey);\n const existing = state.policyIo.get(key);\n if (existing) {\n return existing as Promise<T>;\n }\n if (\n limit !== false &&\n [...state.authorizationCalls].filter(\n (entry) => JSON.parse(entry)[0] === instance,\n ).length >= limit\n ) {\n return Promise.reject(\n AuthFailures.rejected({\n status: 429,\n reason: \"TOO_MANY_AUTHORIZATION_CHECKS\",\n }),\n );\n }\n state.authorizationCalls.add(key);\n const promise = Promise.resolve().then(compute);\n state.policyIo.set(key, promise);\n return promise;\n }\n\n memoDecision(\n invocation: object,\n instance: string,\n concreteKey: string,\n compute: () => Promise<AuthorizationDecision>,\n ): Promise<AuthorizationDecision> {\n const decisions = this.stateFor(invocation).decisions;\n const key = memoKey(instance, concreteKey);\n let promise = decisions.get(key);\n if (!promise) {\n promise = Promise.resolve().then(compute);\n decisions.set(key, promise);\n }\n return promise;\n }\n\n valueKey(instance: string, key: symbol): symbol {\n let keys = this.#valueKeys.get(instance);\n if (!keys) {\n keys = new Map();\n this.#valueKeys.set(instance, keys);\n }\n let namespaced = keys.get(key);\n if (!namespaced) {\n namespaced = Symbol(key.description);\n keys.set(key, namespaced);\n }\n return namespaced;\n }\n\n surfaced(\n key: object,\n error: object,\n context: { instance: string; site: string; lineage?: InvocationLineage },\n ): boolean {\n let id = this.#errorIds.get(error);\n if (!id) {\n id = ++this.#nextError;\n this.#errorIds.set(error, id);\n }\n const reason = isConfigurationError(error)\n ? memoKey(context.instance, error.code, context.site)\n : `error:${id}`;\n const carrier = surfacedRecord(key, context.lineage);\n const state =\n key === context.lineage?.carrier ? carrier : this.stateFor(key).surfaced;\n const repeated = state.has(reason) || carrier.has(reason);\n state.add(reason);\n carrier.add(reason);\n return repeated;\n }\n}\n","import {\n applyDecorators,\n createParamDecorator,\n SetMetadata,\n UseGuards,\n UseInterceptors,\n type ExecutionContext,\n} from \"@nestjs/common\";\nimport type {\n DatabaseHookTarget,\n DbHookMethodDecorator,\n DbHookOptions,\n HookMethodDecorator,\n HookOptions,\n HookPredicate,\n PolicyRef,\n PrincipalReading,\n Requirement,\n RequirementExpr,\n RequirementOptions,\n} from \"./auth-contracts.js\";\nimport type {\n EndpointPath,\n PrincipalKind,\n PrincipalOfKind,\n} from \"./auth-types.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport * as tokens from \"./auth-tokens.js\";\nimport { BetterAuthGuard } from \"./auth-guard.js\";\nimport { BetterAuthScopeInterceptor } from \"./auth-scope-interceptor.js\";\nimport { PrincipalReadings } from \"./principal-readings.js\";\nimport { RequestScope } from \"./request-scope.js\";\n\nfunction append(target: object, key: symbol, value: unknown): void {\n Reflect.defineMetadata(\n key,\n Object.freeze([...(Reflect.getOwnMetadata(key, target) ?? []), value]),\n target,\n );\n}\nexport const Public = () => SetMetadata(tokens.ACCESS_METADATA, \"public\");\nexport const OptionalAuth = () =>\n SetMetadata(tokens.ACCESS_METADATA, \"optional\");\nexport const RequireAuth = (options?: { authoritative?: boolean }) =>\n applyDecorators(\n SetMetadata(tokens.ACCESS_METADATA, \"required\"),\n SetMetadata(tokens.FRESHNESS_METADATA, options?.authoritative === true),\n );\nexport const UseAuthInstance = (name: string) =>\n SetMetadata(tokens.AUTH_INSTANCE_METADATA, name);\nexport const AcceptPrincipals = (\n ...kinds: readonly [PrincipalKind, ...PrincipalKind[]]\n) => SetMetadata(tokens.ACCEPT_PRINCIPALS_METADATA, Object.freeze([...kinds]));\nexport const SkipDefaultRequirements = () =>\n SetMetadata(tokens.SKIP_DEFAULT_REQUIREMENTS_METADATA, true);\nexport const ForwardAuthCookies = (enabled = true) =>\n SetMetadata(tokens.FORWARD_COOKIES_METADATA, enabled);\nexport const SkipOriginCheck = () =>\n SetMetadata(tokens.SKIP_ORIGIN_CHECK_METADATA, true);\nexport const UseBetterAuth = () =>\n applyDecorators(\n SetMetadata(tokens.USE_BETTER_AUTH_METADATA, true),\n UseGuards(BetterAuthGuard),\n UseInterceptors(BetterAuthScopeInterceptor),\n );\nexport function Require(\n ...requirements: readonly RequirementExpr[]\n): ClassDecorator & MethodDecorator {\n return (\n target: object,\n _key?: string | symbol,\n descriptor?: PropertyDescriptor,\n ) => {\n for (const expression of requirements) {\n append(\n descriptor?.value ?? target,\n tokens.REQUIREMENTS_METADATA,\n expression,\n );\n }\n };\n}\nexport const anyOf = (\n ...requirements: readonly RequirementExpr[]\n): RequirementExpr => ({ anyOf: requirements });\nexport const allOf = (\n ...requirements: readonly RequirementExpr[]\n): RequirementExpr => ({ allOf: requirements });\nexport const requirement = <P>(\n policy: PolicyRef<P>,\n params: P,\n options?: RequirementOptions,\n): Requirement<P> => ({ policy, params, ...options });\n\nexport interface ReaderContext {\n readonly args: readonly unknown[];\n readonly readings: PrincipalReadings;\n readonly scope: RequestScope;\n readonly reading?: () => PrincipalReading;\n}\nexport function readerContext(context: ExecutionContext): ReaderContext {\n const args = context.getArgs();\n for (const arg of [args, ...args]) {\n if (\n (typeof arg !== \"object\" || arg === null) &&\n typeof arg !== \"function\"\n ) {\n continue;\n }\n const bridge = Reflect.get(arg, tokens.READER_CONTEXT) as\n | ReaderContext\n | undefined;\n if (\n bridge &&\n bridge.args.length === args.length &&\n bridge.args.every((value, index) => value === args[index]) &&\n bridge.reading !== undefined &&\n bridge.scope.current()?.reading === bridge.reading\n ) {\n return bridge;\n }\n }\n // No application state is created or retained by a DI-free lineage-only reader.\n const scope = new RequestScope();\n return { args, scope, readings: new PrincipalReadings(scope) };\n}\nfunction principalParam(spec?: {\n kind: string;\n reason: string;\n project: (p: any) => unknown;\n}): ParameterDecorator {\n const decorator = createParamDecorator(\n (_data: unknown, context: ExecutionContext) => {\n const bridge = readerContext(context);\n const input = {\n args: context.getArgs(),\n site: `${context.getClass().name}.${context.getHandler().name}`,\n };\n const reading =\n bridge.reading !== undefined &&\n bridge.scope.current()?.reading === bridge.reading\n ? bridge.readings.current()\n : bridge.readings.read(input);\n return bridge.readings.project(\n reading,\n { ...spec, site: input.site, project: spec?.project ?? ((p) => p) },\n input,\n );\n },\n )();\n return (target, propertyKey, index) => {\n if (propertyKey === undefined) {\n throw new BetterAuthConfigurationError(\n \"PRINCIPAL_PARAM_TARGET\",\n \"Principal parameters require a handler method\",\n );\n }\n const handler = Reflect.get(target, propertyKey);\n append(handler, tokens.PRINCIPAL_PARAMS_METADATA, {\n index,\n ...(spec ? { kind: spec.kind, reason: spec.reason } : {}),\n });\n decorator(target, propertyKey, index);\n };\n}\nexport const CurrentPrincipal = (): ParameterDecorator => principalParam();\nexport function definePrincipalParam<K extends PrincipalKind, R>(spec: {\n readonly kind: K;\n readonly reason: string;\n readonly project: (principal: PrincipalOfKind<K>) => R;\n}): () => ParameterDecorator {\n return () => principalParam(spec);\n}\nexport function defineInvocationParam(\n slot: symbol,\n spec: { readonly missing: string },\n): () => ParameterDecorator {\n return () => {\n const decorator = createParamDecorator(\n (_data: unknown, context: ExecutionContext) => {\n const bridge = readerContext(context);\n const state = bridge.scope.current();\n const input = {\n args: context.getArgs(),\n site: `${context.getClass().name}.${context.getHandler().name}`,\n };\n const reading = state?.reading\n ? bridge.readings.current()\n : bridge.readings.read(input);\n const candidates = state?.call\n ? [state.call.invocation]\n : [input.args, ...input.args];\n for (const candidate of candidates) {\n if (\n !candidate ||\n (typeof candidate !== \"object\" && typeof candidate !== \"function\")\n ) {\n continue;\n }\n const values = Reflect.get(candidate, tokens.INVOCATION_VALUES) as\n | Map<string, Map<symbol, unknown>>\n | undefined;\n if (values?.get(reading.instance)?.has(slot)) {\n return values.get(reading.instance)!.get(slot);\n }\n }\n throw bridge.readings.deliver(\n BetterAuthConfigurationError.atRequest(\n \"NO_INVOCATION_VALUE\",\n spec.missing,\n { site: input.site },\n ),\n input,\n reading.instance,\n );\n },\n )();\n return (target, key, index) => {\n if (key === undefined) {\n throw new BetterAuthConfigurationError(\n \"INVOCATION_PARAM_TARGET\",\n \"Invocation parameters require a handler method\",\n );\n }\n append(Reflect.get(target, key), tokens.INVOCATION_PARAMS_METADATA, {\n index,\n slot,\n missing: spec.missing,\n });\n decorator(target, key, index);\n };\n };\n}\nexport interface EndpointHookDeclaration {\n readonly phase: \"before\" | \"after\";\n readonly match: string | readonly string[] | HookPredicate | undefined;\n readonly options: Readonly<HookOptions>;\n readonly method: string | symbol;\n // biome-ignore lint/complexity/noBannedTypes: Nest handler metadata preserves the framework function identity.\n readonly handler: Function;\n readonly declarationOrder: number;\n}\nexport interface DatabaseHookDeclaration {\n readonly phase: \"before\" | \"after\";\n readonly target: DatabaseHookTarget;\n readonly options: Readonly<DbHookOptions>;\n readonly method: string | symbol;\n // biome-ignore lint/complexity/noBannedTypes: Nest handler metadata preserves the framework function identity.\n readonly handler: Function;\n readonly declarationOrder: number;\n}\n// biome-ignore lint/complexity/noBannedTypes: Nest handler metadata preserves the framework function identity.\nexport function readHookMetadata(handler: Function): {\n readonly hooks: readonly EndpointHookDeclaration[];\n readonly databaseHooks: readonly DatabaseHookDeclaration[];\n} {\n return {\n hooks: Reflect.getOwnMetadata(tokens.HOOK_METADATA, handler) ?? [],\n databaseHooks:\n Reflect.getOwnMetadata(tokens.DB_HOOK_METADATA, handler) ?? [],\n };\n}\nfunction hook<P extends string>(\n phase: \"before\" | \"after\",\n match?: P | readonly P[] | HookPredicate,\n options?: HookOptions,\n): HookMethodDecorator<P> {\n return (_target, method, descriptor) => {\n const handler = descriptor.value!;\n const records = readHookMetadata(handler).hooks;\n append(\n handler,\n tokens.HOOK_METADATA,\n Object.freeze({\n phase,\n match: Array.isArray(match) ? Object.freeze([...match]) : match,\n options: Object.freeze({ ...options }),\n method,\n handler,\n declarationOrder: records.length,\n }),\n );\n };\n}\nexport const BeforeAuth = <const P extends EndpointPath | (string & {})>(\n match?: P | readonly P[] | HookPredicate,\n options?: HookOptions,\n): HookMethodDecorator<P> => hook(\"before\", match, options);\nexport const AfterAuth = <const P extends EndpointPath | (string & {})>(\n match?: P | readonly P[] | HookPredicate,\n options?: HookOptions,\n): HookMethodDecorator<P> => hook(\"after\", match, options);\nfunction databaseHook<\n E extends DatabaseHookTarget,\n Ph extends \"before\" | \"after\",\n>(phase: Ph, target: E, options?: DbHookOptions): DbHookMethodDecorator<E, Ph> {\n return (_prototype, method, descriptor) => {\n const handler = descriptor.value!;\n append(\n handler,\n tokens.DB_HOOK_METADATA,\n Object.freeze({\n phase,\n target,\n options: Object.freeze({ ...options }),\n method,\n handler,\n declarationOrder: readHookMetadata(handler).databaseHooks.length,\n }),\n );\n };\n}\nexport const BeforeDatabase = <E extends DatabaseHookTarget>(\n target: E,\n options?: DbHookOptions,\n): DbHookMethodDecorator<E, \"before\"> =>\n databaseHook(\"before\", target, options);\nexport const AfterDatabase = <E extends DatabaseHookTarget>(\n target: E,\n options?: DbHookOptions,\n): DbHookMethodDecorator<E, \"after\"> => databaseHook(\"after\", target, options);\n"],"mappings":"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"}