@grantjs/server 1.0.0 → 1.1.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 (63) hide show
  1. package/README.md +1 -1
  2. package/dist/debug-DICR8lwQ.js +15 -0
  3. package/dist/debug-DICR8lwQ.js.map +1 -0
  4. package/dist/debug-DInHA4QB.cjs +20 -0
  5. package/dist/debug-DInHA4QB.cjs.map +1 -0
  6. package/dist/errors-BUeTn_3G.cjs +91 -0
  7. package/dist/errors-BUeTn_3G.cjs.map +1 -0
  8. package/dist/errors-CSC_Z_KP.js +62 -0
  9. package/dist/errors-CSC_Z_KP.js.map +1 -0
  10. package/dist/express/index.d.ts +3 -3
  11. package/dist/express/index.d.ts.map +1 -1
  12. package/dist/express/middleware.d.ts +2 -2
  13. package/dist/express/middleware.d.ts.map +1 -1
  14. package/dist/express.cjs +106 -95
  15. package/dist/express.cjs.map +1 -1
  16. package/dist/express.mjs +101 -99
  17. package/dist/express.mjs.map +1 -1
  18. package/dist/fastify/index.d.ts +4 -4
  19. package/dist/fastify/index.d.ts.map +1 -1
  20. package/dist/fastify/plugin.d.ts +2 -2
  21. package/dist/fastify/plugin.d.ts.map +1 -1
  22. package/dist/fastify.cjs +133 -97
  23. package/dist/fastify.cjs.map +1 -1
  24. package/dist/fastify.mjs +128 -101
  25. package/dist/fastify.mjs.map +1 -1
  26. package/dist/grant-client-f6IKMyk5.cjs +228 -0
  27. package/dist/grant-client-f6IKMyk5.cjs.map +1 -0
  28. package/dist/grant-client-t3KadxFo.js +205 -0
  29. package/dist/grant-client-t3KadxFo.js.map +1 -0
  30. package/dist/grant-client.d.ts +1 -1
  31. package/dist/grant-client.d.ts.map +1 -1
  32. package/dist/index.cjs +10 -49
  33. package/dist/index.d.ts +4 -4
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.mjs +3 -50
  36. package/dist/nest/grant.guard.d.ts +1 -1
  37. package/dist/nest/grant.guard.d.ts.map +1 -1
  38. package/dist/nest/grant.module.d.ts.map +1 -1
  39. package/dist/nest/index.d.ts +6 -6
  40. package/dist/nest/index.d.ts.map +1 -1
  41. package/dist/nest.cjs +163 -143
  42. package/dist/nest.cjs.map +1 -1
  43. package/dist/nest.mjs +150 -149
  44. package/dist/nest.mjs.map +1 -1
  45. package/dist/next/index.d.ts +4 -4
  46. package/dist/next/index.d.ts.map +1 -1
  47. package/dist/next/with-grant.d.ts +1 -1
  48. package/dist/next/with-grant.d.ts.map +1 -1
  49. package/dist/next.cjs +75 -58
  50. package/dist/next.cjs.map +1 -1
  51. package/dist/next.mjs +70 -60
  52. package/dist/next.mjs.map +1 -1
  53. package/package.json +17 -17
  54. package/dist/debug-C8ibbrE5.js +0 -9
  55. package/dist/debug-C8ibbrE5.js.map +0 -1
  56. package/dist/debug-Crb5gNDM.cjs +0 -8
  57. package/dist/debug-Crb5gNDM.cjs.map +0 -1
  58. package/dist/grant-client-D1LZI2f4.js +0 -180
  59. package/dist/grant-client-D1LZI2f4.js.map +0 -1
  60. package/dist/grant-client-DywfJN5P.cjs +0 -179
  61. package/dist/grant-client-DywfJN5P.cjs.map +0 -1
  62. package/dist/index.cjs.map +0 -1
  63. package/dist/index.mjs.map +0 -1
package/dist/express.mjs CHANGED
@@ -1,101 +1,103 @@
1
- import { AuthenticationError, AuthorizationError, BadRequestError, NotFoundError } from "./index.mjs";
2
- import { GrantServerError } from "./index.mjs";
3
- import { d as debugGrant } from "./debug-C8ibbrE5.js";
4
- import { e as extractTokenFromRequest } from "./grant-client-D1LZI2f4.js";
5
- import { G } from "./grant-client-D1LZI2f4.js";
1
+ import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-t3KadxFo.js";
2
+ import { a as NotFoundError, i as GrantServerError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-CSC_Z_KP.js";
3
+ import { t as debugGrant } from "./debug-DICR8lwQ.js";
4
+ //#region src/express/middleware.ts
5
+ /**
6
+ * Create Express middleware to check if user is granted permission
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { grant } from '@grantjs/server/express';
11
+ * import { GrantClient } from '@grantjs/server';
12
+ *
13
+ * const grantClient = new GrantClient({ apiUrl: 'https://api.grant.com' });
14
+ *
15
+ * router.get('/organizations', grant(grantClient, {
16
+ * resource: 'Organization',
17
+ * action: 'Query',
18
+ * }), handler);
19
+ * ```
20
+ */
6
21
  function grant(client, options) {
7
- return async (req, res, next) => {
8
- try {
9
- debugGrant("Express", { resource: options.resource, action: options.action });
10
- const token = await extractTokenFromRequest(req, client.config);
11
- if (!token) {
12
- res.status(401).json({
13
- error: "Unauthorized",
14
- code: "UNAUTHENTICATED"
15
- });
16
- return;
17
- }
18
- let resolvedResource = null;
19
- if (options.resourceResolver) {
20
- resolvedResource = await options.resourceResolver({
21
- resourceSlug: options.resource,
22
- request: req
23
- });
24
- if (!resolvedResource) {
25
- res.status(404).json({
26
- error: "Resource not found",
27
- code: "NOT_FOUND"
28
- });
29
- return;
30
- }
31
- }
32
- const result = await client.isAuthorized(
33
- options.resource,
34
- options.action,
35
- {
36
- context: {
37
- resource: resolvedResource || void 0
38
- }
39
- },
40
- req
41
- );
42
- debugGrant("Express", {
43
- resource: options.resource,
44
- action: options.action,
45
- authorized: result.authorized,
46
- ...result.authorized ? {} : { reason: result.reason }
47
- });
48
- if (!result.authorized) {
49
- res.status(403).json({
50
- error: "Forbidden",
51
- code: "FORBIDDEN",
52
- reason: result.reason
53
- });
54
- return;
55
- }
56
- req.authorization = result;
57
- next();
58
- } catch (error) {
59
- if (error instanceof AuthenticationError) {
60
- res.status(401).json({
61
- error: error.message,
62
- code: error.code
63
- });
64
- return;
65
- }
66
- if (error instanceof AuthorizationError) {
67
- res.status(403).json({
68
- error: error.message,
69
- code: error.code,
70
- reason: error.reason
71
- });
72
- return;
73
- }
74
- if (error instanceof BadRequestError) {
75
- res.status(400).json({
76
- error: error.message,
77
- code: error.code
78
- });
79
- return;
80
- }
81
- if (error instanceof NotFoundError) {
82
- res.status(404).json({
83
- error: error.message,
84
- code: error.code
85
- });
86
- return;
87
- }
88
- next(error);
89
- }
90
- };
22
+ return async (req, res, next) => {
23
+ try {
24
+ debugGrant("Express", {
25
+ resource: options.resource,
26
+ action: options.action
27
+ });
28
+ if (!await extractTokenFromRequest(req, client.config)) {
29
+ res.status(401).json({
30
+ error: "Unauthorized",
31
+ code: "UNAUTHENTICATED"
32
+ });
33
+ return;
34
+ }
35
+ let resolvedResource = null;
36
+ if (options.resourceResolver) {
37
+ resolvedResource = await options.resourceResolver({
38
+ resourceSlug: options.resource,
39
+ request: req
40
+ });
41
+ if (!resolvedResource) {
42
+ res.status(404).json({
43
+ error: "Resource not found",
44
+ code: "NOT_FOUND"
45
+ });
46
+ return;
47
+ }
48
+ }
49
+ const result = await client.isAuthorized(options.resource, options.action, { context: { resource: resolvedResource || void 0 } }, req);
50
+ debugGrant("Express", {
51
+ resource: options.resource,
52
+ action: options.action,
53
+ authorized: result.authorized,
54
+ ...result.authorized ? {} : { reason: result.reason }
55
+ });
56
+ if (!result.authorized) {
57
+ res.status(403).json({
58
+ error: "Forbidden",
59
+ code: "FORBIDDEN",
60
+ reason: result.reason
61
+ });
62
+ return;
63
+ }
64
+ req.authorization = result;
65
+ next();
66
+ } catch (error) {
67
+ if (error instanceof AuthenticationError) {
68
+ res.status(401).json({
69
+ error: error.message,
70
+ code: error.code
71
+ });
72
+ return;
73
+ }
74
+ if (error instanceof AuthorizationError) {
75
+ res.status(403).json({
76
+ error: error.message,
77
+ code: error.code,
78
+ reason: error.reason
79
+ });
80
+ return;
81
+ }
82
+ if (error instanceof BadRequestError) {
83
+ res.status(400).json({
84
+ error: error.message,
85
+ code: error.code
86
+ });
87
+ return;
88
+ }
89
+ if (error instanceof NotFoundError) {
90
+ res.status(404).json({
91
+ error: error.message,
92
+ code: error.code
93
+ });
94
+ return;
95
+ }
96
+ next(error);
97
+ }
98
+ };
91
99
  }
92
- export {
93
- AuthenticationError,
94
- AuthorizationError,
95
- BadRequestError,
96
- G as GrantClient,
97
- GrantServerError,
98
- NotFoundError,
99
- grant
100
- };
101
- //# sourceMappingURL=express.mjs.map
100
+ //#endregion
101
+ export { AuthenticationError, AuthorizationError, BadRequestError, GrantClient, GrantServerError, NotFoundError, grant };
102
+
103
+ //# sourceMappingURL=express.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"express.mjs","sources":["../src/express/middleware.ts"],"sourcesContent":["import { AuthenticationError, AuthorizationError, BadRequestError, NotFoundError } from '../errors';\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\n\nimport type { ResourceResolver, AuthorizationResult } from '../types';\nimport type { Request, Response, NextFunction } from 'express';\n\n/**\n * Extended Express Request with authorization result\n */\nexport interface AuthorizedRequest extends Request {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Options for Express middleware\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n /** Custom resource resolver for condition evaluation */\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * Create Express middleware to check if user is granted permission\n *\n * @example\n * ```ts\n * import { grant } from '@grantjs/server/express';\n * import { GrantClient } from '@grantjs/server';\n *\n * const grantClient = new GrantClient({ apiUrl: 'https://api.grant.com' });\n *\n * router.get('/organizations', grant(grantClient, {\n * resource: 'Organization',\n * action: 'Query',\n * }), handler);\n * ```\n */\nexport function grant(\n client: GrantClient,\n options: GrantOptions\n): (req: AuthorizedRequest, res: Response, next: NextFunction) => Promise<void> {\n return async (req: AuthorizedRequest, res: Response, next: NextFunction): Promise<void> => {\n try {\n debugGrant('Express', { resource: options.resource, action: options.action });\n\n // 1. Check authentication (token must be present)\n const token = await extractTokenFromRequest(req, client.config);\n if (!token) {\n res.status(401).json({\n error: 'Unauthorized',\n code: 'UNAUTHENTICATED',\n });\n return;\n }\n\n // 2. Optionally resolve resource for condition evaluation\n let resolvedResource: Record<string, unknown> | null = null;\n\n if (options.resourceResolver) {\n resolvedResource = await options.resourceResolver({\n resourceSlug: options.resource,\n request: req,\n });\n\n if (!resolvedResource) {\n res.status(404).json({\n error: 'Resource not found',\n code: 'NOT_FOUND',\n });\n return;\n }\n }\n\n // 3. Check authorization (pass request for token extraction; scope comes from JWT claims)\n const result = await client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource || undefined,\n },\n },\n req\n );\n\n debugGrant('Express', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n res.status(403).json({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n return;\n }\n\n // 4. Attach authorization result to request for downstream use\n (req as AuthorizedRequest).authorization = result;\n\n // 5. Proceed to next middleware/handler\n next();\n } catch (error) {\n // Handle known errors\n if (error instanceof AuthenticationError) {\n res.status(401).json({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof AuthorizationError) {\n res.status(403).json({\n error: error.message,\n code: error.code,\n reason: error.reason,\n });\n return;\n }\n\n if (error instanceof BadRequestError) {\n res.status(400).json({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof NotFoundError) {\n res.status(404).json({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n // Unknown error - pass to Express error handler\n next(error);\n }\n };\n}\n"],"names":[],"mappings":";;;;;AA2CO,SAAS,MACd,QACA,SAC8E;AAC9E,SAAO,OAAO,KAAwB,KAAe,SAAsC;AACzF,QAAI;AACF,iBAAW,WAAW,EAAE,UAAU,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AAG5E,YAAM,QAAQ,MAAM,wBAAwB,KAAK,OAAO,MAAM;AAC9D,UAAI,CAAC,OAAO;AACV,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,MAAM;AAAA,QAAA,CACP;AACD;AAAA,MACF;AAGA,UAAI,mBAAmD;AAEvD,UAAI,QAAQ,kBAAkB;AAC5B,2BAAmB,MAAM,QAAQ,iBAAiB;AAAA,UAChD,cAAc,QAAQ;AAAA,UACtB,SAAS;AAAA,QAAA,CACV;AAED,YAAI,CAAC,kBAAkB;AACrB,cAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YACnB,OAAO;AAAA,YACP,MAAM;AAAA,UAAA,CACP;AACD;AAAA,QACF;AAAA,MACF;AAGA,YAAM,SAAS,MAAM,OAAO;AAAA,QAC1B,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,UACE,SAAS;AAAA,YACP,UAAU,oBAAoB;AAAA,UAAA;AAAA,QAChC;AAAA,QAEF;AAAA,MAAA;AAGF,iBAAW,WAAW;AAAA,QACpB,UAAU,QAAQ;AAAA,QAClB,QAAQ,QAAQ;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,GAAI,OAAO,aAAa,CAAA,IAAK,EAAE,QAAQ,OAAO,OAAA;AAAA,MAAO,CACtD;AAED,UAAI,CAAC,OAAO,YAAY;AACtB,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,MAAM;AAAA,UACN,QAAQ,OAAO;AAAA,QAAA,CAChB;AACD;AAAA,MACF;AAGC,UAA0B,gBAAgB;AAG3C,WAAA;AAAA,IACF,SAAS,OAAO;AAEd,UAAI,iBAAiB,qBAAqB;AACxC,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiB,oBAAoB;AACvC,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,UACZ,QAAQ,MAAM;AAAA,QAAA,CACf;AACD;AAAA,MACF;AAEA,UAAI,iBAAiB,iBAAiB;AACpC,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiB,eAAe;AAClC,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAGA,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AACF;"}
1
+ {"version":3,"file":"express.mjs","names":[],"sources":["../src/express/middleware.ts"],"sourcesContent":["import type { NextFunction, Request, Response } from 'express';\n\nimport { AuthenticationError, AuthorizationError, BadRequestError, NotFoundError } from '../errors';\nimport { GrantClient } from '../grant-client';\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\n\n/**\n * Extended Express Request with authorization result\n */\nexport interface AuthorizedRequest extends Request {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Options for Express middleware\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n /** Custom resource resolver for condition evaluation */\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * Create Express middleware to check if user is granted permission\n *\n * @example\n * ```ts\n * import { grant } from '@grantjs/server/express';\n * import { GrantClient } from '@grantjs/server';\n *\n * const grantClient = new GrantClient({ apiUrl: 'https://api.grant.com' });\n *\n * router.get('/organizations', grant(grantClient, {\n * resource: 'Organization',\n * action: 'Query',\n * }), handler);\n * ```\n */\nexport function grant(\n client: GrantClient,\n options: GrantOptions\n): (req: AuthorizedRequest, res: Response, next: NextFunction) => Promise<void> {\n return async (req: AuthorizedRequest, res: Response, next: NextFunction): Promise<void> => {\n try {\n debugGrant('Express', { resource: options.resource, action: options.action });\n\n // 1. Check authentication (token must be present)\n const token = await extractTokenFromRequest(req, client.config);\n if (!token) {\n res.status(401).json({\n error: 'Unauthorized',\n code: 'UNAUTHENTICATED',\n });\n return;\n }\n\n // 2. Optionally resolve resource for condition evaluation\n let resolvedResource: Record<string, unknown> | null = null;\n\n if (options.resourceResolver) {\n resolvedResource = await options.resourceResolver({\n resourceSlug: options.resource,\n request: req,\n });\n\n if (!resolvedResource) {\n res.status(404).json({\n error: 'Resource not found',\n code: 'NOT_FOUND',\n });\n return;\n }\n }\n\n // 3. Check authorization (pass request for token extraction; scope comes from JWT claims)\n const result = await client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource || undefined,\n },\n },\n req\n );\n\n debugGrant('Express', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n res.status(403).json({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n return;\n }\n\n // 4. Attach authorization result to request for downstream use\n (req as AuthorizedRequest).authorization = result;\n\n // 5. Proceed to next middleware/handler\n next();\n } catch (error) {\n // Handle known errors\n if (error instanceof AuthenticationError) {\n res.status(401).json({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof AuthorizationError) {\n res.status(403).json({\n error: error.message,\n code: error.code,\n reason: error.reason,\n });\n return;\n }\n\n if (error instanceof BadRequestError) {\n res.status(400).json({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof NotFoundError) {\n res.status(404).json({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n // Unknown error - pass to Express error handler\n next(error);\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,MACd,QACA,SAC8E;AAC9E,QAAO,OAAO,KAAwB,KAAe,SAAsC;AACzF,MAAI;AACF,cAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;IAAQ,CAAC;AAI7E,OAAI,CAAC,MADe,wBAAwB,KAAK,OAAO,OAAO,EACnD;AACV,QAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO;KACP,MAAM;KACP,CAAC;AACF;;GAIF,IAAI,mBAAmD;AAEvD,OAAI,QAAQ,kBAAkB;AAC5B,uBAAmB,MAAM,QAAQ,iBAAiB;KAChD,cAAc,QAAQ;KACtB,SAAS;KACV,CAAC;AAEF,QAAI,CAAC,kBAAkB;AACrB,SAAI,OAAO,IAAI,CAAC,KAAK;MACnB,OAAO;MACP,MAAM;MACP,CAAC;AACF;;;GAKJ,MAAM,SAAS,MAAM,OAAO,aAC1B,QAAQ,UACR,QAAQ,QACR,EACE,SAAS,EACP,UAAU,oBAAoB,KAAA,GAC/B,EACF,EACD,IACD;AAED,cAAW,WAAW;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,YAAY,OAAO;IACnB,GAAI,OAAO,aAAa,EAAE,GAAG,EAAE,QAAQ,OAAO,QAAQ;IACvD,CAAC;AAEF,OAAI,CAAC,OAAO,YAAY;AACtB,QAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO;KACP,MAAM;KACN,QAAQ,OAAO;KAChB,CAAC;AACF;;AAID,OAA0B,gBAAgB;AAG3C,SAAM;WACC,OAAO;AAEd,OAAI,iBAAiB,qBAAqB;AACxC,QAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;KACb,CAAC;AACF;;AAGF,OAAI,iBAAiB,oBAAoB;AACvC,QAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;KACZ,QAAQ,MAAM;KACf,CAAC;AACF;;AAGF,OAAI,iBAAiB,iBAAiB;AACpC,QAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;KACb,CAAC;AACF;;AAGF,OAAI,iBAAiB,eAAe;AAClC,QAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;KACb,CAAC;AACF;;AAIF,QAAK,MAAM"}
@@ -1,6 +1,6 @@
1
- export { grantPlugin, grant } from './plugin';
2
- export type { GrantOptions, AuthorizedFastifyRequest, GrantPluginOptions } from './plugin';
3
- export type { GrantServerConfig, AuthorizationResult, PermissionCheckOptions, Scope, ResourceResolver, } from '../types';
4
- export { GrantServerError, AuthenticationError, AuthorizationError, BadRequestError, NotFoundError, } from '../errors';
1
+ export type { AuthorizedFastifyRequest, GrantOptions, GrantPluginOptions } from './plugin';
2
+ export { grant, grantPlugin } from './plugin';
3
+ export type { AuthorizationResult, GrantServerConfig, PermissionCheckOptions, ResourceResolver, Scope, } from '../types';
4
+ export { AuthenticationError, AuthorizationError, BadRequestError, GrantServerError, NotFoundError, } from '../errors';
5
5
  export { GrantClient } from '../grant-client';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fastify/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG3F,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,EACL,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,aAAa,GACd,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fastify/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,wBAAwB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC3F,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG9C,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,GACN,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
@@ -1,6 +1,6 @@
1
- import { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';
1
+ import { FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify';
2
2
  import { GrantClient } from '../grant-client';
3
- import { ResourceResolver, AuthorizationResult, GrantServerConfig } from '../types';
3
+ import { AuthorizationResult, GrantServerConfig, ResourceResolver } from '../types';
4
4
  /**
5
5
  * Extended Fastify Request with authorization result
6
6
  */
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/fastify/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG3E,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAEzF;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CACnB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,YAAY,GACpB,CAAC,OAAO,EAAE,wBAAwB,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAsG3E;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,WAAW,EAAE,kBAAkB,CAAC,kBAAkB,CAK9D,CAAC;AAIF,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,KAAK,EAAE,WAAW,CAAC;KACpB;CACF"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/fastify/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG3E,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAIzF;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CACnB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,YAAY,GACpB,CAAC,OAAO,EAAE,wBAAwB,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAsG3E;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,WAAW,EAAE,kBAAkB,CAAC,kBAAkB,CAK9D,CAAC;AAIF,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,KAAK,EAAE,WAAW,CAAC;KACpB;CACF"}
package/dist/fastify.cjs CHANGED
@@ -1,103 +1,139 @@
1
- "use strict";
2
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const index = require("./index.cjs");
4
- const grantClient = require("./grant-client-DywfJN5P.cjs");
5
- const debug = require("./debug-Crb5gNDM.cjs");
2
+ const require_grant_client = require("./grant-client-f6IKMyk5.cjs");
3
+ const require_errors = require("./errors-BUeTn_3G.cjs");
4
+ const require_debug = require("./debug-DInHA4QB.cjs");
5
+ //#region src/fastify/plugin.ts
6
+ /**
7
+ * Create a Fastify preHandler hook to check if user is granted permission
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { grant } from '@grantjs/server/fastify';
12
+ * import { GrantClient } from '@grantjs/server';
13
+ * import Fastify from 'fastify';
14
+ *
15
+ * const fastify = Fastify();
16
+ * const grantClient = new GrantClient({ apiUrl: 'https://api.grant.com' });
17
+ *
18
+ * fastify.get('/organizations', {
19
+ * preHandler: grant(grantClient, {
20
+ * resource: 'Organization',
21
+ * action: 'Query',
22
+ * }),
23
+ * }, async (request, reply) => {
24
+ * return { organizations: [] };
25
+ * });
26
+ * ```
27
+ */
6
28
  function grant(client, options) {
7
- return async (request, reply) => {
8
- try {
9
- debug.debugGrant("Fastify", { resource: options.resource, action: options.action });
10
- const token = await grantClient.extractTokenFromRequest(request, client.config);
11
- if (!token) {
12
- reply.status(401).send({
13
- error: "Unauthorized",
14
- code: "UNAUTHENTICATED"
15
- });
16
- return;
17
- }
18
- let resolvedResource = null;
19
- if (options.resourceResolver) {
20
- resolvedResource = await options.resourceResolver({
21
- resourceSlug: options.resource,
22
- request
23
- });
24
- if (!resolvedResource) {
25
- reply.status(404).send({
26
- error: "Resource not found",
27
- code: "NOT_FOUND"
28
- });
29
- return;
30
- }
31
- }
32
- const result = await client.isAuthorized(
33
- options.resource,
34
- options.action,
35
- {
36
- context: {
37
- resource: resolvedResource || void 0
38
- }
39
- },
40
- request
41
- );
42
- debug.debugGrant("Fastify", {
43
- resource: options.resource,
44
- action: options.action,
45
- authorized: result.authorized,
46
- ...result.authorized ? {} : { reason: result.reason }
47
- });
48
- if (!result.authorized) {
49
- reply.status(403).send({
50
- error: "Forbidden",
51
- code: "FORBIDDEN",
52
- reason: result.reason
53
- });
54
- return;
55
- }
56
- request.authorization = result;
57
- } catch (error) {
58
- if (error instanceof index.AuthenticationError) {
59
- reply.status(401).send({
60
- error: error.message,
61
- code: error.code
62
- });
63
- return;
64
- }
65
- if (error instanceof index.AuthorizationError) {
66
- reply.status(403).send({
67
- error: error.message,
68
- code: error.code,
69
- reason: error.reason
70
- });
71
- return;
72
- }
73
- if (error instanceof index.BadRequestError) {
74
- reply.status(400).send({
75
- error: error.message,
76
- code: error.code
77
- });
78
- return;
79
- }
80
- if (error instanceof index.NotFoundError) {
81
- reply.status(404).send({
82
- error: error.message,
83
- code: error.code
84
- });
85
- return;
86
- }
87
- throw error;
88
- }
89
- };
29
+ return async (request, reply) => {
30
+ try {
31
+ require_debug.debugGrant("Fastify", {
32
+ resource: options.resource,
33
+ action: options.action
34
+ });
35
+ if (!await require_grant_client.extractTokenFromRequest(request, client.config)) {
36
+ reply.status(401).send({
37
+ error: "Unauthorized",
38
+ code: "UNAUTHENTICATED"
39
+ });
40
+ return;
41
+ }
42
+ let resolvedResource = null;
43
+ if (options.resourceResolver) {
44
+ resolvedResource = await options.resourceResolver({
45
+ resourceSlug: options.resource,
46
+ request
47
+ });
48
+ if (!resolvedResource) {
49
+ reply.status(404).send({
50
+ error: "Resource not found",
51
+ code: "NOT_FOUND"
52
+ });
53
+ return;
54
+ }
55
+ }
56
+ const result = await client.isAuthorized(options.resource, options.action, { context: { resource: resolvedResource || void 0 } }, request);
57
+ require_debug.debugGrant("Fastify", {
58
+ resource: options.resource,
59
+ action: options.action,
60
+ authorized: result.authorized,
61
+ ...result.authorized ? {} : { reason: result.reason }
62
+ });
63
+ if (!result.authorized) {
64
+ reply.status(403).send({
65
+ error: "Forbidden",
66
+ code: "FORBIDDEN",
67
+ reason: result.reason
68
+ });
69
+ return;
70
+ }
71
+ request.authorization = result;
72
+ } catch (error) {
73
+ if (error instanceof require_errors.AuthenticationError) {
74
+ reply.status(401).send({
75
+ error: error.message,
76
+ code: error.code
77
+ });
78
+ return;
79
+ }
80
+ if (error instanceof require_errors.AuthorizationError) {
81
+ reply.status(403).send({
82
+ error: error.message,
83
+ code: error.code,
84
+ reason: error.reason
85
+ });
86
+ return;
87
+ }
88
+ if (error instanceof require_errors.BadRequestError) {
89
+ reply.status(400).send({
90
+ error: error.message,
91
+ code: error.code
92
+ });
93
+ return;
94
+ }
95
+ if (error instanceof require_errors.NotFoundError) {
96
+ reply.status(404).send({
97
+ error: error.message,
98
+ code: error.code
99
+ });
100
+ return;
101
+ }
102
+ throw error;
103
+ }
104
+ };
90
105
  }
91
- const grantPlugin = async (fastify, options) => {
92
- const grant2 = new grantClient.GrantClient(options);
93
- fastify.decorate("grant", grant2);
106
+ /**
107
+ * Fastify plugin that decorates the instance with GrantClient
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * import Fastify from 'fastify';
112
+ * import { grantPlugin } from '@grantjs/server/fastify';
113
+ *
114
+ * const fastify = Fastify();
115
+ *
116
+ * await fastify.register(grantPlugin, {
117
+ * apiUrl: 'https://api.grant.com',
118
+ * cookieName: 'grant-access-token',
119
+ * });
120
+ *
121
+ * // GrantClient is now available on fastify.grant
122
+ * const canEdit = await fastify.grant.isGranted('Document', 'Update', { scope });
123
+ * ```
124
+ */
125
+ var grantPlugin = async (fastify, options) => {
126
+ const grant = new require_grant_client.GrantClient(options);
127
+ fastify.decorate("grant", grant);
94
128
  };
95
- exports.AuthenticationError = index.AuthenticationError;
96
- exports.AuthorizationError = index.AuthorizationError;
97
- exports.BadRequestError = index.BadRequestError;
98
- exports.GrantServerError = index.GrantServerError;
99
- exports.NotFoundError = index.NotFoundError;
100
- exports.GrantClient = grantClient.GrantClient;
129
+ //#endregion
130
+ exports.AuthenticationError = require_errors.AuthenticationError;
131
+ exports.AuthorizationError = require_errors.AuthorizationError;
132
+ exports.BadRequestError = require_errors.BadRequestError;
133
+ exports.GrantClient = require_grant_client.GrantClient;
134
+ exports.GrantServerError = require_errors.GrantServerError;
135
+ exports.NotFoundError = require_errors.NotFoundError;
101
136
  exports.grant = grant;
102
137
  exports.grantPlugin = grantPlugin;
103
- //# sourceMappingURL=fastify.cjs.map
138
+
139
+ //# sourceMappingURL=fastify.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.cjs","sources":["../src/fastify/plugin.ts"],"sourcesContent":["import { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';\n\nimport { AuthenticationError, AuthorizationError, BadRequestError, NotFoundError } from '../errors';\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\n\nimport type { ResourceResolver, AuthorizationResult, GrantServerConfig } from '../types';\n\n/**\n * Extended Fastify Request with authorization result\n */\nexport interface AuthorizedFastifyRequest extends FastifyRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Options for Fastify preHandler hook\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n /** Custom resource resolver for condition evaluation */\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * Create a Fastify preHandler hook to check if user is granted permission\n *\n * @example\n * ```ts\n * import { grant } from '@grantjs/server/fastify';\n * import { GrantClient } from '@grantjs/server';\n * import Fastify from 'fastify';\n *\n * const fastify = Fastify();\n * const grantClient = new GrantClient({ apiUrl: 'https://api.grant.com' });\n *\n * fastify.get('/organizations', {\n * preHandler: grant(grantClient, {\n * resource: 'Organization',\n * action: 'Query',\n * }),\n * }, async (request, reply) => {\n * return { organizations: [] };\n * });\n * ```\n */\nexport function grant(\n client: GrantClient,\n options: GrantOptions\n): (request: AuthorizedFastifyRequest, reply: FastifyReply) => Promise<void> {\n return async (request: AuthorizedFastifyRequest, reply: FastifyReply): Promise<void> => {\n try {\n debugGrant('Fastify', { resource: options.resource, action: options.action });\n\n // 1. Check authentication (token must be present)\n const token = await extractTokenFromRequest(request, client.config);\n if (!token) {\n reply.status(401).send({\n error: 'Unauthorized',\n code: 'UNAUTHENTICATED',\n });\n return;\n }\n\n // 2. Optionally resolve resource for condition evaluation\n let resolvedResource: Record<string, unknown> | null = null;\n\n if (options.resourceResolver) {\n resolvedResource = await options.resourceResolver({\n resourceSlug: options.resource,\n request: request,\n });\n\n if (!resolvedResource) {\n reply.status(404).send({\n error: 'Resource not found',\n code: 'NOT_FOUND',\n });\n return;\n }\n }\n\n // 3. Check authorization (pass request for token extraction; scope comes from JWT claims)\n const result = await client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource || undefined,\n },\n },\n request\n );\n\n debugGrant('Fastify', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n reply.status(403).send({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n return;\n }\n\n // 4. Attach authorization result to request for downstream use\n request.authorization = result;\n } catch (error) {\n // Handle known errors\n if (error instanceof AuthenticationError) {\n reply.status(401).send({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof AuthorizationError) {\n reply.status(403).send({\n error: error.message,\n code: error.code,\n reason: error.reason,\n });\n return;\n }\n\n if (error instanceof BadRequestError) {\n reply.status(400).send({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof NotFoundError) {\n reply.status(404).send({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n // Unknown error - rethrow to let Fastify handle it\n throw error;\n }\n };\n}\n\n/**\n * Fastify plugin options\n * Extends GrantServerConfig - plugin-specific options can be added here in the future\n */\nexport type GrantPluginOptions = GrantServerConfig;\n\n/**\n * Fastify plugin that decorates the instance with GrantClient\n *\n * @example\n * ```ts\n * import Fastify from 'fastify';\n * import { grantPlugin } from '@grantjs/server/fastify';\n *\n * const fastify = Fastify();\n *\n * await fastify.register(grantPlugin, {\n * apiUrl: 'https://api.grant.com',\n * cookieName: 'grant-access-token',\n * });\n *\n * // GrantClient is now available on fastify.grant\n * const canEdit = await fastify.grant.isGranted('Document', 'Update', { scope });\n * ```\n */\nexport const grantPlugin: FastifyPluginAsync<GrantPluginOptions> = async (fastify, options) => {\n const grant = new GrantClient(options);\n\n // Decorate Fastify instance with GrantClient\n fastify.decorate('grant', grant);\n};\n\n// Type declaration for TypeScript\n// This will work once Fastify is installed (it's a peer dependency)\ndeclare module 'fastify' {\n interface FastifyInstance {\n grant: GrantClient;\n }\n}\n"],"names":["debugGrant","extractTokenFromRequest","AuthenticationError","AuthorizationError","BadRequestError","NotFoundError","grant","GrantClient"],"mappings":";;;;;AAkDO,SAAS,MACd,QACA,SAC2E;AAC3E,SAAO,OAAO,SAAmC,UAAuC;AACtF,QAAI;AACFA,uBAAW,WAAW,EAAE,UAAU,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AAG5E,YAAM,QAAQ,MAAMC,YAAAA,wBAAwB,SAAS,OAAO,MAAM;AAClE,UAAI,CAAC,OAAO;AACV,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO;AAAA,UACP,MAAM;AAAA,QAAA,CACP;AACD;AAAA,MACF;AAGA,UAAI,mBAAmD;AAEvD,UAAI,QAAQ,kBAAkB;AAC5B,2BAAmB,MAAM,QAAQ,iBAAiB;AAAA,UAChD,cAAc,QAAQ;AAAA,UACtB;AAAA,QAAA,CACD;AAED,YAAI,CAAC,kBAAkB;AACrB,gBAAM,OAAO,GAAG,EAAE,KAAK;AAAA,YACrB,OAAO;AAAA,YACP,MAAM;AAAA,UAAA,CACP;AACD;AAAA,QACF;AAAA,MACF;AAGA,YAAM,SAAS,MAAM,OAAO;AAAA,QAC1B,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,UACE,SAAS;AAAA,YACP,UAAU,oBAAoB;AAAA,UAAA;AAAA,QAChC;AAAA,QAEF;AAAA,MAAA;AAGFD,YAAAA,WAAW,WAAW;AAAA,QACpB,UAAU,QAAQ;AAAA,QAClB,QAAQ,QAAQ;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,GAAI,OAAO,aAAa,CAAA,IAAK,EAAE,QAAQ,OAAO,OAAA;AAAA,MAAO,CACtD;AAED,UAAI,CAAC,OAAO,YAAY;AACtB,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO;AAAA,UACP,MAAM;AAAA,UACN,QAAQ,OAAO;AAAA,QAAA,CAChB;AACD;AAAA,MACF;AAGA,cAAQ,gBAAgB;AAAA,IAC1B,SAAS,OAAO;AAEd,UAAI,iBAAiBE,MAAAA,qBAAqB;AACxC,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiBC,MAAAA,oBAAoB;AACvC,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,UACZ,QAAQ,MAAM;AAAA,QAAA,CACf;AACD;AAAA,MACF;AAEA,UAAI,iBAAiBC,MAAAA,iBAAiB;AACpC,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiBC,MAAAA,eAAe;AAClC,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAGA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AA2BO,MAAM,cAAsD,OAAO,SAAS,YAAY;AAC7F,QAAMC,SAAQ,IAAIC,YAAAA,YAAY,OAAO;AAGrC,UAAQ,SAAS,SAASD,MAAK;AACjC;;;;;;;;;"}
1
+ {"version":3,"file":"fastify.cjs","names":[],"sources":["../src/fastify/plugin.ts"],"sourcesContent":["import { FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify';\n\nimport { AuthenticationError, AuthorizationError, BadRequestError, NotFoundError } from '../errors';\nimport { GrantClient } from '../grant-client';\nimport type { AuthorizationResult, GrantServerConfig, ResourceResolver } from '../types';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\n\n/**\n * Extended Fastify Request with authorization result\n */\nexport interface AuthorizedFastifyRequest extends FastifyRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Options for Fastify preHandler hook\n */\nexport interface GrantOptions {\n /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n resource: string;\n /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n action: string;\n /** Custom resource resolver for condition evaluation */\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * Create a Fastify preHandler hook to check if user is granted permission\n *\n * @example\n * ```ts\n * import { grant } from '@grantjs/server/fastify';\n * import { GrantClient } from '@grantjs/server';\n * import Fastify from 'fastify';\n *\n * const fastify = Fastify();\n * const grantClient = new GrantClient({ apiUrl: 'https://api.grant.com' });\n *\n * fastify.get('/organizations', {\n * preHandler: grant(grantClient, {\n * resource: 'Organization',\n * action: 'Query',\n * }),\n * }, async (request, reply) => {\n * return { organizations: [] };\n * });\n * ```\n */\nexport function grant(\n client: GrantClient,\n options: GrantOptions\n): (request: AuthorizedFastifyRequest, reply: FastifyReply) => Promise<void> {\n return async (request: AuthorizedFastifyRequest, reply: FastifyReply): Promise<void> => {\n try {\n debugGrant('Fastify', { resource: options.resource, action: options.action });\n\n // 1. Check authentication (token must be present)\n const token = await extractTokenFromRequest(request, client.config);\n if (!token) {\n reply.status(401).send({\n error: 'Unauthorized',\n code: 'UNAUTHENTICATED',\n });\n return;\n }\n\n // 2. Optionally resolve resource for condition evaluation\n let resolvedResource: Record<string, unknown> | null = null;\n\n if (options.resourceResolver) {\n resolvedResource = await options.resourceResolver({\n resourceSlug: options.resource,\n request: request,\n });\n\n if (!resolvedResource) {\n reply.status(404).send({\n error: 'Resource not found',\n code: 'NOT_FOUND',\n });\n return;\n }\n }\n\n // 3. Check authorization (pass request for token extraction; scope comes from JWT claims)\n const result = await client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource || undefined,\n },\n },\n request\n );\n\n debugGrant('Fastify', {\n resource: options.resource,\n action: options.action,\n authorized: result.authorized,\n ...(result.authorized ? {} : { reason: result.reason }),\n });\n\n if (!result.authorized) {\n reply.status(403).send({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n return;\n }\n\n // 4. Attach authorization result to request for downstream use\n request.authorization = result;\n } catch (error) {\n // Handle known errors\n if (error instanceof AuthenticationError) {\n reply.status(401).send({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof AuthorizationError) {\n reply.status(403).send({\n error: error.message,\n code: error.code,\n reason: error.reason,\n });\n return;\n }\n\n if (error instanceof BadRequestError) {\n reply.status(400).send({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n if (error instanceof NotFoundError) {\n reply.status(404).send({\n error: error.message,\n code: error.code,\n });\n return;\n }\n\n // Unknown error - rethrow to let Fastify handle it\n throw error;\n }\n };\n}\n\n/**\n * Fastify plugin options\n * Extends GrantServerConfig - plugin-specific options can be added here in the future\n */\nexport type GrantPluginOptions = GrantServerConfig;\n\n/**\n * Fastify plugin that decorates the instance with GrantClient\n *\n * @example\n * ```ts\n * import Fastify from 'fastify';\n * import { grantPlugin } from '@grantjs/server/fastify';\n *\n * const fastify = Fastify();\n *\n * await fastify.register(grantPlugin, {\n * apiUrl: 'https://api.grant.com',\n * cookieName: 'grant-access-token',\n * });\n *\n * // GrantClient is now available on fastify.grant\n * const canEdit = await fastify.grant.isGranted('Document', 'Update', { scope });\n * ```\n */\nexport const grantPlugin: FastifyPluginAsync<GrantPluginOptions> = async (fastify, options) => {\n const grant = new GrantClient(options);\n\n // Decorate Fastify instance with GrantClient\n fastify.decorate('grant', grant);\n};\n\n// Type declaration for TypeScript\n// This will work once Fastify is installed (it's a peer dependency)\ndeclare module 'fastify' {\n interface FastifyInstance {\n grant: GrantClient;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,MACd,QACA,SAC2E;AAC3E,QAAO,OAAO,SAAmC,UAAuC;AACtF,MAAI;AACF,iBAAA,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;IAAQ,CAAC;AAI7E,OAAI,CAAC,MADe,qBAAA,wBAAwB,SAAS,OAAO,OAAO,EACvD;AACV,UAAM,OAAO,IAAI,CAAC,KAAK;KACrB,OAAO;KACP,MAAM;KACP,CAAC;AACF;;GAIF,IAAI,mBAAmD;AAEvD,OAAI,QAAQ,kBAAkB;AAC5B,uBAAmB,MAAM,QAAQ,iBAAiB;KAChD,cAAc,QAAQ;KACb;KACV,CAAC;AAEF,QAAI,CAAC,kBAAkB;AACrB,WAAM,OAAO,IAAI,CAAC,KAAK;MACrB,OAAO;MACP,MAAM;MACP,CAAC;AACF;;;GAKJ,MAAM,SAAS,MAAM,OAAO,aAC1B,QAAQ,UACR,QAAQ,QACR,EACE,SAAS,EACP,UAAU,oBAAoB,KAAA,GAC/B,EACF,EACD,QACD;AAED,iBAAA,WAAW,WAAW;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,YAAY,OAAO;IACnB,GAAI,OAAO,aAAa,EAAE,GAAG,EAAE,QAAQ,OAAO,QAAQ;IACvD,CAAC;AAEF,OAAI,CAAC,OAAO,YAAY;AACtB,UAAM,OAAO,IAAI,CAAC,KAAK;KACrB,OAAO;KACP,MAAM;KACN,QAAQ,OAAO;KAChB,CAAC;AACF;;AAIF,WAAQ,gBAAgB;WACjB,OAAO;AAEd,OAAI,iBAAiB,eAAA,qBAAqB;AACxC,UAAM,OAAO,IAAI,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;KACb,CAAC;AACF;;AAGF,OAAI,iBAAiB,eAAA,oBAAoB;AACvC,UAAM,OAAO,IAAI,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;KACZ,QAAQ,MAAM;KACf,CAAC;AACF;;AAGF,OAAI,iBAAiB,eAAA,iBAAiB;AACpC,UAAM,OAAO,IAAI,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;KACb,CAAC;AACF;;AAGF,OAAI,iBAAiB,eAAA,eAAe;AAClC,UAAM,OAAO,IAAI,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;KACb,CAAC;AACF;;AAIF,SAAM;;;;;;;;;;;;;;;;;;;;;;;AA8BZ,IAAa,cAAsD,OAAO,SAAS,YAAY;CAC7F,MAAM,QAAQ,IAAI,qBAAA,YAAY,QAAQ;AAGtC,SAAQ,SAAS,SAAS,MAAM"}