@nestjs-kit/better-auth 0.0.0-stage → 2.0.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 (142) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +1078 -2
  3. package/dist/admin.cjs +88 -0
  4. package/dist/admin.cjs.map +1 -0
  5. package/dist/admin.d.cts +14 -0
  6. package/dist/admin.d.cts.map +1 -0
  7. package/dist/admin.d.mts +14 -0
  8. package/dist/admin.d.mts.map +1 -0
  9. package/dist/admin.mjs +85 -0
  10. package/dist/admin.mjs.map +1 -0
  11. package/dist/api-key.cjs +164 -0
  12. package/dist/api-key.cjs.map +1 -0
  13. package/dist/api-key.d.cts +42 -0
  14. package/dist/api-key.d.cts.map +1 -0
  15. package/dist/api-key.d.mts +42 -0
  16. package/dist/api-key.d.mts.map +1 -0
  17. package/dist/api-key.mjs +160 -0
  18. package/dist/api-key.mjs.map +1 -0
  19. package/dist/auth-contracts-Brh_Qko9.d.cts +1654 -0
  20. package/dist/auth-contracts-Brh_Qko9.d.cts.map +1 -0
  21. package/dist/auth-contracts-Cz7C4_Ch.d.mts +1654 -0
  22. package/dist/auth-contracts-Cz7C4_Ch.d.mts.map +1 -0
  23. package/dist/auth-decorators-B1mFnGJ6.mjs +284 -0
  24. package/dist/auth-decorators-B1mFnGJ6.mjs.map +1 -0
  25. package/dist/auth-decorators-Cu_nxWnF.cjs +421 -0
  26. package/dist/auth-decorators-Cu_nxWnF.cjs.map +1 -0
  27. package/dist/auth-errors-C3rbTV9p.mjs +301 -0
  28. package/dist/auth-errors-C3rbTV9p.mjs.map +1 -0
  29. package/dist/auth-errors-DmiKl0L_.cjs +378 -0
  30. package/dist/auth-errors-DmiKl0L_.cjs.map +1 -0
  31. package/dist/auth-guard-CLyhAEEj.cjs +136 -0
  32. package/dist/auth-guard-CLyhAEEj.cjs.map +1 -0
  33. package/dist/auth-guard-h9QB_l11.mjs +125 -0
  34. package/dist/auth-guard-h9QB_l11.mjs.map +1 -0
  35. package/dist/auth-module-BgcY2HpA.cjs +2809 -0
  36. package/dist/auth-module-BgcY2HpA.cjs.map +1 -0
  37. package/dist/auth-module-CtHx3gRU.mjs +2708 -0
  38. package/dist/auth-module-CtHx3gRU.mjs.map +1 -0
  39. package/dist/auth-scope-interceptor-BjxAL1-S.cjs +290 -0
  40. package/dist/auth-scope-interceptor-BjxAL1-S.cjs.map +1 -0
  41. package/dist/auth-scope-interceptor-DYPn3b8M.mjs +231 -0
  42. package/dist/auth-scope-interceptor-DYPn3b8M.mjs.map +1 -0
  43. package/dist/bridge-protocol-BDw8opAG.cjs +25 -0
  44. package/dist/bridge-protocol-BDw8opAG.cjs.map +1 -0
  45. package/dist/bridge-protocol-c66aEkr5.mjs +8 -0
  46. package/dist/bridge-protocol-c66aEkr5.mjs.map +1 -0
  47. package/dist/decorate-Cr4Q7bNE.cjs +319 -0
  48. package/dist/decorate-Cr4Q7bNE.cjs.map +1 -0
  49. package/dist/decorate-Dt0e9K6R.mjs +80 -0
  50. package/dist/decorate-Dt0e9K6R.mjs.map +1 -0
  51. package/dist/express.cjs +256 -0
  52. package/dist/express.cjs.map +1 -0
  53. package/dist/express.d.cts +37 -0
  54. package/dist/express.d.cts.map +1 -0
  55. package/dist/express.d.mts +37 -0
  56. package/dist/express.d.mts.map +1 -0
  57. package/dist/express.mjs +254 -0
  58. package/dist/express.mjs.map +1 -0
  59. package/dist/fastify.cjs +218 -0
  60. package/dist/fastify.cjs.map +1 -0
  61. package/dist/fastify.d.cts +28 -0
  62. package/dist/fastify.d.cts.map +1 -0
  63. package/dist/fastify.d.mts +28 -0
  64. package/dist/fastify.d.mts.map +1 -0
  65. package/dist/fastify.mjs +216 -0
  66. package/dist/fastify.mjs.map +1 -0
  67. package/dist/graphql.cjs +509 -0
  68. package/dist/graphql.cjs.map +1 -0
  69. package/dist/graphql.d.cts +38 -0
  70. package/dist/graphql.d.cts.map +1 -0
  71. package/dist/graphql.d.mts +38 -0
  72. package/dist/graphql.d.mts.map +1 -0
  73. package/dist/graphql.mjs +503 -0
  74. package/dist/graphql.mjs.map +1 -0
  75. package/dist/index.cjs +86 -0
  76. package/dist/index.d.cts +2 -0
  77. package/dist/index.d.mts +2 -0
  78. package/dist/index.mjs +8 -0
  79. package/dist/microservices.cjs +231 -0
  80. package/dist/microservices.cjs.map +1 -0
  81. package/dist/microservices.d.cts +39 -0
  82. package/dist/microservices.d.cts.map +1 -0
  83. package/dist/microservices.d.mts +39 -0
  84. package/dist/microservices.d.mts.map +1 -0
  85. package/dist/microservices.mjs +222 -0
  86. package/dist/microservices.mjs.map +1 -0
  87. package/dist/organization.cjs +191 -0
  88. package/dist/organization.cjs.map +1 -0
  89. package/dist/organization.d.cts +40 -0
  90. package/dist/organization.d.cts.map +1 -0
  91. package/dist/organization.d.mts +40 -0
  92. package/dist/organization.d.mts.map +1 -0
  93. package/dist/organization.mjs +177 -0
  94. package/dist/organization.mjs.map +1 -0
  95. package/dist/platform-CZl7TY8j.mjs +414 -0
  96. package/dist/platform-CZl7TY8j.mjs.map +1 -0
  97. package/dist/platform-YICqqYv6.cjs +473 -0
  98. package/dist/platform-YICqqYv6.cjs.map +1 -0
  99. package/dist/platform.cjs +10 -0
  100. package/dist/platform.d.cts +43 -0
  101. package/dist/platform.d.cts.map +1 -0
  102. package/dist/platform.d.mts +43 -0
  103. package/dist/platform.d.mts.map +1 -0
  104. package/dist/platform.mjs +1 -0
  105. package/dist/plugin.cjs +323 -0
  106. package/dist/plugin.cjs.map +1 -0
  107. package/dist/plugin.d.cts +24 -0
  108. package/dist/plugin.d.cts.map +1 -0
  109. package/dist/plugin.d.mts +24 -0
  110. package/dist/plugin.d.mts.map +1 -0
  111. package/dist/plugin.mjs +321 -0
  112. package/dist/plugin.mjs.map +1 -0
  113. package/dist/principal-readings-Chmt6rQV.cjs +208 -0
  114. package/dist/principal-readings-Chmt6rQV.cjs.map +1 -0
  115. package/dist/principal-readings-Ied2staw.mjs +191 -0
  116. package/dist/principal-readings-Ied2staw.mjs.map +1 -0
  117. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  118. package/dist/testing/conformance.cjs +6214 -0
  119. package/dist/testing/conformance.cjs.map +1 -0
  120. package/dist/testing/conformance.d.cts +528 -0
  121. package/dist/testing/conformance.d.cts.map +1 -0
  122. package/dist/testing/conformance.d.mts +528 -0
  123. package/dist/testing/conformance.d.mts.map +1 -0
  124. package/dist/testing/conformance.mjs +6204 -0
  125. package/dist/testing/conformance.mjs.map +1 -0
  126. package/dist/testing.cjs +221 -0
  127. package/dist/testing.cjs.map +1 -0
  128. package/dist/testing.d.cts +64 -0
  129. package/dist/testing.d.cts.map +1 -0
  130. package/dist/testing.d.mts +64 -0
  131. package/dist/testing.d.mts.map +1 -0
  132. package/dist/testing.mjs +214 -0
  133. package/dist/testing.mjs.map +1 -0
  134. package/dist/websockets.cjs +396 -0
  135. package/dist/websockets.cjs.map +1 -0
  136. package/dist/websockets.d.cts +67 -0
  137. package/dist/websockets.d.cts.map +1 -0
  138. package/dist/websockets.d.mts +67 -0
  139. package/dist/websockets.d.mts.map +1 -0
  140. package/dist/websockets.mjs +383 -0
  141. package/dist/websockets.mjs.map +1 -0
  142. package/package.json +327 -3
package/dist/admin.cjs ADDED
@@ -0,0 +1,88 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_auth_errors = require("./auth-errors-DmiKl0L_.cjs");
3
+ const require_auth_decorators = require("./auth-decorators-Cu_nxWnF.cjs");
4
+ let better_auth_api = require("better-auth/api");
5
+ //#region src/admin.ts
6
+ const adminPermissionPolicy = {
7
+ id: "better-auth:admin/permission",
8
+ requires: {
9
+ plugins: ["admin"],
10
+ principals: ["session"],
11
+ freshIdentity: true,
12
+ hostlessCalls: true
13
+ },
14
+ validate({ permissions }) {
15
+ if (!Object.keys(permissions).length || Object.values(permissions).some((actions) => !Array.isArray(actions) || !actions.length)) throw new require_auth_errors.BetterAuthConfigurationError("EMPTY_PERMISSIONS", "Admin permissions must contain a resource and actions.");
16
+ },
17
+ async evaluate({ permissions }, context) {
18
+ const { principal } = context;
19
+ if (!principal.userId) return {
20
+ effect: "deny",
21
+ reason: "USER_REQUIRED"
22
+ };
23
+ const userId = principal.userId;
24
+ let role;
25
+ if (principal.delegation) {
26
+ const owner = await context.memo(JSON.stringify(["admin:owner", userId]), async () => (await context.auth.context()).internalAdapter.findUserById(userId));
27
+ if (!owner) return {
28
+ effect: "deny",
29
+ status: 401,
30
+ reason: "USER_NOT_FOUND"
31
+ };
32
+ if (owner.banned && (!owner.banExpires || new Date(owner.banExpires).getTime() > Date.now())) return {
33
+ effect: "deny",
34
+ status: 401,
35
+ reason: "USER_BANNED"
36
+ };
37
+ if (typeof owner.role === "string" && owner.role.length) role = owner.role;
38
+ }
39
+ const stablePermissions = Object.entries(permissions).sort(([a], [b]) => a.localeCompare(b)).map(([resource, actions]) => [resource, [...actions].sort()]);
40
+ try {
41
+ if (!(await context.memo(JSON.stringify([
42
+ "admin:userHasPermission",
43
+ userId,
44
+ role ?? null,
45
+ stablePermissions
46
+ ]), () => context.auth.run({
47
+ cookies: context.cookies,
48
+ inbound: () => context.headers,
49
+ internal: true
50
+ }, () => context.auth.api.userHasPermission({ body: {
51
+ userId,
52
+ permissions,
53
+ ...role === void 0 ? {} : { role }
54
+ } })))).success) return {
55
+ effect: "deny",
56
+ reason: "MISSING_PERMISSION"
57
+ };
58
+ } catch (error) {
59
+ if ((0, better_auth_api.isAPIError)(error) && error.statusCode === 400 && error.body?.message === "user not found") return {
60
+ effect: "deny",
61
+ status: 401,
62
+ reason: "USER_NOT_FOUND"
63
+ };
64
+ throw error;
65
+ }
66
+ if (principal.delegation && !principal.delegation.allows(permissions)) return {
67
+ effect: "deny",
68
+ reason: "INSUFFICIENT_SCOPE"
69
+ };
70
+ return { effect: "allow" };
71
+ }
72
+ };
73
+ function permission(permissions, options = {}) {
74
+ return {
75
+ policy: adminPermissionPolicy,
76
+ params: { permissions },
77
+ ...options.principals ? { principals: options.principals } : {}
78
+ };
79
+ }
80
+ function RequirePermission(permissions, options) {
81
+ return require_auth_decorators.Require(permission(permissions, options));
82
+ }
83
+ //#endregion
84
+ exports.RequirePermission = RequirePermission;
85
+ exports.adminPermissionPolicy = adminPermissionPolicy;
86
+ exports.permission = permission;
87
+
88
+ //# sourceMappingURL=admin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.cjs","names":["BetterAuthConfigurationError","isAPIError","Require"],"sources":["../src/admin.ts"],"sourcesContent":["import { isAPIError } from \"better-auth/api\";\nimport type { AuthorizationPolicy, Requirement } from \"./auth-contracts.js\";\nimport { Require } from \"./auth-decorators.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport type {\n AdminPermissions,\n AuthPrincipal,\n PrincipalKind,\n} from \"./auth-types.js\";\nexport type { AdminPermissions } from \"./auth-types.js\";\nexport interface PermissionOptions {\n principals?: readonly PrincipalKind[];\n}\nexport interface AdminPermissionParams {\n readonly permissions: AdminPermissions;\n}\ninterface AdminApi {\n userHasPermission(input: {\n body: {\n userId: string;\n role?: string;\n permissions: Readonly<Record<string, readonly string[]>>;\n };\n }): Promise<{ success: boolean }>;\n}\nexport const adminPermissionPolicy: AuthorizationPolicy<\n AdminPermissionParams,\n AuthPrincipal\n> = {\n id: \"better-auth:admin/permission\",\n requires: {\n plugins: [\"admin\"],\n principals: [\"session\"],\n freshIdentity: true,\n hostlessCalls: true,\n },\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 \"Admin permissions must contain a resource and actions.\",\n );\n }\n },\n async evaluate({ permissions }, context) {\n const { principal } = context;\n if (!principal.userId) {\n return { effect: \"deny\", reason: \"USER_REQUIRED\" };\n }\n const userId = principal.userId;\n let role: string | undefined;\n if (principal.delegation) {\n const owner = await context.memo(\n JSON.stringify([\"admin:owner\", userId]),\n async () =>\n (await context.auth.context()).internalAdapter.findUserById(userId),\n );\n if (!owner) {\n return { effect: \"deny\", status: 401, reason: \"USER_NOT_FOUND\" };\n }\n if (\n owner.banned &&\n (!owner.banExpires || new Date(owner.banExpires).getTime() > Date.now())\n ) {\n return { effect: \"deny\", status: 401, reason: \"USER_BANNED\" };\n }\n if (typeof owner.role === \"string\" && owner.role.length) {\n role = owner.role;\n }\n }\n const stablePermissions = Object.entries(permissions)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([resource, actions]) => [resource, [...actions].sort()]);\n try {\n const result = await context.memo(\n JSON.stringify([\n \"admin:userHasPermission\",\n userId,\n role ?? null,\n stablePermissions,\n ]),\n () =>\n context.auth.run(\n {\n cookies: context.cookies,\n inbound: () => context.headers,\n internal: true,\n },\n () =>\n (context.auth.api as unknown as AdminApi).userHasPermission({\n body: {\n userId,\n permissions,\n ...(role === undefined ? {} : { role }),\n },\n }),\n ),\n );\n if (!result.success) {\n return { effect: \"deny\", reason: \"MISSING_PERMISSION\" };\n }\n } catch (error) {\n if (\n isAPIError(error) &&\n error.statusCode === 400 &&\n error.body?.message === \"user not found\"\n ) {\n return { effect: \"deny\", status: 401, reason: \"USER_NOT_FOUND\" };\n }\n throw error;\n }\n if (principal.delegation && !principal.delegation.allows(permissions)) {\n return { effect: \"deny\", reason: \"INSUFFICIENT_SCOPE\" };\n }\n return { effect: \"allow\" };\n },\n};\nexport function permission<N extends string = \"default\">(\n permissions: AdminPermissions<N>,\n options: PermissionOptions = {},\n): Requirement<AdminPermissionParams> {\n return {\n policy: adminPermissionPolicy,\n params: { permissions: permissions as AdminPermissions },\n ...(options.principals ? { principals: options.principals } : {}),\n };\n}\nexport function RequirePermission<N extends string = \"default\">(\n permissions: AdminPermissions<N>,\n options?: PermissionOptions,\n): ClassDecorator & MethodDecorator {\n return Require(permission<N>(permissions, options));\n}\n"],"mappings":";;;;;AAyBA,MAAa,wBAGT;CACF,IAAI;CACJ,UAAU;EACR,SAAS,CAAC,OAAO;EACjB,YAAY,CAAC,SAAS;EACtB,eAAe;EACf,eAAe;CACjB;CACA,SAAS,EAAE,eAAe;EACxB,IACE,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC,UAC1B,OAAO,OAAO,WAAW,CAAC,CAAC,MACxB,YAAY,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,MACnD,GAEA,MAAM,IAAIA,oBAAAA,6BACR,qBACA,wDACF;CAEJ;CACA,MAAM,SAAS,EAAE,eAAe,SAAS;EACvC,MAAM,EAAE,cAAc;EACtB,IAAI,CAAC,UAAU,QACb,OAAO;GAAE,QAAQ;GAAQ,QAAQ;EAAgB;EAEnD,MAAM,SAAS,UAAU;EACzB,IAAI;EACJ,IAAI,UAAU,YAAY;GACxB,MAAM,QAAQ,MAAM,QAAQ,KAC1B,KAAK,UAAU,CAAC,eAAe,MAAM,CAAC,GACtC,aACG,MAAM,QAAQ,KAAK,QAAQ,EAAA,CAAG,gBAAgB,aAAa,MAAM,CACtE;GACA,IAAI,CAAC,OACH,OAAO;IAAE,QAAQ;IAAQ,QAAQ;IAAK,QAAQ;GAAiB;GAEjE,IACE,MAAM,WACL,CAAC,MAAM,cAAc,IAAI,KAAK,MAAM,UAAU,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI,IAEtE,OAAO;IAAE,QAAQ;IAAQ,QAAQ;IAAK,QAAQ;GAAc;GAE9D,IAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,QAC/C,OAAO,MAAM;EAEjB;EACA,MAAM,oBAAoB,OAAO,QAAQ,WAAW,CAAC,CAClD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,CACtC,KAAK,CAAC,UAAU,aAAa,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;EAC/D,IAAI;GAyBF,IAAI,EAAC,MAxBgB,QAAQ,KAC3B,KAAK,UAAU;IACb;IACA;IACA,QAAQ;IACR;GACF,CAAC,SAEC,QAAQ,KAAK,IACX;IACE,SAAS,QAAQ;IACjB,eAAe,QAAQ;IACvB,UAAU;GACZ,SAEG,QAAQ,KAAK,IAA4B,kBAAkB,EAC1D,MAAM;IACJ;IACA;IACA,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;GACvC,EACF,CAAC,CACL,CACJ,EAAA,CACY,SACV,OAAO;IAAE,QAAQ;IAAQ,QAAQ;GAAqB;EAE1D,SAAS,OAAO;GACd,KAAA,GACEC,gBAAAA,WAAAA,CAAW,KAAK,KAChB,MAAM,eAAe,OACrB,MAAM,MAAM,YAAY,kBAExB,OAAO;IAAE,QAAQ;IAAQ,QAAQ;IAAK,QAAQ;GAAiB;GAEjE,MAAM;EACR;EACA,IAAI,UAAU,cAAc,CAAC,UAAU,WAAW,OAAO,WAAW,GAClE,OAAO;GAAE,QAAQ;GAAQ,QAAQ;EAAqB;EAExD,OAAO,EAAE,QAAQ,QAAQ;CAC3B;AACF;AACA,SAAgB,WACd,aACA,UAA6B,CAAC,GACM;CACpC,OAAO;EACL,QAAQ;EACR,QAAQ,EAAe,YAAgC;EACvD,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;CACjE;AACF;AACA,SAAgB,kBACd,aACA,SACkC;CAClC,OAAOC,wBAAAA,QAAQ,WAAc,aAAa,OAAO,CAAC;AACpD"}
@@ -0,0 +1,14 @@
1
+ import { Ct as AuthPrincipal, Nt as PrincipalKind, f as AuthorizationPolicy, ot as Requirement, vt as AdminPermissions } from "./auth-contracts-Brh_Qko9.cjs";
2
+ //#region src/admin.d.ts
3
+ export interface PermissionOptions {
4
+ principals?: readonly PrincipalKind[];
5
+ }
6
+ export interface AdminPermissionParams {
7
+ readonly permissions: AdminPermissions;
8
+ }
9
+ export declare const adminPermissionPolicy: AuthorizationPolicy<AdminPermissionParams, AuthPrincipal>;
10
+ export declare function permission<N extends string = "default">(permissions: AdminPermissions<N>, options?: PermissionOptions): Requirement<AdminPermissionParams>;
11
+ export declare function RequirePermission<N extends string = "default">(permissions: AdminPermissions<N>, options?: PermissionOptions): ClassDecorator & MethodDecorator;
12
+ //#endregion
13
+ export type { AdminPermissions };
14
+ //# sourceMappingURL=admin.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.cts","names":[],"sources":["../src/admin.ts"],"mappings":";;iBAUiB;EACf,sBAAsB;;iBAEP;WACN,aAAa;;qBAWX,uBAAuB,oBAClC,uBACA;wBA+Fc,WAAW,8BACzB,aAAa,iBAAiB,IAC9B,UAAS,oBACR,YAAY;wBAOC,kBAAkB,8BAChC,aAAa,iBAAiB,IAC9B,UAAU,oBACT,iBAAiB"}
@@ -0,0 +1,14 @@
1
+ import { Ct as AuthPrincipal, Nt as PrincipalKind, f as AuthorizationPolicy, ot as Requirement, vt as AdminPermissions } from "./auth-contracts-Cz7C4_Ch.mjs";
2
+ //#region src/admin.d.ts
3
+ export interface PermissionOptions {
4
+ principals?: readonly PrincipalKind[];
5
+ }
6
+ export interface AdminPermissionParams {
7
+ readonly permissions: AdminPermissions;
8
+ }
9
+ export declare const adminPermissionPolicy: AuthorizationPolicy<AdminPermissionParams, AuthPrincipal>;
10
+ export declare function permission<N extends string = "default">(permissions: AdminPermissions<N>, options?: PermissionOptions): Requirement<AdminPermissionParams>;
11
+ export declare function RequirePermission<N extends string = "default">(permissions: AdminPermissions<N>, options?: PermissionOptions): ClassDecorator & MethodDecorator;
12
+ //#endregion
13
+ export type { AdminPermissions };
14
+ //# sourceMappingURL=admin.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.mts","names":[],"sources":["../src/admin.ts"],"mappings":";;iBAUiB;EACf,sBAAsB;;iBAEP;WACN,aAAa;;qBAWX,uBAAuB,oBAClC,uBACA;wBA+Fc,WAAW,8BACzB,aAAa,iBAAiB,IAC9B,UAAS,oBACR,YAAY;wBAOC,kBAAkB,8BAChC,aAAa,iBAAiB,IAC9B,UAAU,oBACT,iBAAiB"}
package/dist/admin.mjs ADDED
@@ -0,0 +1,85 @@
1
+ import { n as BetterAuthConfigurationError } from "./auth-errors-C3rbTV9p.mjs";
2
+ import { u as Require } from "./auth-decorators-B1mFnGJ6.mjs";
3
+ import { isAPIError } from "better-auth/api";
4
+ //#region src/admin.ts
5
+ const adminPermissionPolicy = {
6
+ id: "better-auth:admin/permission",
7
+ requires: {
8
+ plugins: ["admin"],
9
+ principals: ["session"],
10
+ freshIdentity: true,
11
+ hostlessCalls: true
12
+ },
13
+ validate({ permissions }) {
14
+ if (!Object.keys(permissions).length || Object.values(permissions).some((actions) => !Array.isArray(actions) || !actions.length)) throw new BetterAuthConfigurationError("EMPTY_PERMISSIONS", "Admin permissions must contain a resource and actions.");
15
+ },
16
+ async evaluate({ permissions }, context) {
17
+ const { principal } = context;
18
+ if (!principal.userId) return {
19
+ effect: "deny",
20
+ reason: "USER_REQUIRED"
21
+ };
22
+ const userId = principal.userId;
23
+ let role;
24
+ if (principal.delegation) {
25
+ const owner = await context.memo(JSON.stringify(["admin:owner", userId]), async () => (await context.auth.context()).internalAdapter.findUserById(userId));
26
+ if (!owner) return {
27
+ effect: "deny",
28
+ status: 401,
29
+ reason: "USER_NOT_FOUND"
30
+ };
31
+ if (owner.banned && (!owner.banExpires || new Date(owner.banExpires).getTime() > Date.now())) return {
32
+ effect: "deny",
33
+ status: 401,
34
+ reason: "USER_BANNED"
35
+ };
36
+ if (typeof owner.role === "string" && owner.role.length) role = owner.role;
37
+ }
38
+ const stablePermissions = Object.entries(permissions).sort(([a], [b]) => a.localeCompare(b)).map(([resource, actions]) => [resource, [...actions].sort()]);
39
+ try {
40
+ if (!(await context.memo(JSON.stringify([
41
+ "admin:userHasPermission",
42
+ userId,
43
+ role ?? null,
44
+ stablePermissions
45
+ ]), () => context.auth.run({
46
+ cookies: context.cookies,
47
+ inbound: () => context.headers,
48
+ internal: true
49
+ }, () => context.auth.api.userHasPermission({ body: {
50
+ userId,
51
+ permissions,
52
+ ...role === void 0 ? {} : { role }
53
+ } })))).success) return {
54
+ effect: "deny",
55
+ reason: "MISSING_PERMISSION"
56
+ };
57
+ } catch (error) {
58
+ if (isAPIError(error) && error.statusCode === 400 && error.body?.message === "user not found") return {
59
+ effect: "deny",
60
+ status: 401,
61
+ reason: "USER_NOT_FOUND"
62
+ };
63
+ throw error;
64
+ }
65
+ if (principal.delegation && !principal.delegation.allows(permissions)) return {
66
+ effect: "deny",
67
+ reason: "INSUFFICIENT_SCOPE"
68
+ };
69
+ return { effect: "allow" };
70
+ }
71
+ };
72
+ function permission(permissions, options = {}) {
73
+ return {
74
+ policy: adminPermissionPolicy,
75
+ params: { permissions },
76
+ ...options.principals ? { principals: options.principals } : {}
77
+ };
78
+ }
79
+ function RequirePermission(permissions, options) {
80
+ return Require(permission(permissions, options));
81
+ }
82
+ //#endregion
83
+ export { RequirePermission, adminPermissionPolicy, permission };
84
+
85
+ //# sourceMappingURL=admin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.mjs","names":[],"sources":["../src/admin.ts"],"sourcesContent":["import { isAPIError } from \"better-auth/api\";\nimport type { AuthorizationPolicy, Requirement } from \"./auth-contracts.js\";\nimport { Require } from \"./auth-decorators.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport type {\n AdminPermissions,\n AuthPrincipal,\n PrincipalKind,\n} from \"./auth-types.js\";\nexport type { AdminPermissions } from \"./auth-types.js\";\nexport interface PermissionOptions {\n principals?: readonly PrincipalKind[];\n}\nexport interface AdminPermissionParams {\n readonly permissions: AdminPermissions;\n}\ninterface AdminApi {\n userHasPermission(input: {\n body: {\n userId: string;\n role?: string;\n permissions: Readonly<Record<string, readonly string[]>>;\n };\n }): Promise<{ success: boolean }>;\n}\nexport const adminPermissionPolicy: AuthorizationPolicy<\n AdminPermissionParams,\n AuthPrincipal\n> = {\n id: \"better-auth:admin/permission\",\n requires: {\n plugins: [\"admin\"],\n principals: [\"session\"],\n freshIdentity: true,\n hostlessCalls: true,\n },\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 \"Admin permissions must contain a resource and actions.\",\n );\n }\n },\n async evaluate({ permissions }, context) {\n const { principal } = context;\n if (!principal.userId) {\n return { effect: \"deny\", reason: \"USER_REQUIRED\" };\n }\n const userId = principal.userId;\n let role: string | undefined;\n if (principal.delegation) {\n const owner = await context.memo(\n JSON.stringify([\"admin:owner\", userId]),\n async () =>\n (await context.auth.context()).internalAdapter.findUserById(userId),\n );\n if (!owner) {\n return { effect: \"deny\", status: 401, reason: \"USER_NOT_FOUND\" };\n }\n if (\n owner.banned &&\n (!owner.banExpires || new Date(owner.banExpires).getTime() > Date.now())\n ) {\n return { effect: \"deny\", status: 401, reason: \"USER_BANNED\" };\n }\n if (typeof owner.role === \"string\" && owner.role.length) {\n role = owner.role;\n }\n }\n const stablePermissions = Object.entries(permissions)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([resource, actions]) => [resource, [...actions].sort()]);\n try {\n const result = await context.memo(\n JSON.stringify([\n \"admin:userHasPermission\",\n userId,\n role ?? null,\n stablePermissions,\n ]),\n () =>\n context.auth.run(\n {\n cookies: context.cookies,\n inbound: () => context.headers,\n internal: true,\n },\n () =>\n (context.auth.api as unknown as AdminApi).userHasPermission({\n body: {\n userId,\n permissions,\n ...(role === undefined ? {} : { role }),\n },\n }),\n ),\n );\n if (!result.success) {\n return { effect: \"deny\", reason: \"MISSING_PERMISSION\" };\n }\n } catch (error) {\n if (\n isAPIError(error) &&\n error.statusCode === 400 &&\n error.body?.message === \"user not found\"\n ) {\n return { effect: \"deny\", status: 401, reason: \"USER_NOT_FOUND\" };\n }\n throw error;\n }\n if (principal.delegation && !principal.delegation.allows(permissions)) {\n return { effect: \"deny\", reason: \"INSUFFICIENT_SCOPE\" };\n }\n return { effect: \"allow\" };\n },\n};\nexport function permission<N extends string = \"default\">(\n permissions: AdminPermissions<N>,\n options: PermissionOptions = {},\n): Requirement<AdminPermissionParams> {\n return {\n policy: adminPermissionPolicy,\n params: { permissions: permissions as AdminPermissions },\n ...(options.principals ? { principals: options.principals } : {}),\n };\n}\nexport function RequirePermission<N extends string = \"default\">(\n permissions: AdminPermissions<N>,\n options?: PermissionOptions,\n): ClassDecorator & MethodDecorator {\n return Require(permission<N>(permissions, options));\n}\n"],"mappings":";;;;AAyBA,MAAa,wBAGT;CACF,IAAI;CACJ,UAAU;EACR,SAAS,CAAC,OAAO;EACjB,YAAY,CAAC,SAAS;EACtB,eAAe;EACf,eAAe;CACjB;CACA,SAAS,EAAE,eAAe;EACxB,IACE,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC,UAC1B,OAAO,OAAO,WAAW,CAAC,CAAC,MACxB,YAAY,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,MACnD,GAEA,MAAM,IAAI,6BACR,qBACA,wDACF;CAEJ;CACA,MAAM,SAAS,EAAE,eAAe,SAAS;EACvC,MAAM,EAAE,cAAc;EACtB,IAAI,CAAC,UAAU,QACb,OAAO;GAAE,QAAQ;GAAQ,QAAQ;EAAgB;EAEnD,MAAM,SAAS,UAAU;EACzB,IAAI;EACJ,IAAI,UAAU,YAAY;GACxB,MAAM,QAAQ,MAAM,QAAQ,KAC1B,KAAK,UAAU,CAAC,eAAe,MAAM,CAAC,GACtC,aACG,MAAM,QAAQ,KAAK,QAAQ,EAAA,CAAG,gBAAgB,aAAa,MAAM,CACtE;GACA,IAAI,CAAC,OACH,OAAO;IAAE,QAAQ;IAAQ,QAAQ;IAAK,QAAQ;GAAiB;GAEjE,IACE,MAAM,WACL,CAAC,MAAM,cAAc,IAAI,KAAK,MAAM,UAAU,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI,IAEtE,OAAO;IAAE,QAAQ;IAAQ,QAAQ;IAAK,QAAQ;GAAc;GAE9D,IAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,QAC/C,OAAO,MAAM;EAEjB;EACA,MAAM,oBAAoB,OAAO,QAAQ,WAAW,CAAC,CAClD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,CACtC,KAAK,CAAC,UAAU,aAAa,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;EAC/D,IAAI;GAyBF,IAAI,EAAC,MAxBgB,QAAQ,KAC3B,KAAK,UAAU;IACb;IACA;IACA,QAAQ;IACR;GACF,CAAC,SAEC,QAAQ,KAAK,IACX;IACE,SAAS,QAAQ;IACjB,eAAe,QAAQ;IACvB,UAAU;GACZ,SAEG,QAAQ,KAAK,IAA4B,kBAAkB,EAC1D,MAAM;IACJ;IACA;IACA,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;GACvC,EACF,CAAC,CACL,CACJ,EAAA,CACY,SACV,OAAO;IAAE,QAAQ;IAAQ,QAAQ;GAAqB;EAE1D,SAAS,OAAO;GACd,IACE,WAAW,KAAK,KAChB,MAAM,eAAe,OACrB,MAAM,MAAM,YAAY,kBAExB,OAAO;IAAE,QAAQ;IAAQ,QAAQ;IAAK,QAAQ;GAAiB;GAEjE,MAAM;EACR;EACA,IAAI,UAAU,cAAc,CAAC,UAAU,WAAW,OAAO,WAAW,GAClE,OAAO;GAAE,QAAQ;GAAQ,QAAQ;EAAqB;EAExD,OAAO,EAAE,QAAQ,QAAQ;CAC3B;AACF;AACA,SAAgB,WACd,aACA,UAA6B,CAAC,GACM;CACpC,OAAO;EACL,QAAQ;EACR,QAAQ,EAAe,YAAgC;EACvD,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;CACjE;AACF;AACA,SAAgB,kBACd,aACA,SACkC;CAClC,OAAO,QAAQ,WAAc,aAAa,OAAO,CAAC;AACpD"}
@@ -0,0 +1,164 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_auth_errors = require("./auth-errors-DmiKl0L_.cjs");
3
+ const require_auth_scope_interceptor = require("./auth-scope-interceptor-BjxAL1-S.cjs");
4
+ const require_auth_decorators = require("./auth-decorators-Cu_nxWnF.cjs");
5
+ let better_auth_plugins_access = require("better-auth/plugins/access");
6
+ //#region src/api-key.ts
7
+ const API_KEY_PRINCIPAL_KIND = "api-key";
8
+ function apiKeyPrincipal(options = {}) {
9
+ const header = options.header ?? "x-api-key";
10
+ const slowMs = typeof options.outageProbe === "object" ? options.outageProbe.slowMs ?? 500 : 500;
11
+ if (!header.trim() || slowMs !== false && (!Number.isFinite(slowMs) || slowMs < 0)) throw new require_auth_errors.BetterAuthConfigurationError("INVALID_API_KEY_OPTIONS", "API-key header must be nonempty and slowMs must be finite and nonnegative.");
12
+ const verificationMemo = Symbol("api-key-verification");
13
+ const probes = /* @__PURE__ */ new WeakMap();
14
+ async function probe(auth) {
15
+ const previous = probes.get(auth.instance);
16
+ if (previous && (previous.pending || Date.now() - previous.at < 1e3)) return previous.result;
17
+ const result = (async () => {
18
+ const context = await auth.context();
19
+ const where = [{
20
+ field: "key",
21
+ value: crypto.randomUUID()
22
+ }];
23
+ await context.adapter.findOne({
24
+ model: "apikey",
25
+ where
26
+ });
27
+ await context.adapter.updateMany({
28
+ model: "apikey",
29
+ where,
30
+ update: { updatedAt: /* @__PURE__ */ new Date() }
31
+ });
32
+ })();
33
+ const state = {
34
+ at: Date.now(),
35
+ pending: true,
36
+ result
37
+ };
38
+ probes.set(auth.instance, state);
39
+ result.then(() => {
40
+ state.pending = false;
41
+ }, () => {
42
+ state.pending = false;
43
+ });
44
+ return result;
45
+ }
46
+ return {
47
+ id: "better-auth:api-key",
48
+ kinds: [API_KEY_PRINCIPAL_KIND],
49
+ acceptance: options.acceptance ?? "explicit",
50
+ delegates: true,
51
+ credentialHeaders: [header],
52
+ effects: {
53
+ consumesQuota: true,
54
+ writes: true
55
+ },
56
+ requires: { plugins: ["api-key"] },
57
+ appliesTo: (request) => request.headers.has(header),
58
+ async resolve(request) {
59
+ if (!request.headers.has(header)) return require_auth_scope_interceptor.absent();
60
+ return request.memo(verificationMemo, async () => {
61
+ const key = request.headers.get(header);
62
+ const headers = new Headers();
63
+ for (const name of [
64
+ "host",
65
+ "x-forwarded-host",
66
+ "x-forwarded-proto"
67
+ ]) {
68
+ const value = request.headers.get(name);
69
+ if (value !== null) headers.set(name, value);
70
+ }
71
+ const started = performance.now();
72
+ try {
73
+ const result = await request.auth.run({
74
+ cookies: request.cookies,
75
+ inbound: () => request.headers,
76
+ internal: true
77
+ }, () => request.auth.api.verifyApiKey({
78
+ body: {
79
+ key,
80
+ ...options.configId === void 0 ? {} : { configId: options.configId }
81
+ },
82
+ headers
83
+ }));
84
+ if (result.valid) {
85
+ const verified = result.key;
86
+ if (!verified || typeof verified.id !== "string" || typeof verified.referenceId !== "string") throw new require_auth_errors.BetterAuthInfrastructureError(/* @__PURE__ */ new Error("Malformed API-key verification result"));
87
+ const configId = verified.configId ?? null;
88
+ const references = typeof options.references === "function" ? options.references({ configId }) : options.references ?? "user";
89
+ const permissions = verified.permissions ?? null;
90
+ const grant = (0, better_auth_plugins_access.role)(permissions ?? {});
91
+ return require_auth_scope_interceptor.authenticated({
92
+ kind: API_KEY_PRINCIPAL_KIND,
93
+ source: "better-auth:api-key",
94
+ keyId: verified.id,
95
+ configId,
96
+ referenceId: verified.referenceId,
97
+ userId: references === "user" ? verified.referenceId : null,
98
+ organizationId: references === "organization" ? verified.referenceId : null,
99
+ permissions,
100
+ delegation: {
101
+ description: "api-key permissions",
102
+ allows: (requested) => grant.authorize(requested).success
103
+ }
104
+ });
105
+ }
106
+ const code = result.error?.code;
107
+ if (code === "RATE_LIMITED" || code === "USAGE_EXCEEDED") {
108
+ const retry = result.error?.details?.tryAgainIn;
109
+ return require_auth_scope_interceptor.rejected(require_auth_errors.AuthFailures.rejected({
110
+ status: 429,
111
+ reason: code,
112
+ ...code === "RATE_LIMITED" && typeof retry === "number" ? { retryAfterSeconds: retry / 1e3 } : {}
113
+ }));
114
+ }
115
+ if (code === "INVALID_API_KEY" && options.outageProbe !== false) {
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
+ await probe(request.auth);
118
+ }
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
+ status: 401,
121
+ reason: code
122
+ }));
123
+ throw new require_auth_errors.BetterAuthInfrastructureError(/* @__PURE__ */ new Error(`Unexpected API-key verification error: ${code ?? "missing code"}`));
124
+ } catch (error) {
125
+ const failure = require_auth_errors.normalizeThrown(error, "source", "better-auth:api-key", [key]);
126
+ if ("sessionLoss" in failure) throw new require_auth_errors.BetterAuthInfrastructureError(error, { secrets: [key] });
127
+ return require_auth_scope_interceptor.rejected(failure);
128
+ }
129
+ });
130
+ }
131
+ };
132
+ }
133
+ const apiKeyPermissionPolicy = {
134
+ id: "better-auth:api-key/permission",
135
+ requires: {
136
+ plugins: ["api-key"],
137
+ principals: [API_KEY_PRINCIPAL_KIND]
138
+ },
139
+ validate(permissions) {
140
+ if (!Object.keys(permissions).length || Object.values(permissions).some((actions) => !Array.isArray(actions) || !actions.length)) throw new require_auth_errors.BetterAuthConfigurationError("EMPTY_PERMISSIONS", "API-key permissions must contain a resource and actions.");
141
+ },
142
+ evaluate(permissions, context) {
143
+ return context.principal.delegation.allows(permissions) ? { effect: "allow" } : {
144
+ effect: "deny",
145
+ reason: "MISSING_PERMISSION"
146
+ };
147
+ }
148
+ };
149
+ function apiKeyPermission(permissions) {
150
+ return {
151
+ policy: apiKeyPermissionPolicy,
152
+ params: permissions
153
+ };
154
+ }
155
+ function RequireApiKeyPermission(permissions) {
156
+ return require_auth_decorators.Require(apiKeyPermission(permissions));
157
+ }
158
+ //#endregion
159
+ exports.API_KEY_PRINCIPAL_KIND = API_KEY_PRINCIPAL_KIND;
160
+ exports.RequireApiKeyPermission = RequireApiKeyPermission;
161
+ exports.apiKeyPermission = apiKeyPermission;
162
+ exports.apiKeyPrincipal = apiKeyPrincipal;
163
+
164
+ //# sourceMappingURL=api-key.cjs.map
@@ -0,0 +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-kit/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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"}
@@ -0,0 +1,42 @@
1
+ import { $ as PrincipalDelegation, it as PrincipalSource, o as AuthPrincipalBase, ot as Requirement } from "./auth-contracts-Brh_Qko9.cjs";
2
+ //#region src/api-key.d.ts
3
+ export declare const API_KEY_PRINCIPAL_KIND: "api-key";
4
+ export interface ApiKeyPrincipal extends AuthPrincipalBase {
5
+ readonly kind: "api-key";
6
+ readonly keyId: string;
7
+ readonly configId: string | null;
8
+ readonly referenceId: string;
9
+ readonly userId: string | null;
10
+ readonly organizationId: string | null;
11
+ readonly permissions: Readonly<Record<string, readonly string[]>> | null;
12
+ readonly delegation: PrincipalDelegation;
13
+ }
14
+ declare module "@nestjs-kit/better-auth" {
15
+ interface PrincipalKinds {
16
+ "api-key": ApiKeyPrincipal;
17
+ }
18
+ }
19
+ export interface ApiKeyPrincipalOptions {
20
+ header?: string;
21
+ configId?: string;
22
+ references?: "user" | "organization" | ((key: {
23
+ readonly configId: string | null;
24
+ }) => "user" | "organization");
25
+ /**
26
+ * Default true: slow INVALID_API_KEY results (>500 ms) throw infrastructure errors,
27
+ * then fast failures share one read/no-op-write probe per second per instance.
28
+ * false disables both checks; { slowMs: false } keeps only the probe.
29
+ * Set slowMs below pool lock/statement timeouts and above custom hashing costs.
30
+ * Fast failures confined to one row, or secondary storage without database fallback,
31
+ * may still look like invalid keys. A statement-level trigger can observe the zero-row write.
32
+ */
33
+ outageProbe?: boolean | {
34
+ readonly slowMs?: number | false;
35
+ };
36
+ acceptance?: "default" | "explicit";
37
+ }
38
+ export declare function apiKeyPrincipal(options?: ApiKeyPrincipalOptions): PrincipalSource<ApiKeyPrincipal>;
39
+ export declare function apiKeyPermission(permissions: Record<string, string[]>): Requirement<Record<string, string[]>>;
40
+ export declare function RequireApiKeyPermission(permissions: Record<string, string[]>): ClassDecorator & MethodDecorator;
41
+ //#endregion
42
+ //# sourceMappingURL=api-key.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key.d.cts","names":[],"sources":["../src/api-key.ts"],"mappings":";;qBAkBa;iBACI,wBAAwB;WAC9B;WACA;WACA;WACA;WACA;WACA;WACA,aAAa,SAAS;WACtB,YAAY;;;YAIX;IACR,WAAW;;;iBAGE;EACf;EACA;EACA,yCAGM;aAAgB;;;;;;;;;;EAStB;aAAmC;;EACnC;;wBAmBc,gBACd,UAAS,yBACR,gBAAgB;wBAwMH,iBACd,aAAa,2BACZ,YAAY;wBAGC,wBACd,aAAa,2BACZ,iBAAiB"}
@@ -0,0 +1,42 @@
1
+ import { $ as PrincipalDelegation, it as PrincipalSource, o as AuthPrincipalBase, ot as Requirement } from "./auth-contracts-Cz7C4_Ch.mjs";
2
+ //#region src/api-key.d.ts
3
+ export declare const API_KEY_PRINCIPAL_KIND: "api-key";
4
+ export interface ApiKeyPrincipal extends AuthPrincipalBase {
5
+ readonly kind: "api-key";
6
+ readonly keyId: string;
7
+ readonly configId: string | null;
8
+ readonly referenceId: string;
9
+ readonly userId: string | null;
10
+ readonly organizationId: string | null;
11
+ readonly permissions: Readonly<Record<string, readonly string[]>> | null;
12
+ readonly delegation: PrincipalDelegation;
13
+ }
14
+ declare module "@nestjs-kit/better-auth" {
15
+ interface PrincipalKinds {
16
+ "api-key": ApiKeyPrincipal;
17
+ }
18
+ }
19
+ export interface ApiKeyPrincipalOptions {
20
+ header?: string;
21
+ configId?: string;
22
+ references?: "user" | "organization" | ((key: {
23
+ readonly configId: string | null;
24
+ }) => "user" | "organization");
25
+ /**
26
+ * Default true: slow INVALID_API_KEY results (>500 ms) throw infrastructure errors,
27
+ * then fast failures share one read/no-op-write probe per second per instance.
28
+ * false disables both checks; { slowMs: false } keeps only the probe.
29
+ * Set slowMs below pool lock/statement timeouts and above custom hashing costs.
30
+ * Fast failures confined to one row, or secondary storage without database fallback,
31
+ * may still look like invalid keys. A statement-level trigger can observe the zero-row write.
32
+ */
33
+ outageProbe?: boolean | {
34
+ readonly slowMs?: number | false;
35
+ };
36
+ acceptance?: "default" | "explicit";
37
+ }
38
+ export declare function apiKeyPrincipal(options?: ApiKeyPrincipalOptions): PrincipalSource<ApiKeyPrincipal>;
39
+ export declare function apiKeyPermission(permissions: Record<string, string[]>): Requirement<Record<string, string[]>>;
40
+ export declare function RequireApiKeyPermission(permissions: Record<string, string[]>): ClassDecorator & MethodDecorator;
41
+ //#endregion
42
+ //# sourceMappingURL=api-key.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key.d.mts","names":[],"sources":["../src/api-key.ts"],"mappings":";;qBAkBa;iBACI,wBAAwB;WAC9B;WACA;WACA;WACA;WACA;WACA;WACA,aAAa,SAAS;WACtB,YAAY;;;YAIX;IACR,WAAW;;;iBAGE;EACf;EACA;EACA,yCAGM;aAAgB;;;;;;;;;;EAStB;aAAmC;;EACnC;;wBAmBc,gBACd,UAAS,yBACR,gBAAgB;wBAwMH,iBACd,aAAa,2BACZ,YAAY;wBAGC,wBACd,aAAa,2BACZ,iBAAiB"}