@grantjs/server 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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +443 -0
  3. package/dist/debug-C8ibbrE5.js +9 -0
  4. package/dist/debug-C8ibbrE5.js.map +1 -0
  5. package/dist/debug-Crb5gNDM.cjs +8 -0
  6. package/dist/debug-Crb5gNDM.cjs.map +1 -0
  7. package/dist/errors.d.ts +38 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/express/index.d.ts +6 -0
  10. package/dist/express/index.d.ts.map +1 -0
  11. package/dist/express/middleware.d.ts +38 -0
  12. package/dist/express/middleware.d.ts.map +1 -0
  13. package/dist/express.cjs +99 -0
  14. package/dist/express.cjs.map +1 -0
  15. package/dist/express.mjs +101 -0
  16. package/dist/express.mjs.map +1 -0
  17. package/dist/fastify/index.d.ts +6 -0
  18. package/dist/fastify/index.d.ts.map +1 -0
  19. package/dist/fastify/plugin.d.ts +74 -0
  20. package/dist/fastify/plugin.d.ts.map +1 -0
  21. package/dist/fastify.cjs +103 -0
  22. package/dist/fastify.cjs.map +1 -0
  23. package/dist/fastify.mjs +104 -0
  24. package/dist/fastify.mjs.map +1 -0
  25. package/dist/grant-client-D1LZI2f4.js +180 -0
  26. package/dist/grant-client-D1LZI2f4.js.map +1 -0
  27. package/dist/grant-client-DywfJN5P.cjs +179 -0
  28. package/dist/grant-client-DywfJN5P.cjs.map +1 -0
  29. package/dist/grant-client.d.ts +50 -0
  30. package/dist/grant-client.d.ts.map +1 -0
  31. package/dist/index.cjs +50 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.ts +6 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.mjs +50 -0
  36. package/dist/index.mjs.map +1 -0
  37. package/dist/nest/grant.decorator.d.ts +38 -0
  38. package/dist/nest/grant.decorator.d.ts.map +1 -0
  39. package/dist/nest/grant.guard.d.ts +29 -0
  40. package/dist/nest/grant.guard.d.ts.map +1 -0
  41. package/dist/nest/grant.module.d.ts +28 -0
  42. package/dist/nest/grant.module.d.ts.map +1 -0
  43. package/dist/nest/index.d.ts +8 -0
  44. package/dist/nest/index.d.ts.map +1 -0
  45. package/dist/nest.cjs +154 -0
  46. package/dist/nest.cjs.map +1 -0
  47. package/dist/nest.mjs +156 -0
  48. package/dist/nest.mjs.map +1 -0
  49. package/dist/next/index.d.ts +6 -0
  50. package/dist/next/index.d.ts.map +1 -0
  51. package/dist/next/with-grant.d.ts +57 -0
  52. package/dist/next/with-grant.d.ts.map +1 -0
  53. package/dist/next.cjs +62 -0
  54. package/dist/next.cjs.map +1 -0
  55. package/dist/next.mjs +63 -0
  56. package/dist/next.mjs.map +1 -0
  57. package/dist/types.d.ts +92 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/utils/debug.d.ts +9 -0
  60. package/dist/utils/debug.d.ts.map +1 -0
  61. package/dist/utils/token-extractor.d.ts +14 -0
  62. package/dist/utils/token-extractor.d.ts.map +1 -0
  63. package/package.json +109 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.cjs","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":["debugGrant","extractTokenFromRequest","AuthenticationError","AuthorizationError","BadRequestError","NotFoundError"],"mappings":";;;;;AA2CO,SAAS,MACd,QACA,SAC8E;AAC9E,SAAO,OAAO,KAAwB,KAAe,SAAsC;AACzF,QAAI;AACFA,uBAAW,WAAW,EAAE,UAAU,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AAG5E,YAAM,QAAQ,MAAMC,YAAAA,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;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,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,iBAAiBE,MAAAA,qBAAqB;AACxC,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiBC,MAAAA,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,iBAAiBC,MAAAA,iBAAiB;AACpC,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiBC,MAAAA,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;;;;;;;;"}
@@ -0,0 +1,101 @@
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";
6
+ 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
+ };
91
+ }
92
+ export {
93
+ AuthenticationError,
94
+ AuthorizationError,
95
+ BadRequestError,
96
+ G as GrantClient,
97
+ GrantServerError,
98
+ NotFoundError,
99
+ grant
100
+ };
101
+ //# sourceMappingURL=express.mjs.map
@@ -0,0 +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;"}
@@ -0,0 +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';
5
+ export { GrantClient } from '../grant-client';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,74 @@
1
+ import { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';
2
+ import { GrantClient } from '../grant-client';
3
+ import { ResourceResolver, AuthorizationResult, GrantServerConfig } from '../types';
4
+ /**
5
+ * Extended Fastify Request with authorization result
6
+ */
7
+ export interface AuthorizedFastifyRequest extends FastifyRequest {
8
+ authorization?: AuthorizationResult;
9
+ }
10
+ /**
11
+ * Options for Fastify preHandler hook
12
+ */
13
+ export interface GrantOptions {
14
+ /** The resource slug to check (e.g., "Organization", "Project", "Document") */
15
+ resource: string;
16
+ /** The action to check (e.g., "Query", "Create", "Update", "Delete") */
17
+ action: string;
18
+ /** Custom resource resolver for condition evaluation */
19
+ resourceResolver?: ResourceResolver;
20
+ }
21
+ /**
22
+ * Create a Fastify preHandler hook to check if user is granted permission
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { grant } from '@grantjs/server/fastify';
27
+ * import { GrantClient } from '@grantjs/server';
28
+ * import Fastify from 'fastify';
29
+ *
30
+ * const fastify = Fastify();
31
+ * const grantClient = new GrantClient({ apiUrl: 'https://api.grant.com' });
32
+ *
33
+ * fastify.get('/organizations', {
34
+ * preHandler: grant(grantClient, {
35
+ * resource: 'Organization',
36
+ * action: 'Query',
37
+ * }),
38
+ * }, async (request, reply) => {
39
+ * return { organizations: [] };
40
+ * });
41
+ * ```
42
+ */
43
+ export declare function grant(client: GrantClient, options: GrantOptions): (request: AuthorizedFastifyRequest, reply: FastifyReply) => Promise<void>;
44
+ /**
45
+ * Fastify plugin options
46
+ * Extends GrantServerConfig - plugin-specific options can be added here in the future
47
+ */
48
+ export type GrantPluginOptions = GrantServerConfig;
49
+ /**
50
+ * Fastify plugin that decorates the instance with GrantClient
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import Fastify from 'fastify';
55
+ * import { grantPlugin } from '@grantjs/server/fastify';
56
+ *
57
+ * const fastify = Fastify();
58
+ *
59
+ * await fastify.register(grantPlugin, {
60
+ * apiUrl: 'https://api.grant.com',
61
+ * cookieName: 'grant-access-token',
62
+ * });
63
+ *
64
+ * // GrantClient is now available on fastify.grant
65
+ * const canEdit = await fastify.grant.isGranted('Document', 'Update', { scope });
66
+ * ```
67
+ */
68
+ export declare const grantPlugin: FastifyPluginAsync<GrantPluginOptions>;
69
+ declare module 'fastify' {
70
+ interface FastifyInstance {
71
+ grant: GrantClient;
72
+ }
73
+ }
74
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ 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");
6
+ 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
+ };
90
+ }
91
+ const grantPlugin = async (fastify, options) => {
92
+ const grant2 = new grantClient.GrantClient(options);
93
+ fastify.decorate("grant", grant2);
94
+ };
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;
101
+ exports.grant = grant;
102
+ exports.grantPlugin = grantPlugin;
103
+ //# sourceMappingURL=fastify.cjs.map
@@ -0,0 +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;;;;;;;;;"}
@@ -0,0 +1,104 @@
1
+ import { AuthenticationError, AuthorizationError, BadRequestError, NotFoundError } from "./index.mjs";
2
+ import { GrantServerError } from "./index.mjs";
3
+ import { e as extractTokenFromRequest, G as GrantClient } from "./grant-client-D1LZI2f4.js";
4
+ import { d as debugGrant } from "./debug-C8ibbrE5.js";
5
+ function grant(client, options) {
6
+ return async (request, reply) => {
7
+ try {
8
+ debugGrant("Fastify", { resource: options.resource, action: options.action });
9
+ const token = await extractTokenFromRequest(request, client.config);
10
+ if (!token) {
11
+ reply.status(401).send({
12
+ error: "Unauthorized",
13
+ code: "UNAUTHENTICATED"
14
+ });
15
+ return;
16
+ }
17
+ let resolvedResource = null;
18
+ if (options.resourceResolver) {
19
+ resolvedResource = await options.resourceResolver({
20
+ resourceSlug: options.resource,
21
+ request
22
+ });
23
+ if (!resolvedResource) {
24
+ reply.status(404).send({
25
+ error: "Resource not found",
26
+ code: "NOT_FOUND"
27
+ });
28
+ return;
29
+ }
30
+ }
31
+ const result = await client.isAuthorized(
32
+ options.resource,
33
+ options.action,
34
+ {
35
+ context: {
36
+ resource: resolvedResource || void 0
37
+ }
38
+ },
39
+ request
40
+ );
41
+ debugGrant("Fastify", {
42
+ resource: options.resource,
43
+ action: options.action,
44
+ authorized: result.authorized,
45
+ ...result.authorized ? {} : { reason: result.reason }
46
+ });
47
+ if (!result.authorized) {
48
+ reply.status(403).send({
49
+ error: "Forbidden",
50
+ code: "FORBIDDEN",
51
+ reason: result.reason
52
+ });
53
+ return;
54
+ }
55
+ request.authorization = result;
56
+ } catch (error) {
57
+ if (error instanceof AuthenticationError) {
58
+ reply.status(401).send({
59
+ error: error.message,
60
+ code: error.code
61
+ });
62
+ return;
63
+ }
64
+ if (error instanceof AuthorizationError) {
65
+ reply.status(403).send({
66
+ error: error.message,
67
+ code: error.code,
68
+ reason: error.reason
69
+ });
70
+ return;
71
+ }
72
+ if (error instanceof BadRequestError) {
73
+ reply.status(400).send({
74
+ error: error.message,
75
+ code: error.code
76
+ });
77
+ return;
78
+ }
79
+ if (error instanceof NotFoundError) {
80
+ reply.status(404).send({
81
+ error: error.message,
82
+ code: error.code
83
+ });
84
+ return;
85
+ }
86
+ throw error;
87
+ }
88
+ };
89
+ }
90
+ const grantPlugin = async (fastify, options) => {
91
+ const grant2 = new GrantClient(options);
92
+ fastify.decorate("grant", grant2);
93
+ };
94
+ export {
95
+ AuthenticationError,
96
+ AuthorizationError,
97
+ BadRequestError,
98
+ GrantClient,
99
+ GrantServerError,
100
+ NotFoundError,
101
+ grant,
102
+ grantPlugin
103
+ };
104
+ //# sourceMappingURL=fastify.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.mjs","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":["grant"],"mappings":";;;;AAkDO,SAAS,MACd,QACA,SAC2E;AAC3E,SAAO,OAAO,SAAmC,UAAuC;AACtF,QAAI;AACF,iBAAW,WAAW,EAAE,UAAU,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AAG5E,YAAM,QAAQ,MAAM,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;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,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,iBAAiB,qBAAqB;AACxC,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiB,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,iBAAiB,iBAAiB;AACpC,cAAM,OAAO,GAAG,EAAE,KAAK;AAAA,UACrB,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,iBAAiB,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,QAAMA,SAAQ,IAAI,YAAY,OAAO;AAGrC,UAAQ,SAAS,SAASA,MAAK;AACjC;"}
@@ -0,0 +1,180 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ function extractBearerToken(authHeader) {
5
+ if (!authHeader) return null;
6
+ const parts = authHeader.trim().split(/\s+/);
7
+ if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
8
+ return null;
9
+ }
10
+ return parts[1];
11
+ }
12
+ function parseCookieHeader(cookieHeader) {
13
+ const cookies = {};
14
+ cookieHeader.split(";").forEach((cookie) => {
15
+ const [name, ...rest] = cookie.trim().split("=");
16
+ if (name && rest.length > 0) {
17
+ cookies[name.trim()] = rest.join("=").trim();
18
+ }
19
+ });
20
+ return cookies;
21
+ }
22
+ function extractTokenFromCookies(cookieHeader, cookieName) {
23
+ if (!cookieHeader) return null;
24
+ const cookies = parseCookieHeader(cookieHeader);
25
+ return cookies[cookieName] || null;
26
+ }
27
+ async function extractTokenFromRequest(request, config) {
28
+ if (config.getToken) {
29
+ const token = await config.getToken(request);
30
+ if (token) return token;
31
+ }
32
+ const req = request;
33
+ if (!req?.headers) return null;
34
+ const headersGet = req.headers.get;
35
+ if (typeof headersGet === "function") {
36
+ const authHeader2 = headersGet.call(req.headers, "authorization");
37
+ const bearerToken2 = extractBearerToken(authHeader2 ?? void 0);
38
+ if (bearerToken2) return bearerToken2;
39
+ const cookieName2 = config.cookieName || "grant-access-token";
40
+ const cookieHeader2 = headersGet.call(req.headers, "cookie");
41
+ if (cookieHeader2) {
42
+ const cookieToken = extractTokenFromCookies(cookieHeader2, cookieName2);
43
+ if (cookieToken) return cookieToken;
44
+ }
45
+ return null;
46
+ }
47
+ const plainHeaders = req.headers;
48
+ const authHeader = plainHeaders.authorization;
49
+ const bearerToken = extractBearerToken(authHeader);
50
+ if (bearerToken) return bearerToken;
51
+ const cookieName = config.cookieName || "grant-access-token";
52
+ if (req.cookies?.[cookieName]) {
53
+ return req.cookies[cookieName];
54
+ }
55
+ const cookieHeader = plainHeaders.cookie;
56
+ if (cookieHeader) {
57
+ const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);
58
+ if (cookieToken) return cookieToken;
59
+ }
60
+ return null;
61
+ }
62
+ class GrantClient {
63
+ constructor(config) {
64
+ __publicField(this, "config");
65
+ this.config = config;
66
+ }
67
+ // ============================================================================
68
+ // Public API - Permission Checks
69
+ // ============================================================================
70
+ /**
71
+ * Check if the current user has a specific permission
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const canEdit = await grant.isGranted('document', 'update', { scope });
76
+ * if (canEdit) {
77
+ * // Proceed with operation
78
+ * }
79
+ * ```
80
+ */
81
+ async isGranted(resource, action, options, request) {
82
+ const result = await this.isAuthorized(resource, action, options, request);
83
+ return result.authorized;
84
+ }
85
+ /**
86
+ * Check authorization with full result details
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const result = await grant.isAuthorized('document', 'update', { scope }, request);
91
+ * if (!result.authorized) {
92
+ * console.log('Denied:', result.reason);
93
+ * }
94
+ * ```
95
+ */
96
+ async isAuthorized(resource, action, options, request) {
97
+ try {
98
+ const response = await this.fetchWithAuth(
99
+ "/api/auth/is-authorized",
100
+ {
101
+ method: "POST",
102
+ body: JSON.stringify({
103
+ permission: {
104
+ resource,
105
+ action
106
+ },
107
+ context: {
108
+ resource: options?.context?.resource || null
109
+ }
110
+ })
111
+ },
112
+ request
113
+ );
114
+ if (!response.ok) {
115
+ const error = await response.json().catch(() => ({}));
116
+ return {
117
+ authorized: false,
118
+ reason: error.message || `API error: ${response.status}`
119
+ };
120
+ }
121
+ const json = await response.json();
122
+ const result = json.data ?? json;
123
+ return result;
124
+ } catch (error) {
125
+ return {
126
+ authorized: false,
127
+ reason: error instanceof Error ? error.message : "Unknown error"
128
+ };
129
+ }
130
+ }
131
+ /**
132
+ * Extract token from a request object
133
+ * Useful for middleware that needs to extract token before making authorization calls
134
+ */
135
+ async getTokenFromRequest(request) {
136
+ return extractTokenFromRequest(request, this.config);
137
+ }
138
+ // ============================================================================
139
+ // Private - HTTP & Authentication
140
+ // ============================================================================
141
+ /**
142
+ * Make an authenticated fetch request (token from request; no refresh).
143
+ */
144
+ async fetchWithAuth(url, init, request) {
145
+ return this.doFetch(url, init, request);
146
+ }
147
+ /**
148
+ * Perform the actual fetch request
149
+ */
150
+ async doFetch(url, init, request) {
151
+ const fetchFn = this.config.fetch ?? globalThis.fetch;
152
+ const fullUrl = url.startsWith("http") ? url : `${this.config.apiUrl}${url}`;
153
+ const headers = {
154
+ "Content-Type": "application/json",
155
+ ...init?.headers
156
+ };
157
+ if (request) {
158
+ const token = await extractTokenFromRequest(request, this.config);
159
+ if (token) {
160
+ headers["Authorization"] = `Bearer ${token}`;
161
+ }
162
+ const req = request;
163
+ if (req.headers?.cookie) {
164
+ headers["Cookie"] = req.headers.cookie;
165
+ }
166
+ }
167
+ return fetchFn(fullUrl, {
168
+ ...init,
169
+ headers,
170
+ // Include cookies for same-origin requests (supports cookie-based auth)
171
+ credentials: this.config.credentials ?? "include"
172
+ });
173
+ }
174
+ }
175
+ export {
176
+ GrantClient as G,
177
+ extractBearerToken as a,
178
+ extractTokenFromRequest as e
179
+ };
180
+ //# sourceMappingURL=grant-client-D1LZI2f4.js.map