nestjs-slightly-better-auth 1.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 (77) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +57 -0
  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 +163 -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 +159 -0
  18. package/dist/api-key.mjs.map +1 -0
  19. package/dist/auth-contracts-BBA1C1gj.d.cts +1635 -0
  20. package/dist/auth-contracts-BBA1C1gj.d.cts.map +1 -0
  21. package/dist/auth-contracts-tqsfBZ8B.d.mts +1635 -0
  22. package/dist/auth-contracts-tqsfBZ8B.d.mts.map +1 -0
  23. package/dist/auth-decorators-CwW5JY4y.cjs +1274 -0
  24. package/dist/auth-decorators-CwW5JY4y.cjs.map +1 -0
  25. package/dist/auth-decorators-DfAJbHar.mjs +849 -0
  26. package/dist/auth-decorators-DfAJbHar.mjs.map +1 -0
  27. package/dist/auth-errors-CQjfgjaO.mjs +283 -0
  28. package/dist/auth-errors-CQjfgjaO.mjs.map +1 -0
  29. package/dist/auth-errors-CsERBNWI.cjs +348 -0
  30. package/dist/auth-errors-CsERBNWI.cjs.map +1 -0
  31. package/dist/express.cjs +247 -0
  32. package/dist/express.cjs.map +1 -0
  33. package/dist/express.d.cts +37 -0
  34. package/dist/express.d.cts.map +1 -0
  35. package/dist/express.d.mts +37 -0
  36. package/dist/express.d.mts.map +1 -0
  37. package/dist/express.mjs +245 -0
  38. package/dist/express.mjs.map +1 -0
  39. package/dist/fastify.cjs +215 -0
  40. package/dist/fastify.cjs.map +1 -0
  41. package/dist/fastify.d.cts +28 -0
  42. package/dist/fastify.d.cts.map +1 -0
  43. package/dist/fastify.d.mts +28 -0
  44. package/dist/fastify.d.mts.map +1 -0
  45. package/dist/fastify.mjs +213 -0
  46. package/dist/fastify.mjs.map +1 -0
  47. package/dist/index.cjs +2634 -0
  48. package/dist/index.cjs.map +1 -0
  49. package/dist/index.d.cts +2 -0
  50. package/dist/index.d.mts +2 -0
  51. package/dist/index.mjs +2556 -0
  52. package/dist/index.mjs.map +1 -0
  53. package/dist/organization.cjs +191 -0
  54. package/dist/organization.cjs.map +1 -0
  55. package/dist/organization.d.cts +40 -0
  56. package/dist/organization.d.cts.map +1 -0
  57. package/dist/organization.d.mts +40 -0
  58. package/dist/organization.d.mts.map +1 -0
  59. package/dist/organization.mjs +177 -0
  60. package/dist/organization.mjs.map +1 -0
  61. package/dist/platform.cjs +393 -0
  62. package/dist/platform.cjs.map +1 -0
  63. package/dist/platform.d.cts +43 -0
  64. package/dist/platform.d.cts.map +1 -0
  65. package/dist/platform.d.mts +43 -0
  66. package/dist/platform.d.mts.map +1 -0
  67. package/dist/platform.mjs +385 -0
  68. package/dist/platform.mjs.map +1 -0
  69. package/dist/plugin.cjs +312 -0
  70. package/dist/plugin.cjs.map +1 -0
  71. package/dist/plugin.d.cts +26 -0
  72. package/dist/plugin.d.cts.map +1 -0
  73. package/dist/plugin.d.mts +26 -0
  74. package/dist/plugin.d.mts.map +1 -0
  75. package/dist/plugin.mjs +310 -0
  76. package/dist/plugin.mjs.map +1 -0
  77. package/package.json +186 -0
@@ -0,0 +1,191 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_auth_errors = require("./auth-errors-CsERBNWI.cjs");
3
+ const require_auth_decorators = require("./auth-decorators-CwW5JY4y.cjs");
4
+ let better_auth_api = require("better-auth/api");
5
+ //#region src/organization.ts
6
+ const ACTIVE_ORGANIZATION_ID = Symbol.for("nestjs-slightly-better-auth:org:active-organization-id");
7
+ const ACTIVE_MEMBER_ROLE = Symbol.for("nestjs-slightly-better-auth:org:active-member-role");
8
+ const ActiveOrganizationId = require_auth_decorators.defineInvocationParam(ACTIVE_ORGANIZATION_ID, { missing: "ORGANIZATION_REQUIRED" });
9
+ const ActiveMemberRole = require_auth_decorators.defineInvocationParam(ACTIVE_MEMBER_ROLE, { missing: "MEMBER_ROLE_REQUIRED" });
10
+ const REF = Symbol.for("nestjs-slightly-better-auth:org:ref");
11
+ function organizationRef(resolve, options = {}) {
12
+ return Object.assign((ctx) => resolve(ctx), options);
13
+ }
14
+ function activeOrganization() {
15
+ return Object.assign(organizationRef(async (ctx) => {
16
+ const value = ctx.principal.session.session?.activeOrganizationId;
17
+ if (value !== void 0) return value;
18
+ if (!await ctx.auth.hasPlugin("custom-session")) return;
19
+ try {
20
+ return (await ctx.memo(JSON.stringify(["org:activeMember"]), () => ctx.auth.run({
21
+ cookies: ctx.cookies,
22
+ inbound: () => ctx.headers,
23
+ internal: true
24
+ }, () => ctx.auth.api.getActiveMember({ headers: ctx.headers }))))?.organizationId;
25
+ } catch (error) {
26
+ if ((0, better_auth_api.isAPIError)(error) && error.statusCode === 400 && (error.body?.code === "NO_ACTIVE_ORGANIZATION" || error.body?.code === "MEMBER_NOT_FOUND")) return;
27
+ throw error;
28
+ }
29
+ }, { missingReason: "NO_ACTIVE_ORGANIZATION" }), { [REF]: { kind: "active" } });
30
+ }
31
+ function fromParam(name) {
32
+ return Object.assign(organizationRef((ctx) => ctx.param(name)), { [REF]: {
33
+ kind: "param",
34
+ name
35
+ } });
36
+ }
37
+ function fromHeader(name) {
38
+ return organizationRef((ctx) => ctx.headers.get(name));
39
+ }
40
+ function requirements(expressions) {
41
+ return expressions.flatMap((expression) => "anyOf" in expression ? requirements(expression.anyOf) : "allOf" in expression ? requirements(expression.allOf) : [expression]);
42
+ }
43
+ function advise(ctx, policy) {
44
+ const advice = [];
45
+ for (const handler of ctx.handlers) for (const requirement of requirements(handler.plan.requirements)) {
46
+ if (requirement.policy !== policy) continue;
47
+ const marker = requirement.params.organization[REF];
48
+ if (marker?.kind === "active") {
49
+ const input = handler.inputs.find((name) => [
50
+ "orgId",
51
+ "organizationId",
52
+ "organization",
53
+ "orgSlug"
54
+ ].includes(name));
55
+ if (input) advice.push({
56
+ level: "warn",
57
+ code: "W_ORG_PARAM_IGNORED",
58
+ message: `${handler.plan.site} checks the active organization but accepts ${input}.`,
59
+ hint: input === "orgSlug" ? "Map the slug to an ID with organizationRef and auth.api.listOrganizations({ headers })." : `Use fromParam('${input}').`
60
+ });
61
+ } else if (marker?.kind === "param" && !handler.inputs.includes(marker.name)) advice.push({
62
+ level: "warn",
63
+ code: "W_ORG_PARAM_MISSING",
64
+ message: `${handler.plan.site} reads ${marker.name}, absent from claimed inputs: ${handler.inputs.join(", ") || "none"}.`,
65
+ hint: "Use the correct input name, a method-level organizationRef, or SkipDefaultRequirements with a method-level requirement."
66
+ });
67
+ }
68
+ return advice;
69
+ }
70
+ const requires = {
71
+ plugins: ["organization"],
72
+ principals: ["session"],
73
+ presentsCredentials: true
74
+ };
75
+ const orgPermissionPolicy = {
76
+ id: "better-auth:organization/permission",
77
+ requires,
78
+ advise: (ctx) => advise(ctx, orgPermissionPolicy),
79
+ validate({ permissions }) {
80
+ if (!Object.keys(permissions).length || Object.values(permissions).some((actions) => !Array.isArray(actions) || !actions.length)) throw new require_auth_errors.BetterAuthConfigurationError("EMPTY_PERMISSIONS", "Organization permissions must contain a resource and actions.");
81
+ },
82
+ async evaluate({ permissions, organization }, context) {
83
+ const organizationId = await organization(context);
84
+ if (typeof organizationId !== "string" || !organizationId.trim()) return {
85
+ effect: "deny",
86
+ reason: organization.missingReason ?? "ORGANIZATION_REQUIRED"
87
+ };
88
+ const stable = Object.entries(permissions).sort(([a], [b]) => a.localeCompare(b)).map(([resource, actions]) => [resource, [...actions].sort()]);
89
+ try {
90
+ if (!(await context.memo(JSON.stringify([
91
+ "org:hasPermission",
92
+ organizationId,
93
+ stable
94
+ ]), () => context.auth.run({
95
+ cookies: context.cookies,
96
+ inbound: () => context.headers,
97
+ internal: true
98
+ }, () => context.auth.api.hasPermission({
99
+ headers: context.headers,
100
+ body: {
101
+ organizationId,
102
+ permissions
103
+ }
104
+ })))).success) return {
105
+ effect: "deny",
106
+ reason: "MISSING_PERMISSION"
107
+ };
108
+ context.provide(ACTIVE_ORGANIZATION_ID, organizationId);
109
+ return { effect: "allow" };
110
+ } catch (error) {
111
+ if ((0, better_auth_api.isAPIError)(error) && error.body?.code === "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION") return {
112
+ effect: "deny",
113
+ reason: "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION"
114
+ };
115
+ throw error;
116
+ }
117
+ }
118
+ };
119
+ const orgMemberPolicy = {
120
+ id: "better-auth:organization/member",
121
+ requires,
122
+ advise: (ctx) => advise(ctx, orgMemberPolicy),
123
+ async evaluate({ organization }, context) {
124
+ const organizationId = await organization(context);
125
+ if (typeof organizationId !== "string" || !organizationId.trim()) return {
126
+ effect: "deny",
127
+ reason: organization.missingReason ?? "ORGANIZATION_REQUIRED"
128
+ };
129
+ try {
130
+ const result = await context.memo(JSON.stringify(["org:activeMemberRole", organizationId]), () => context.auth.run({
131
+ cookies: context.cookies,
132
+ inbound: () => context.headers,
133
+ internal: true
134
+ }, () => context.auth.api.getActiveMemberRole({
135
+ headers: context.headers,
136
+ query: { organizationId }
137
+ })));
138
+ if (typeof result.role !== "string" || !result.role.trim()) return {
139
+ effect: "deny",
140
+ reason: "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION"
141
+ };
142
+ context.provide(ACTIVE_ORGANIZATION_ID, organizationId);
143
+ context.provide(ACTIVE_MEMBER_ROLE, result.role);
144
+ return { effect: "allow" };
145
+ } catch (error) {
146
+ if ((0, better_auth_api.isAPIError)(error) && error.body?.code === "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION") return {
147
+ effect: "deny",
148
+ reason: "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION"
149
+ };
150
+ throw error;
151
+ }
152
+ }
153
+ };
154
+ function orgPermission(permissions, options = {}) {
155
+ return {
156
+ policy: orgPermissionPolicy,
157
+ params: {
158
+ permissions,
159
+ organization: options.organization ?? activeOrganization()
160
+ }
161
+ };
162
+ }
163
+ function RequireOrgPermission(permissions, options) {
164
+ return require_auth_decorators.Require(orgPermission(permissions, options));
165
+ }
166
+ function orgMember(options = {}) {
167
+ return {
168
+ policy: orgMemberPolicy,
169
+ params: { organization: options.organization ?? activeOrganization() }
170
+ };
171
+ }
172
+ function RequireOrgMember(options) {
173
+ return require_auth_decorators.Require(orgMember(options));
174
+ }
175
+ //#endregion
176
+ exports.ACTIVE_MEMBER_ROLE = ACTIVE_MEMBER_ROLE;
177
+ exports.ACTIVE_ORGANIZATION_ID = ACTIVE_ORGANIZATION_ID;
178
+ exports.ActiveMemberRole = ActiveMemberRole;
179
+ exports.ActiveOrganizationId = ActiveOrganizationId;
180
+ exports.RequireOrgMember = RequireOrgMember;
181
+ exports.RequireOrgPermission = RequireOrgPermission;
182
+ exports.activeOrganization = activeOrganization;
183
+ exports.fromHeader = fromHeader;
184
+ exports.fromParam = fromParam;
185
+ exports.orgMember = orgMember;
186
+ exports.orgMemberPolicy = orgMemberPolicy;
187
+ exports.orgPermission = orgPermission;
188
+ exports.orgPermissionPolicy = orgPermissionPolicy;
189
+ exports.organizationRef = organizationRef;
190
+
191
+ //# sourceMappingURL=organization.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.cjs","names":["defineInvocationParam","isAPIError","BetterAuthConfigurationError","Require"],"sources":["../src/organization.ts"],"sourcesContent":["import { isAPIError } from \"better-auth/api\";\nimport type {\n AuthorizationContext,\n AuthorizationPolicy,\n BootAdvice,\n BootAdviceContext,\n Requirement,\n RequirementExpr,\n} from \"./auth-contracts.js\";\nimport { defineInvocationParam, Require } from \"./auth-decorators.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport type { OrgPermissions, SessionPrincipal } from \"./auth-types.js\";\nexport type { OrgPermissions } from \"./auth-types.js\";\nexport interface OrganizationRef {\n (ctx: AuthorizationContext): unknown | Promise<unknown>;\n readonly missingReason?: string;\n}\nexport interface OrgPermissionParams {\n readonly permissions: OrgPermissions;\n readonly organization: OrganizationRef;\n}\nexport interface OrgMemberParams {\n readonly organization: OrganizationRef;\n}\nexport const ACTIVE_ORGANIZATION_ID = Symbol.for(\n \"nestjs-slightly-better-auth:org:active-organization-id\",\n);\nexport const ACTIVE_MEMBER_ROLE = Symbol.for(\n \"nestjs-slightly-better-auth:org:active-member-role\",\n);\nexport const ActiveOrganizationId = defineInvocationParam(\n ACTIVE_ORGANIZATION_ID,\n { missing: \"ORGANIZATION_REQUIRED\" },\n);\nexport const ActiveMemberRole = defineInvocationParam(ACTIVE_MEMBER_ROLE, {\n missing: \"MEMBER_ROLE_REQUIRED\",\n});\nconst REF = Symbol.for(\"nestjs-slightly-better-auth:org:ref\");\ntype MarkedRef = OrganizationRef & {\n readonly [REF]?: { kind: \"active\" } | { kind: \"param\"; name: string };\n};\ninterface OrgApi {\n hasPermission(input: {\n headers: Headers;\n body: { organizationId: string; permissions: OrgPermissions };\n }): Promise<{ success: boolean }>;\n getActiveMemberRole(input: {\n headers: Headers;\n query: { organizationId: string };\n }): Promise<{ role?: string | null }>;\n getActiveMember(input: {\n headers: Headers;\n }): Promise<{ organizationId?: string }>;\n}\nexport function organizationRef(\n resolve: (ctx: AuthorizationContext) => unknown | Promise<unknown>,\n options: { missingReason?: string } = {},\n): OrganizationRef {\n return Object.assign((ctx: AuthorizationContext) => resolve(ctx), options);\n}\nexport function activeOrganization(): OrganizationRef {\n return Object.assign(\n organizationRef(\n async (ctx) => {\n const session = (ctx.principal as SessionPrincipal).session as {\n session?: { activeOrganizationId?: unknown };\n };\n const value = session.session?.activeOrganizationId;\n if (value !== undefined) {\n return value;\n }\n if (!(await ctx.auth.hasPlugin(\"custom-session\"))) {\n return undefined;\n }\n try {\n const member = await ctx.memo(\n JSON.stringify([\"org:activeMember\"]),\n () =>\n ctx.auth.run(\n {\n cookies: ctx.cookies,\n inbound: () => ctx.headers,\n internal: true,\n },\n () =>\n (ctx.auth.api as unknown as OrgApi).getActiveMember({\n headers: ctx.headers,\n }),\n ),\n );\n return member?.organizationId;\n } catch (error) {\n if (\n isAPIError(error) &&\n error.statusCode === 400 &&\n (error.body?.code === \"NO_ACTIVE_ORGANIZATION\" ||\n error.body?.code === \"MEMBER_NOT_FOUND\")\n ) {\n return undefined;\n }\n throw error;\n }\n },\n { missingReason: \"NO_ACTIVE_ORGANIZATION\" },\n ),\n { [REF]: { kind: \"active\" as const } },\n );\n}\nexport function fromParam(name: string): OrganizationRef {\n return Object.assign(\n organizationRef((ctx) => ctx.param(name)),\n { [REF]: { kind: \"param\" as const, name } },\n );\n}\nexport function fromHeader(name: string): OrganizationRef {\n return organizationRef((ctx) => ctx.headers.get(name));\n}\nfunction requirements(expressions: readonly RequirementExpr[]): Requirement[] {\n return expressions.flatMap((expression) =>\n \"anyOf\" in expression\n ? requirements(expression.anyOf)\n : \"allOf\" in expression\n ? requirements(expression.allOf)\n : [expression],\n );\n}\nfunction advise(\n ctx: BootAdviceContext,\n policy: AuthorizationPolicy<any, any>,\n): BootAdvice[] {\n const advice: BootAdvice[] = [];\n for (const handler of ctx.handlers) {\n for (const requirement of requirements(handler.plan.requirements)) {\n if (requirement.policy !== policy) {\n continue;\n }\n const ref = (requirement.params as OrgMemberParams)\n .organization as MarkedRef;\n const marker = ref[REF];\n if (marker?.kind === \"active\") {\n const input = handler.inputs.find((name) =>\n [\"orgId\", \"organizationId\", \"organization\", \"orgSlug\"].includes(name),\n );\n if (input) {\n advice.push({\n level: \"warn\",\n code: \"W_ORG_PARAM_IGNORED\",\n message: `${handler.plan.site} checks the active organization but accepts ${input}.`,\n hint:\n input === \"orgSlug\"\n ? \"Map the slug to an ID with organizationRef and auth.api.listOrganizations({ headers }).\"\n : `Use fromParam('${input}').`,\n });\n }\n } else if (\n marker?.kind === \"param\" &&\n !handler.inputs.includes(marker.name)\n ) {\n advice.push({\n level: \"warn\",\n code: \"W_ORG_PARAM_MISSING\",\n message: `${handler.plan.site} reads ${marker.name}, absent from claimed inputs: ${handler.inputs.join(\", \") || \"none\"}.`,\n hint: \"Use the correct input name, a method-level organizationRef, or SkipDefaultRequirements with a method-level requirement.\",\n });\n }\n }\n }\n return advice;\n}\nconst requires = {\n plugins: [\"organization\"],\n principals: [\"session\" as const],\n presentsCredentials: true,\n};\nexport const orgPermissionPolicy: AuthorizationPolicy<\n OrgPermissionParams,\n SessionPrincipal\n> = {\n id: \"better-auth:organization/permission\", // gitleaks:allow: public policy identifier\n requires,\n advise: (ctx) => advise(ctx, orgPermissionPolicy),\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 \"Organization permissions must contain a resource and actions.\",\n );\n }\n },\n async evaluate({ permissions, organization }, context) {\n const organizationId = await organization(context);\n if (typeof organizationId !== \"string\" || !organizationId.trim()) {\n return {\n effect: \"deny\",\n reason: organization.missingReason ?? \"ORGANIZATION_REQUIRED\",\n };\n }\n const stable = 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([\"org:hasPermission\", organizationId, stable]),\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 OrgApi).hasPermission({\n headers: context.headers,\n body: { organizationId, permissions },\n }),\n ),\n );\n if (!result.success) {\n return { effect: \"deny\", reason: \"MISSING_PERMISSION\" };\n }\n context.provide(ACTIVE_ORGANIZATION_ID, organizationId);\n return { effect: \"allow\" };\n } catch (error) {\n if (\n isAPIError(error) &&\n error.body?.code === \"USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION\"\n ) {\n return {\n effect: \"deny\",\n reason: \"USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION\",\n };\n }\n throw error;\n }\n },\n};\nexport const orgMemberPolicy: AuthorizationPolicy<\n OrgMemberParams,\n SessionPrincipal\n> = {\n id: \"better-auth:organization/member\",\n requires,\n advise: (ctx) => advise(ctx, orgMemberPolicy),\n async evaluate({ organization }, context) {\n const organizationId = await organization(context);\n if (typeof organizationId !== \"string\" || !organizationId.trim()) {\n return {\n effect: \"deny\",\n reason: organization.missingReason ?? \"ORGANIZATION_REQUIRED\",\n };\n }\n try {\n const result = await context.memo(\n JSON.stringify([\"org:activeMemberRole\", organizationId]),\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 OrgApi).getActiveMemberRole({\n headers: context.headers,\n query: { organizationId },\n }),\n ),\n );\n if (typeof result.role !== \"string\" || !result.role.trim()) {\n return {\n effect: \"deny\",\n reason: \"YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION\",\n };\n }\n context.provide(ACTIVE_ORGANIZATION_ID, organizationId);\n context.provide(ACTIVE_MEMBER_ROLE, result.role);\n return { effect: \"allow\" };\n } catch (error) {\n if (\n isAPIError(error) &&\n error.body?.code === \"YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION\"\n ) {\n return {\n effect: \"deny\",\n reason: \"YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION\",\n };\n }\n throw error;\n }\n },\n};\nexport function orgPermission<N extends string = \"default\">(\n permissions: OrgPermissions<N>,\n options: { organization?: OrganizationRef } = {},\n): Requirement<OrgPermissionParams> {\n return {\n policy: orgPermissionPolicy,\n params: {\n permissions: permissions as OrgPermissions,\n organization: options.organization ?? activeOrganization(),\n },\n };\n}\nexport function RequireOrgPermission<N extends string = \"default\">(\n permissions: OrgPermissions<N>,\n options?: { organization?: OrganizationRef },\n): ClassDecorator & MethodDecorator {\n return Require(orgPermission<N>(permissions, options));\n}\nexport function orgMember(\n options: { organization?: OrganizationRef } = {},\n): Requirement<OrgMemberParams> {\n return {\n policy: orgMemberPolicy,\n params: { organization: options.organization ?? activeOrganization() },\n };\n}\nexport function RequireOrgMember(options?: {\n organization?: OrganizationRef;\n}): ClassDecorator & MethodDecorator {\n return Require(orgMember(options));\n}\n"],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { Ft as SessionPrincipal, Ot as OrgPermissions, f as AuthorizationPolicy, l as AuthorizationContext, nt as Requirement } from "./auth-contracts-BBA1C1gj.cjs";
2
+ //#region src/organization.d.ts
3
+ export interface OrganizationRef {
4
+ (ctx: AuthorizationContext): unknown | Promise<unknown>;
5
+ readonly missingReason?: string;
6
+ }
7
+ export interface OrgPermissionParams {
8
+ readonly permissions: OrgPermissions;
9
+ readonly organization: OrganizationRef;
10
+ }
11
+ export interface OrgMemberParams {
12
+ readonly organization: OrganizationRef;
13
+ }
14
+ export declare const ACTIVE_ORGANIZATION_ID: unique symbol;
15
+ export declare const ACTIVE_MEMBER_ROLE: unique symbol;
16
+ export declare const ActiveOrganizationId: () => ParameterDecorator;
17
+ export declare const ActiveMemberRole: () => ParameterDecorator;
18
+ export declare function organizationRef(resolve: (ctx: AuthorizationContext) => unknown | Promise<unknown>, options?: {
19
+ missingReason?: string;
20
+ }): OrganizationRef;
21
+ export declare function activeOrganization(): OrganizationRef;
22
+ export declare function fromParam(name: string): OrganizationRef;
23
+ export declare function fromHeader(name: string): OrganizationRef;
24
+ export declare const orgPermissionPolicy: AuthorizationPolicy<OrgPermissionParams, SessionPrincipal>;
25
+ export declare const orgMemberPolicy: AuthorizationPolicy<OrgMemberParams, SessionPrincipal>;
26
+ export declare function orgPermission<N extends string = "default">(permissions: OrgPermissions<N>, options?: {
27
+ organization?: OrganizationRef;
28
+ }): Requirement<OrgPermissionParams>;
29
+ export declare function RequireOrgPermission<N extends string = "default">(permissions: OrgPermissions<N>, options?: {
30
+ organization?: OrganizationRef;
31
+ }): ClassDecorator & MethodDecorator;
32
+ export declare function orgMember(options?: {
33
+ organization?: OrganizationRef;
34
+ }): Requirement<OrgMemberParams>;
35
+ export declare function RequireOrgMember(options?: {
36
+ organization?: OrganizationRef;
37
+ }): ClassDecorator & MethodDecorator;
38
+ //#endregion
39
+ export type { OrgPermissions };
40
+ //# sourceMappingURL=organization.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.d.cts","names":[],"sources":["../src/organization.ts"],"mappings":";;iBAaiB;GACd,KAAK,iCAAiC;WAC9B;;iBAEM;WACN,aAAa;WACb,cAAc;;iBAER;WACN,cAAc;;qBAEZ;qBAGA;qBAGA,4BAAoB;qBAIpB,wBAAgB;wBAoBb,gBACd,UAAU,KAAK,mCAAmC,kBAClD;EAAW;IACV;wBAGa,sBAAsB;wBAgDtB,UAAU,eAAe;wBAMzB,WAAW,eAAe;qBA4D7B,qBAAqB,oBAChC,qBACA;qBAiEW,iBAAiB,oBAC5B,iBACA;wBAqDc,cAAc,8BAC5B,aAAa,eAAe,IAC5B;EAAW,eAAe;IACzB,YAAY;wBASC,qBAAqB,8BACnC,aAAa,eAAe,IAC5B;EAAY,eAAe;IAC1B,iBAAiB;wBAGJ,UACd;EAAW,eAAe;IACzB,YAAY;wBAMC,iBAAiB;EAC/B,eAAe;IACb,iBAAiB"}
@@ -0,0 +1,40 @@
1
+ import { Ft as SessionPrincipal, Ot as OrgPermissions, f as AuthorizationPolicy, l as AuthorizationContext, nt as Requirement } from "./auth-contracts-tqsfBZ8B.mjs";
2
+ //#region src/organization.d.ts
3
+ export interface OrganizationRef {
4
+ (ctx: AuthorizationContext): unknown | Promise<unknown>;
5
+ readonly missingReason?: string;
6
+ }
7
+ export interface OrgPermissionParams {
8
+ readonly permissions: OrgPermissions;
9
+ readonly organization: OrganizationRef;
10
+ }
11
+ export interface OrgMemberParams {
12
+ readonly organization: OrganizationRef;
13
+ }
14
+ export declare const ACTIVE_ORGANIZATION_ID: unique symbol;
15
+ export declare const ACTIVE_MEMBER_ROLE: unique symbol;
16
+ export declare const ActiveOrganizationId: () => ParameterDecorator;
17
+ export declare const ActiveMemberRole: () => ParameterDecorator;
18
+ export declare function organizationRef(resolve: (ctx: AuthorizationContext) => unknown | Promise<unknown>, options?: {
19
+ missingReason?: string;
20
+ }): OrganizationRef;
21
+ export declare function activeOrganization(): OrganizationRef;
22
+ export declare function fromParam(name: string): OrganizationRef;
23
+ export declare function fromHeader(name: string): OrganizationRef;
24
+ export declare const orgPermissionPolicy: AuthorizationPolicy<OrgPermissionParams, SessionPrincipal>;
25
+ export declare const orgMemberPolicy: AuthorizationPolicy<OrgMemberParams, SessionPrincipal>;
26
+ export declare function orgPermission<N extends string = "default">(permissions: OrgPermissions<N>, options?: {
27
+ organization?: OrganizationRef;
28
+ }): Requirement<OrgPermissionParams>;
29
+ export declare function RequireOrgPermission<N extends string = "default">(permissions: OrgPermissions<N>, options?: {
30
+ organization?: OrganizationRef;
31
+ }): ClassDecorator & MethodDecorator;
32
+ export declare function orgMember(options?: {
33
+ organization?: OrganizationRef;
34
+ }): Requirement<OrgMemberParams>;
35
+ export declare function RequireOrgMember(options?: {
36
+ organization?: OrganizationRef;
37
+ }): ClassDecorator & MethodDecorator;
38
+ //#endregion
39
+ export type { OrgPermissions };
40
+ //# sourceMappingURL=organization.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.d.mts","names":[],"sources":["../src/organization.ts"],"mappings":";;iBAaiB;GACd,KAAK,iCAAiC;WAC9B;;iBAEM;WACN,aAAa;WACb,cAAc;;iBAER;WACN,cAAc;;qBAEZ;qBAGA;qBAGA,4BAAoB;qBAIpB,wBAAgB;wBAoBb,gBACd,UAAU,KAAK,mCAAmC,kBAClD;EAAW;IACV;wBAGa,sBAAsB;wBAgDtB,UAAU,eAAe;wBAMzB,WAAW,eAAe;qBA4D7B,qBAAqB,oBAChC,qBACA;qBAiEW,iBAAiB,oBAC5B,iBACA;wBAqDc,cAAc,8BAC5B,aAAa,eAAe,IAC5B;EAAW,eAAe;IACzB,YAAY;wBASC,qBAAqB,8BACnC,aAAa,eAAe,IAC5B;EAAY,eAAe;IAC1B,iBAAiB;wBAGJ,UACd;EAAW,eAAe;IACzB,YAAY;wBAMC,iBAAiB;EAC/B,eAAe;IACb,iBAAiB"}
@@ -0,0 +1,177 @@
1
+ import { n as BetterAuthConfigurationError } from "./auth-errors-CQjfgjaO.mjs";
2
+ import { u as Require, v as defineInvocationParam } from "./auth-decorators-DfAJbHar.mjs";
3
+ import { isAPIError } from "better-auth/api";
4
+ //#region src/organization.ts
5
+ const ACTIVE_ORGANIZATION_ID = Symbol.for("nestjs-slightly-better-auth:org:active-organization-id");
6
+ const ACTIVE_MEMBER_ROLE = Symbol.for("nestjs-slightly-better-auth:org:active-member-role");
7
+ const ActiveOrganizationId = defineInvocationParam(ACTIVE_ORGANIZATION_ID, { missing: "ORGANIZATION_REQUIRED" });
8
+ const ActiveMemberRole = defineInvocationParam(ACTIVE_MEMBER_ROLE, { missing: "MEMBER_ROLE_REQUIRED" });
9
+ const REF = Symbol.for("nestjs-slightly-better-auth:org:ref");
10
+ function organizationRef(resolve, options = {}) {
11
+ return Object.assign((ctx) => resolve(ctx), options);
12
+ }
13
+ function activeOrganization() {
14
+ return Object.assign(organizationRef(async (ctx) => {
15
+ const value = ctx.principal.session.session?.activeOrganizationId;
16
+ if (value !== void 0) return value;
17
+ if (!await ctx.auth.hasPlugin("custom-session")) return;
18
+ try {
19
+ return (await ctx.memo(JSON.stringify(["org:activeMember"]), () => ctx.auth.run({
20
+ cookies: ctx.cookies,
21
+ inbound: () => ctx.headers,
22
+ internal: true
23
+ }, () => ctx.auth.api.getActiveMember({ headers: ctx.headers }))))?.organizationId;
24
+ } catch (error) {
25
+ if (isAPIError(error) && error.statusCode === 400 && (error.body?.code === "NO_ACTIVE_ORGANIZATION" || error.body?.code === "MEMBER_NOT_FOUND")) return;
26
+ throw error;
27
+ }
28
+ }, { missingReason: "NO_ACTIVE_ORGANIZATION" }), { [REF]: { kind: "active" } });
29
+ }
30
+ function fromParam(name) {
31
+ return Object.assign(organizationRef((ctx) => ctx.param(name)), { [REF]: {
32
+ kind: "param",
33
+ name
34
+ } });
35
+ }
36
+ function fromHeader(name) {
37
+ return organizationRef((ctx) => ctx.headers.get(name));
38
+ }
39
+ function requirements(expressions) {
40
+ return expressions.flatMap((expression) => "anyOf" in expression ? requirements(expression.anyOf) : "allOf" in expression ? requirements(expression.allOf) : [expression]);
41
+ }
42
+ function advise(ctx, policy) {
43
+ const advice = [];
44
+ for (const handler of ctx.handlers) for (const requirement of requirements(handler.plan.requirements)) {
45
+ if (requirement.policy !== policy) continue;
46
+ const marker = requirement.params.organization[REF];
47
+ if (marker?.kind === "active") {
48
+ const input = handler.inputs.find((name) => [
49
+ "orgId",
50
+ "organizationId",
51
+ "organization",
52
+ "orgSlug"
53
+ ].includes(name));
54
+ if (input) advice.push({
55
+ level: "warn",
56
+ code: "W_ORG_PARAM_IGNORED",
57
+ message: `${handler.plan.site} checks the active organization but accepts ${input}.`,
58
+ hint: input === "orgSlug" ? "Map the slug to an ID with organizationRef and auth.api.listOrganizations({ headers })." : `Use fromParam('${input}').`
59
+ });
60
+ } else if (marker?.kind === "param" && !handler.inputs.includes(marker.name)) advice.push({
61
+ level: "warn",
62
+ code: "W_ORG_PARAM_MISSING",
63
+ message: `${handler.plan.site} reads ${marker.name}, absent from claimed inputs: ${handler.inputs.join(", ") || "none"}.`,
64
+ hint: "Use the correct input name, a method-level organizationRef, or SkipDefaultRequirements with a method-level requirement."
65
+ });
66
+ }
67
+ return advice;
68
+ }
69
+ const requires = {
70
+ plugins: ["organization"],
71
+ principals: ["session"],
72
+ presentsCredentials: true
73
+ };
74
+ const orgPermissionPolicy = {
75
+ id: "better-auth:organization/permission",
76
+ requires,
77
+ advise: (ctx) => advise(ctx, orgPermissionPolicy),
78
+ validate({ permissions }) {
79
+ if (!Object.keys(permissions).length || Object.values(permissions).some((actions) => !Array.isArray(actions) || !actions.length)) throw new BetterAuthConfigurationError("EMPTY_PERMISSIONS", "Organization permissions must contain a resource and actions.");
80
+ },
81
+ async evaluate({ permissions, organization }, context) {
82
+ const organizationId = await organization(context);
83
+ if (typeof organizationId !== "string" || !organizationId.trim()) return {
84
+ effect: "deny",
85
+ reason: organization.missingReason ?? "ORGANIZATION_REQUIRED"
86
+ };
87
+ const stable = Object.entries(permissions).sort(([a], [b]) => a.localeCompare(b)).map(([resource, actions]) => [resource, [...actions].sort()]);
88
+ try {
89
+ if (!(await context.memo(JSON.stringify([
90
+ "org:hasPermission",
91
+ organizationId,
92
+ stable
93
+ ]), () => context.auth.run({
94
+ cookies: context.cookies,
95
+ inbound: () => context.headers,
96
+ internal: true
97
+ }, () => context.auth.api.hasPermission({
98
+ headers: context.headers,
99
+ body: {
100
+ organizationId,
101
+ permissions
102
+ }
103
+ })))).success) return {
104
+ effect: "deny",
105
+ reason: "MISSING_PERMISSION"
106
+ };
107
+ context.provide(ACTIVE_ORGANIZATION_ID, organizationId);
108
+ return { effect: "allow" };
109
+ } catch (error) {
110
+ if (isAPIError(error) && error.body?.code === "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION") return {
111
+ effect: "deny",
112
+ reason: "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION"
113
+ };
114
+ throw error;
115
+ }
116
+ }
117
+ };
118
+ const orgMemberPolicy = {
119
+ id: "better-auth:organization/member",
120
+ requires,
121
+ advise: (ctx) => advise(ctx, orgMemberPolicy),
122
+ async evaluate({ organization }, context) {
123
+ const organizationId = await organization(context);
124
+ if (typeof organizationId !== "string" || !organizationId.trim()) return {
125
+ effect: "deny",
126
+ reason: organization.missingReason ?? "ORGANIZATION_REQUIRED"
127
+ };
128
+ try {
129
+ const result = await context.memo(JSON.stringify(["org:activeMemberRole", organizationId]), () => context.auth.run({
130
+ cookies: context.cookies,
131
+ inbound: () => context.headers,
132
+ internal: true
133
+ }, () => context.auth.api.getActiveMemberRole({
134
+ headers: context.headers,
135
+ query: { organizationId }
136
+ })));
137
+ if (typeof result.role !== "string" || !result.role.trim()) return {
138
+ effect: "deny",
139
+ reason: "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION"
140
+ };
141
+ context.provide(ACTIVE_ORGANIZATION_ID, organizationId);
142
+ context.provide(ACTIVE_MEMBER_ROLE, result.role);
143
+ return { effect: "allow" };
144
+ } catch (error) {
145
+ if (isAPIError(error) && error.body?.code === "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION") return {
146
+ effect: "deny",
147
+ reason: "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION"
148
+ };
149
+ throw error;
150
+ }
151
+ }
152
+ };
153
+ function orgPermission(permissions, options = {}) {
154
+ return {
155
+ policy: orgPermissionPolicy,
156
+ params: {
157
+ permissions,
158
+ organization: options.organization ?? activeOrganization()
159
+ }
160
+ };
161
+ }
162
+ function RequireOrgPermission(permissions, options) {
163
+ return Require(orgPermission(permissions, options));
164
+ }
165
+ function orgMember(options = {}) {
166
+ return {
167
+ policy: orgMemberPolicy,
168
+ params: { organization: options.organization ?? activeOrganization() }
169
+ };
170
+ }
171
+ function RequireOrgMember(options) {
172
+ return Require(orgMember(options));
173
+ }
174
+ //#endregion
175
+ export { ACTIVE_MEMBER_ROLE, ACTIVE_ORGANIZATION_ID, ActiveMemberRole, ActiveOrganizationId, RequireOrgMember, RequireOrgPermission, activeOrganization, fromHeader, fromParam, orgMember, orgMemberPolicy, orgPermission, orgPermissionPolicy, organizationRef };
176
+
177
+ //# sourceMappingURL=organization.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.mjs","names":[],"sources":["../src/organization.ts"],"sourcesContent":["import { isAPIError } from \"better-auth/api\";\nimport type {\n AuthorizationContext,\n AuthorizationPolicy,\n BootAdvice,\n BootAdviceContext,\n Requirement,\n RequirementExpr,\n} from \"./auth-contracts.js\";\nimport { defineInvocationParam, Require } from \"./auth-decorators.js\";\nimport { BetterAuthConfigurationError } from \"./auth-errors.js\";\nimport type { OrgPermissions, SessionPrincipal } from \"./auth-types.js\";\nexport type { OrgPermissions } from \"./auth-types.js\";\nexport interface OrganizationRef {\n (ctx: AuthorizationContext): unknown | Promise<unknown>;\n readonly missingReason?: string;\n}\nexport interface OrgPermissionParams {\n readonly permissions: OrgPermissions;\n readonly organization: OrganizationRef;\n}\nexport interface OrgMemberParams {\n readonly organization: OrganizationRef;\n}\nexport const ACTIVE_ORGANIZATION_ID = Symbol.for(\n \"nestjs-slightly-better-auth:org:active-organization-id\",\n);\nexport const ACTIVE_MEMBER_ROLE = Symbol.for(\n \"nestjs-slightly-better-auth:org:active-member-role\",\n);\nexport const ActiveOrganizationId = defineInvocationParam(\n ACTIVE_ORGANIZATION_ID,\n { missing: \"ORGANIZATION_REQUIRED\" },\n);\nexport const ActiveMemberRole = defineInvocationParam(ACTIVE_MEMBER_ROLE, {\n missing: \"MEMBER_ROLE_REQUIRED\",\n});\nconst REF = Symbol.for(\"nestjs-slightly-better-auth:org:ref\");\ntype MarkedRef = OrganizationRef & {\n readonly [REF]?: { kind: \"active\" } | { kind: \"param\"; name: string };\n};\ninterface OrgApi {\n hasPermission(input: {\n headers: Headers;\n body: { organizationId: string; permissions: OrgPermissions };\n }): Promise<{ success: boolean }>;\n getActiveMemberRole(input: {\n headers: Headers;\n query: { organizationId: string };\n }): Promise<{ role?: string | null }>;\n getActiveMember(input: {\n headers: Headers;\n }): Promise<{ organizationId?: string }>;\n}\nexport function organizationRef(\n resolve: (ctx: AuthorizationContext) => unknown | Promise<unknown>,\n options: { missingReason?: string } = {},\n): OrganizationRef {\n return Object.assign((ctx: AuthorizationContext) => resolve(ctx), options);\n}\nexport function activeOrganization(): OrganizationRef {\n return Object.assign(\n organizationRef(\n async (ctx) => {\n const session = (ctx.principal as SessionPrincipal).session as {\n session?: { activeOrganizationId?: unknown };\n };\n const value = session.session?.activeOrganizationId;\n if (value !== undefined) {\n return value;\n }\n if (!(await ctx.auth.hasPlugin(\"custom-session\"))) {\n return undefined;\n }\n try {\n const member = await ctx.memo(\n JSON.stringify([\"org:activeMember\"]),\n () =>\n ctx.auth.run(\n {\n cookies: ctx.cookies,\n inbound: () => ctx.headers,\n internal: true,\n },\n () =>\n (ctx.auth.api as unknown as OrgApi).getActiveMember({\n headers: ctx.headers,\n }),\n ),\n );\n return member?.organizationId;\n } catch (error) {\n if (\n isAPIError(error) &&\n error.statusCode === 400 &&\n (error.body?.code === \"NO_ACTIVE_ORGANIZATION\" ||\n error.body?.code === \"MEMBER_NOT_FOUND\")\n ) {\n return undefined;\n }\n throw error;\n }\n },\n { missingReason: \"NO_ACTIVE_ORGANIZATION\" },\n ),\n { [REF]: { kind: \"active\" as const } },\n );\n}\nexport function fromParam(name: string): OrganizationRef {\n return Object.assign(\n organizationRef((ctx) => ctx.param(name)),\n { [REF]: { kind: \"param\" as const, name } },\n );\n}\nexport function fromHeader(name: string): OrganizationRef {\n return organizationRef((ctx) => ctx.headers.get(name));\n}\nfunction requirements(expressions: readonly RequirementExpr[]): Requirement[] {\n return expressions.flatMap((expression) =>\n \"anyOf\" in expression\n ? requirements(expression.anyOf)\n : \"allOf\" in expression\n ? requirements(expression.allOf)\n : [expression],\n );\n}\nfunction advise(\n ctx: BootAdviceContext,\n policy: AuthorizationPolicy<any, any>,\n): BootAdvice[] {\n const advice: BootAdvice[] = [];\n for (const handler of ctx.handlers) {\n for (const requirement of requirements(handler.plan.requirements)) {\n if (requirement.policy !== policy) {\n continue;\n }\n const ref = (requirement.params as OrgMemberParams)\n .organization as MarkedRef;\n const marker = ref[REF];\n if (marker?.kind === \"active\") {\n const input = handler.inputs.find((name) =>\n [\"orgId\", \"organizationId\", \"organization\", \"orgSlug\"].includes(name),\n );\n if (input) {\n advice.push({\n level: \"warn\",\n code: \"W_ORG_PARAM_IGNORED\",\n message: `${handler.plan.site} checks the active organization but accepts ${input}.`,\n hint:\n input === \"orgSlug\"\n ? \"Map the slug to an ID with organizationRef and auth.api.listOrganizations({ headers }).\"\n : `Use fromParam('${input}').`,\n });\n }\n } else if (\n marker?.kind === \"param\" &&\n !handler.inputs.includes(marker.name)\n ) {\n advice.push({\n level: \"warn\",\n code: \"W_ORG_PARAM_MISSING\",\n message: `${handler.plan.site} reads ${marker.name}, absent from claimed inputs: ${handler.inputs.join(\", \") || \"none\"}.`,\n hint: \"Use the correct input name, a method-level organizationRef, or SkipDefaultRequirements with a method-level requirement.\",\n });\n }\n }\n }\n return advice;\n}\nconst requires = {\n plugins: [\"organization\"],\n principals: [\"session\" as const],\n presentsCredentials: true,\n};\nexport const orgPermissionPolicy: AuthorizationPolicy<\n OrgPermissionParams,\n SessionPrincipal\n> = {\n id: \"better-auth:organization/permission\", // gitleaks:allow: public policy identifier\n requires,\n advise: (ctx) => advise(ctx, orgPermissionPolicy),\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 \"Organization permissions must contain a resource and actions.\",\n );\n }\n },\n async evaluate({ permissions, organization }, context) {\n const organizationId = await organization(context);\n if (typeof organizationId !== \"string\" || !organizationId.trim()) {\n return {\n effect: \"deny\",\n reason: organization.missingReason ?? \"ORGANIZATION_REQUIRED\",\n };\n }\n const stable = 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([\"org:hasPermission\", organizationId, stable]),\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 OrgApi).hasPermission({\n headers: context.headers,\n body: { organizationId, permissions },\n }),\n ),\n );\n if (!result.success) {\n return { effect: \"deny\", reason: \"MISSING_PERMISSION\" };\n }\n context.provide(ACTIVE_ORGANIZATION_ID, organizationId);\n return { effect: \"allow\" };\n } catch (error) {\n if (\n isAPIError(error) &&\n error.body?.code === \"USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION\"\n ) {\n return {\n effect: \"deny\",\n reason: \"USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION\",\n };\n }\n throw error;\n }\n },\n};\nexport const orgMemberPolicy: AuthorizationPolicy<\n OrgMemberParams,\n SessionPrincipal\n> = {\n id: \"better-auth:organization/member\",\n requires,\n advise: (ctx) => advise(ctx, orgMemberPolicy),\n async evaluate({ organization }, context) {\n const organizationId = await organization(context);\n if (typeof organizationId !== \"string\" || !organizationId.trim()) {\n return {\n effect: \"deny\",\n reason: organization.missingReason ?? \"ORGANIZATION_REQUIRED\",\n };\n }\n try {\n const result = await context.memo(\n JSON.stringify([\"org:activeMemberRole\", organizationId]),\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 OrgApi).getActiveMemberRole({\n headers: context.headers,\n query: { organizationId },\n }),\n ),\n );\n if (typeof result.role !== \"string\" || !result.role.trim()) {\n return {\n effect: \"deny\",\n reason: \"YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION\",\n };\n }\n context.provide(ACTIVE_ORGANIZATION_ID, organizationId);\n context.provide(ACTIVE_MEMBER_ROLE, result.role);\n return { effect: \"allow\" };\n } catch (error) {\n if (\n isAPIError(error) &&\n error.body?.code === \"YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION\"\n ) {\n return {\n effect: \"deny\",\n reason: \"YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION\",\n };\n }\n throw error;\n }\n },\n};\nexport function orgPermission<N extends string = \"default\">(\n permissions: OrgPermissions<N>,\n options: { organization?: OrganizationRef } = {},\n): Requirement<OrgPermissionParams> {\n return {\n policy: orgPermissionPolicy,\n params: {\n permissions: permissions as OrgPermissions,\n organization: options.organization ?? activeOrganization(),\n },\n };\n}\nexport function RequireOrgPermission<N extends string = \"default\">(\n permissions: OrgPermissions<N>,\n options?: { organization?: OrganizationRef },\n): ClassDecorator & MethodDecorator {\n return Require(orgPermission<N>(permissions, options));\n}\nexport function orgMember(\n options: { organization?: OrganizationRef } = {},\n): Requirement<OrgMemberParams> {\n return {\n policy: orgMemberPolicy,\n params: { organization: options.organization ?? activeOrganization() },\n };\n}\nexport function RequireOrgMember(options?: {\n organization?: OrganizationRef;\n}): ClassDecorator & MethodDecorator {\n return Require(orgMember(options));\n}\n"],"mappings":"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"}