@grantjs/server 1.4.2 → 1.5.1
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.
- package/dist/{errors-C1t6IDWm.cjs → errors-BGaMqKLU.cjs} +2 -2
- package/dist/{errors-C1t6IDWm.cjs.map → errors-BGaMqKLU.cjs.map} +1 -1
- package/dist/{errors-DS7QtVTK.js → errors-CYELoFEG.js} +2 -2
- package/dist/{errors-DS7QtVTK.js.map → errors-CYELoFEG.js.map} +1 -1
- package/dist/express.cjs +2 -2
- package/dist/express.cjs.map +1 -1
- package/dist/express.mjs +2 -2
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.cjs +2 -2
- package/dist/fastify.cjs.map +1 -1
- package/dist/fastify.mjs +2 -2
- package/dist/fastify.mjs.map +1 -1
- package/dist/{grant-client-ByBQjwxH.cjs → grant-client-CDwjehfE.cjs} +5 -5
- package/dist/grant-client-CDwjehfE.cjs.map +1 -0
- package/dist/{grant-client-B3ofrQqL.js → grant-client-DxGOMZu5.js} +5 -5
- package/dist/grant-client-DxGOMZu5.js.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/nest/grant.guard.d.ts.map +1 -1
- package/dist/nest.cjs +4 -4
- package/dist/nest.cjs.map +1 -1
- package/dist/nest.mjs +4 -4
- package/dist/nest.mjs.map +1 -1
- package/dist/next.cjs +2 -2
- package/dist/next.mjs +2 -2
- package/dist/utils/token-extractor.d.ts.map +1 -1
- package/package.json +13 -13
- package/dist/grant-client-B3ofrQqL.js.map +0 -1
- package/dist/grant-client-ByBQjwxH.cjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_grant_client = require("./grant-client-
|
|
1
|
+
const require_grant_client = require("./grant-client-CDwjehfE.cjs");
|
|
2
2
|
//#region src/errors.ts
|
|
3
3
|
/**
|
|
4
4
|
* Base error class for Grant server errors
|
|
@@ -92,4 +92,4 @@ Object.defineProperty(exports, "NotFoundError", {
|
|
|
92
92
|
}
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
//# sourceMappingURL=errors-
|
|
95
|
+
//# sourceMappingURL=errors-BGaMqKLU.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors-
|
|
1
|
+
{"version":3,"file":"errors-BGaMqKLU.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base error class for Grant server errors\n */\nexport class GrantServerError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode: number = 500\n ) {\n super(message);\n this.name = 'GrantServerError';\n Object.setPrototypeOf(this, GrantServerError.prototype);\n }\n}\n\n/**\n * Authentication error (401)\n * Thrown when user is not authenticated\n */\nexport class AuthenticationError extends GrantServerError {\n constructor(message: string = 'Unauthorized', code: string = 'UNAUTHENTICATED') {\n super(message, code, 401);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Authorization error (403)\n * Thrown when user is authenticated but lacks required permission\n */\nexport class AuthorizationError extends GrantServerError {\n constructor(\n message: string = 'Forbidden',\n code: string = 'FORBIDDEN',\n public readonly reason?: string\n ) {\n super(message, code, 403);\n this.name = 'AuthorizationError';\n Object.setPrototypeOf(this, AuthorizationError.prototype);\n }\n}\n\n/**\n * Bad request error (400)\n * Thrown when request is malformed or missing required data\n */\nexport class BadRequestError extends GrantServerError {\n constructor(message: string = 'Bad Request', code: string = 'BAD_REQUEST') {\n super(message, code, 400);\n this.name = 'BadRequestError';\n Object.setPrototypeOf(this, BadRequestError.prototype);\n }\n}\n\n/**\n * Not found error (404)\n * Thrown when resource is not found\n */\nexport class NotFoundError extends GrantServerError {\n constructor(message: string = 'Not Found', code: string = 'NOT_FOUND') {\n super(message, code, 404);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n"],"mappings":";;;;;AAGA,IAAa,mBAAb,MAAa,yBAAyB,MAAM;CAC1C,YACE,SACA,MACA,aAAqC,KACrC;EACA,MAAM,OAAO;EAHG,qBAAA,gBAAA,MAAA,QAAA,KAAA,CAAA;EACA,qBAAA,gBAAA,MAAA,cAAA,KAAA,CAAA;EADA,KAAA,OAAA;EACA,KAAA,aAAA;EAGhB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,iBAAiB,SAAS;CACxD;AACF;;;;;AAMA,IAAa,sBAAb,MAAa,4BAA4B,iBAAiB;CACxD,YAAY,UAAkB,gBAAgB,OAAe,mBAAmB;EAC9E,MAAM,SAAS,MAAM,GAAG;EACxB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF;;;;;AAMA,IAAa,qBAAb,MAAa,2BAA2B,iBAAiB;CACvD,YACE,UAAkB,aAClB,OAAe,aACf,QACA;EACA,MAAM,SAAS,MAAM,GAAG;EAFR,qBAAA,gBAAA,MAAA,UAAA,KAAA,CAAA;EAAA,KAAA,SAAA;EAGhB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,mBAAmB,SAAS;CAC1D;AACF;;;;;AAMA,IAAa,kBAAb,MAAa,wBAAwB,iBAAiB;CACpD,YAAY,UAAkB,eAAe,OAAe,eAAe;EACzE,MAAM,SAAS,MAAM,GAAG;EACxB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,gBAAgB,SAAS;CACvD;AACF;;;;;AAMA,IAAa,gBAAb,MAAa,sBAAsB,iBAAiB;CAClD,YAAY,UAAkB,aAAa,OAAe,aAAa;EACrE,MAAM,SAAS,MAAM,GAAG;EACxB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,cAAc,SAAS;CACrD;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as _defineProperty } from "./grant-client-
|
|
1
|
+
import { n as _defineProperty } from "./grant-client-DxGOMZu5.js";
|
|
2
2
|
//#region src/errors.ts
|
|
3
3
|
/**
|
|
4
4
|
* Base error class for Grant server errors
|
|
@@ -63,4 +63,4 @@ var NotFoundError = class NotFoundError extends GrantServerError {
|
|
|
63
63
|
//#endregion
|
|
64
64
|
export { NotFoundError as a, GrantServerError as i, AuthorizationError as n, BadRequestError as r, AuthenticationError as t };
|
|
65
65
|
|
|
66
|
-
//# sourceMappingURL=errors-
|
|
66
|
+
//# sourceMappingURL=errors-CYELoFEG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors-
|
|
1
|
+
{"version":3,"file":"errors-CYELoFEG.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base error class for Grant server errors\n */\nexport class GrantServerError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode: number = 500\n ) {\n super(message);\n this.name = 'GrantServerError';\n Object.setPrototypeOf(this, GrantServerError.prototype);\n }\n}\n\n/**\n * Authentication error (401)\n * Thrown when user is not authenticated\n */\nexport class AuthenticationError extends GrantServerError {\n constructor(message: string = 'Unauthorized', code: string = 'UNAUTHENTICATED') {\n super(message, code, 401);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Authorization error (403)\n * Thrown when user is authenticated but lacks required permission\n */\nexport class AuthorizationError extends GrantServerError {\n constructor(\n message: string = 'Forbidden',\n code: string = 'FORBIDDEN',\n public readonly reason?: string\n ) {\n super(message, code, 403);\n this.name = 'AuthorizationError';\n Object.setPrototypeOf(this, AuthorizationError.prototype);\n }\n}\n\n/**\n * Bad request error (400)\n * Thrown when request is malformed or missing required data\n */\nexport class BadRequestError extends GrantServerError {\n constructor(message: string = 'Bad Request', code: string = 'BAD_REQUEST') {\n super(message, code, 400);\n this.name = 'BadRequestError';\n Object.setPrototypeOf(this, BadRequestError.prototype);\n }\n}\n\n/**\n * Not found error (404)\n * Thrown when resource is not found\n */\nexport class NotFoundError extends GrantServerError {\n constructor(message: string = 'Not Found', code: string = 'NOT_FOUND') {\n super(message, code, 404);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n"],"mappings":";;;;;AAGA,IAAa,mBAAb,MAAa,yBAAyB,MAAM;CAC1C,YACE,SACA,MACA,aAAqC,KACrC;EACA,MAAM,OAAO;EAHG,gBAAA,MAAA,QAAA,KAAA,CAAA;EACA,gBAAA,MAAA,cAAA,KAAA,CAAA;EADA,KAAA,OAAA;EACA,KAAA,aAAA;EAGhB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,iBAAiB,SAAS;CACxD;AACF;;;;;AAMA,IAAa,sBAAb,MAAa,4BAA4B,iBAAiB;CACxD,YAAY,UAAkB,gBAAgB,OAAe,mBAAmB;EAC9E,MAAM,SAAS,MAAM,GAAG;EACxB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,oBAAoB,SAAS;CAC3D;AACF;;;;;AAMA,IAAa,qBAAb,MAAa,2BAA2B,iBAAiB;CACvD,YACE,UAAkB,aAClB,OAAe,aACf,QACA;EACA,MAAM,SAAS,MAAM,GAAG;EAFR,gBAAA,MAAA,UAAA,KAAA,CAAA;EAAA,KAAA,SAAA;EAGhB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,mBAAmB,SAAS;CAC1D;AACF;;;;;AAMA,IAAa,kBAAb,MAAa,wBAAwB,iBAAiB;CACpD,YAAY,UAAkB,eAAe,OAAe,eAAe;EACzE,MAAM,SAAS,MAAM,GAAG;EACxB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,gBAAgB,SAAS;CACvD;AACF;;;;;AAMA,IAAa,gBAAb,MAAa,sBAAsB,iBAAiB;CAClD,YAAY,UAAkB,aAAa,OAAe,aAAa;EACrE,MAAM,SAAS,MAAM,GAAG;EACxB,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,cAAc,SAAS;CACrD;AACF"}
|
package/dist/express.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_grant_client = require("./grant-client-
|
|
3
|
-
const require_errors = require("./errors-
|
|
2
|
+
const require_grant_client = require("./grant-client-CDwjehfE.cjs");
|
|
3
|
+
const require_errors = require("./errors-BGaMqKLU.cjs");
|
|
4
4
|
const require_debug = require("./debug-BZtvGkmP.cjs");
|
|
5
5
|
//#region src/express/middleware.ts
|
|
6
6
|
/**
|
package/dist/express.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.cjs","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;CAC9E,OAAO,OAAO,KAAwB,KAAe,SAAsC;EACzF,IAAI;GACF,cAAA,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,qBAAA,wBAAwB,KAAK,OAAO,MAAM,GAClD;IACV,IAAI,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"express.cjs","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;CAC9E,OAAO,OAAO,KAAwB,KAAe,SAAsC;EACzF,IAAI;GACF,cAAA,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,qBAAA,wBAAwB,KAAK,OAAO,MAAM,GAClD;IACV,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO;KACP,MAAM;IACR,CAAC;IACD;GACF;GAGA,IAAI,mBAAmD;GAEvD,IAAI,QAAQ,kBAAkB;IAC5B,mBAAmB,MAAM,QAAQ,iBAAiB;KAChD,cAAc,QAAQ;KACtB,SAAS;IACX,CAAC;IAED,IAAI,CAAC,kBAAkB;KACrB,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;MACnB,OAAO;MACP,MAAM;KACR,CAAC;KACD;IACF;GACF;GAGA,MAAM,SAAS,MAAM,OAAO,aAC1B,QAAQ,UACR,QAAQ,QACR,EACE,SAAS,EACP,UAAU,oBAAoB,KAAA,EAChC,EACF,GACA,GACF;GAEA,cAAA,WAAW,WAAW;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,YAAY,OAAO;IACnB,GAAI,OAAO,aAAa,CAAC,IAAI,EAAE,QAAQ,OAAO,OAAO;GACvD,CAAC;GAED,IAAI,CAAC,OAAO,YAAY;IACtB,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO;KACP,MAAM;KACN,QAAQ,OAAO;IACjB,CAAC;IACD;GACF;GAGA,IAA2B,gBAAgB;GAG3C,KAAK;EACP,SAAS,OAAO;GAEd,IAAI,iBAAiB,eAAA,qBAAqB;IACxC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAA,oBAAoB;IACvC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;KACZ,QAAQ,MAAM;IAChB,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAA,iBAAiB;IACpC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAA,eAAe;IAClC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAGA,KAAK,KAAK;EACZ;CACF;AACF"}
|
package/dist/express.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-
|
|
2
|
-
import { a as NotFoundError, i as GrantServerError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-
|
|
1
|
+
import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-DxGOMZu5.js";
|
|
2
|
+
import { a as NotFoundError, i as GrantServerError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-CYELoFEG.js";
|
|
3
3
|
import { t as debugGrant } from "./debug-EjP-cuYr.js";
|
|
4
4
|
//#region src/express/middleware.ts
|
|
5
5
|
/**
|
package/dist/express.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;CAC9E,OAAO,OAAO,KAAwB,KAAe,SAAsC;EACzF,IAAI;GACF,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,wBAAwB,KAAK,OAAO,MAAM,GAClD;IACV,IAAI,OAAO,GAAG,
|
|
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;CAC9E,OAAO,OAAO,KAAwB,KAAe,SAAsC;EACzF,IAAI;GACF,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,wBAAwB,KAAK,OAAO,MAAM,GAClD;IACV,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO;KACP,MAAM;IACR,CAAC;IACD;GACF;GAGA,IAAI,mBAAmD;GAEvD,IAAI,QAAQ,kBAAkB;IAC5B,mBAAmB,MAAM,QAAQ,iBAAiB;KAChD,cAAc,QAAQ;KACtB,SAAS;IACX,CAAC;IAED,IAAI,CAAC,kBAAkB;KACrB,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;MACnB,OAAO;MACP,MAAM;KACR,CAAC;KACD;IACF;GACF;GAGA,MAAM,SAAS,MAAM,OAAO,aAC1B,QAAQ,UACR,QAAQ,QACR,EACE,SAAS,EACP,UAAU,oBAAoB,KAAA,EAChC,EACF,GACA,GACF;GAEA,WAAW,WAAW;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,YAAY,OAAO;IACnB,GAAI,OAAO,aAAa,CAAC,IAAI,EAAE,QAAQ,OAAO,OAAO;GACvD,CAAC;GAED,IAAI,CAAC,OAAO,YAAY;IACtB,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO;KACP,MAAM;KACN,QAAQ,OAAO;IACjB,CAAC;IACD;GACF;GAGA,IAA2B,gBAAgB;GAG3C,KAAK;EACP,SAAS,OAAO;GAEd,IAAI,iBAAiB,qBAAqB;IACxC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,oBAAoB;IACvC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;KACZ,QAAQ,MAAM;IAChB,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,iBAAiB;IACpC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAe;IAClC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK;KACnB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAGA,KAAK,KAAK;EACZ;CACF;AACF"}
|
package/dist/fastify.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_grant_client = require("./grant-client-
|
|
3
|
-
const require_errors = require("./errors-
|
|
2
|
+
const require_grant_client = require("./grant-client-CDwjehfE.cjs");
|
|
3
|
+
const require_errors = require("./errors-BGaMqKLU.cjs");
|
|
4
4
|
const require_debug = require("./debug-BZtvGkmP.cjs");
|
|
5
5
|
//#region src/fastify/plugin.ts
|
|
6
6
|
/**
|
package/dist/fastify.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;CAC3E,OAAO,OAAO,SAAmC,UAAuC;EACtF,IAAI;GACF,cAAA,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,qBAAA,wBAAwB,SAAS,OAAO,MAAM,GACtD;IACV,MAAM,OAAO,GAAG,
|
|
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;CAC3E,OAAO,OAAO,SAAmC,UAAuC;EACtF,IAAI;GACF,cAAA,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,qBAAA,wBAAwB,SAAS,OAAO,MAAM,GACtD;IACV,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO;KACP,MAAM;IACR,CAAC;IACD;GACF;GAGA,IAAI,mBAAmD;GAEvD,IAAI,QAAQ,kBAAkB;IAC5B,mBAAmB,MAAM,QAAQ,iBAAiB;KAChD,cAAc,QAAQ;KACb;IACX,CAAC;IAED,IAAI,CAAC,kBAAkB;KACrB,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;MACrB,OAAO;MACP,MAAM;KACR,CAAC;KACD;IACF;GACF;GAGA,MAAM,SAAS,MAAM,OAAO,aAC1B,QAAQ,UACR,QAAQ,QACR,EACE,SAAS,EACP,UAAU,oBAAoB,KAAA,EAChC,EACF,GACA,OACF;GAEA,cAAA,WAAW,WAAW;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,YAAY,OAAO;IACnB,GAAI,OAAO,aAAa,CAAC,IAAI,EAAE,QAAQ,OAAO,OAAO;GACvD,CAAC;GAED,IAAI,CAAC,OAAO,YAAY;IACtB,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO;KACP,MAAM;KACN,QAAQ,OAAO;IACjB,CAAC;IACD;GACF;GAGA,QAAQ,gBAAgB;EAC1B,SAAS,OAAO;GAEd,IAAI,iBAAiB,eAAA,qBAAqB;IACxC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAA,oBAAoB;IACvC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;KACZ,QAAQ,MAAM;IAChB,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAA,iBAAiB;IACpC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAA,eAAe;IAClC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAGA,MAAM;EACR;CACF;AACF;;;;;;;;;;;;;;;;;;;;AA2BA,IAAa,cAAsD,OAAO,SAAS,YAAY;CAC7F,MAAM,QAAQ,IAAI,qBAAA,YAAY,OAAO;CAGrC,QAAQ,SAAS,SAAS,KAAK;AACjC"}
|
package/dist/fastify.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-
|
|
2
|
-
import { a as NotFoundError, i as GrantServerError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-
|
|
1
|
+
import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-DxGOMZu5.js";
|
|
2
|
+
import { a as NotFoundError, i as GrantServerError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-CYELoFEG.js";
|
|
3
3
|
import { t as debugGrant } from "./debug-EjP-cuYr.js";
|
|
4
4
|
//#region src/fastify/plugin.ts
|
|
5
5
|
/**
|
package/dist/fastify.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastify.mjs","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;CAC3E,OAAO,OAAO,SAAmC,UAAuC;EACtF,IAAI;GACF,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,wBAAwB,SAAS,OAAO,MAAM,GACtD;IACV,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"fastify.mjs","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;CAC3E,OAAO,OAAO,SAAmC,UAAuC;EACtF,IAAI;GACF,WAAW,WAAW;IAAE,UAAU,QAAQ;IAAU,QAAQ,QAAQ;GAAO,CAAC;GAI5E,IAAI,CAAC,MADe,wBAAwB,SAAS,OAAO,MAAM,GACtD;IACV,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO;KACP,MAAM;IACR,CAAC;IACD;GACF;GAGA,IAAI,mBAAmD;GAEvD,IAAI,QAAQ,kBAAkB;IAC5B,mBAAmB,MAAM,QAAQ,iBAAiB;KAChD,cAAc,QAAQ;KACb;IACX,CAAC;IAED,IAAI,CAAC,kBAAkB;KACrB,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;MACrB,OAAO;MACP,MAAM;KACR,CAAC;KACD;IACF;GACF;GAGA,MAAM,SAAS,MAAM,OAAO,aAC1B,QAAQ,UACR,QAAQ,QACR,EACE,SAAS,EACP,UAAU,oBAAoB,KAAA,EAChC,EACF,GACA,OACF;GAEA,WAAW,WAAW;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,YAAY,OAAO;IACnB,GAAI,OAAO,aAAa,CAAC,IAAI,EAAE,QAAQ,OAAO,OAAO;GACvD,CAAC;GAED,IAAI,CAAC,OAAO,YAAY;IACtB,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO;KACP,MAAM;KACN,QAAQ,OAAO;IACjB,CAAC;IACD;GACF;GAGA,QAAQ,gBAAgB;EAC1B,SAAS,OAAO;GAEd,IAAI,iBAAiB,qBAAqB;IACxC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,oBAAoB;IACvC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;KACZ,QAAQ,MAAM;IAChB,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,iBAAiB;IACpC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAEA,IAAI,iBAAiB,eAAe;IAClC,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;KACrB,OAAO,MAAM;KACb,MAAM,MAAM;IACd,CAAC;IACD;GACF;GAGA,MAAM;EACR;CACF;AACF;;;;;;;;;;;;;;;;;;;;AA2BA,IAAa,cAAsD,OAAO,SAAS,YAAY;CAC7F,MAAM,QAAQ,IAAI,YAAY,OAAO;CAGrC,QAAQ,SAAS,SAAS,KAAK;AACjC"}
|
|
@@ -62,7 +62,7 @@ async function extractTokenFromRequest(request, config) {
|
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
64
|
//#endregion
|
|
65
|
-
//#region \0@oxc-project+runtime@0.
|
|
65
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/typeof.js
|
|
66
66
|
function _typeof(o) {
|
|
67
67
|
"@babel/helpers - typeof";
|
|
68
68
|
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
@@ -72,7 +72,7 @@ function _typeof(o) {
|
|
|
72
72
|
}, _typeof(o);
|
|
73
73
|
}
|
|
74
74
|
//#endregion
|
|
75
|
-
//#region \0@oxc-project+runtime@0.
|
|
75
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/toPrimitive.js
|
|
76
76
|
function toPrimitive(t, r) {
|
|
77
77
|
if ("object" != _typeof(t) || !t) return t;
|
|
78
78
|
var e = t[Symbol.toPrimitive];
|
|
@@ -84,13 +84,13 @@ function toPrimitive(t, r) {
|
|
|
84
84
|
return ("string" === r ? String : Number)(t);
|
|
85
85
|
}
|
|
86
86
|
//#endregion
|
|
87
|
-
//#region \0@oxc-project+runtime@0.
|
|
87
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/toPropertyKey.js
|
|
88
88
|
function toPropertyKey(t) {
|
|
89
89
|
var i = toPrimitive(t, "string");
|
|
90
90
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
91
91
|
}
|
|
92
92
|
//#endregion
|
|
93
|
-
//#region \0@oxc-project+runtime@0.
|
|
93
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/defineProperty.js
|
|
94
94
|
function _defineProperty(e, r, t) {
|
|
95
95
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
96
96
|
value: t,
|
|
@@ -225,4 +225,4 @@ Object.defineProperty(exports, "extractTokenFromRequest", {
|
|
|
225
225
|
}
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
-
//# sourceMappingURL=grant-client-
|
|
228
|
+
//# sourceMappingURL=grant-client-CDwjehfE.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant-client-CDwjehfE.cjs","names":[],"sources":["../src/utils/token-extractor.ts","../src/grant-client.ts"],"sourcesContent":["import type { GrantServerConfig } from '../types';\n\n/**\n * Extract Bearer token from Authorization header\n */\nexport function extractBearerToken(authHeader: string | undefined): string | null {\n if (!authHeader) return null;\n\n const parts = authHeader.trim().split(/\\s+/);\n if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {\n return null;\n }\n\n return parts[1];\n}\n\n/**\n * Parse cookie header string into key-value pairs\n */\nfunction parseCookieHeader(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n cookieHeader.split(';').forEach((cookie) => {\n const [name, ...rest] = cookie.trim().split('=');\n if (name && rest.length > 0) {\n cookies[name.trim()] = rest.join('=').trim();\n }\n });\n\n return cookies;\n}\n\n/**\n * Extract token from cookies\n */\nexport function extractTokenFromCookies(\n cookieHeader: string | undefined,\n cookieName: string\n): string | null {\n if (!cookieHeader) return null;\n\n const cookies = parseCookieHeader(cookieHeader);\n return cookies[cookieName] || null;\n}\n\n/**\n * Extract token from Express request\n */\nexport async function extractTokenFromRequest(\n request: unknown,\n config: GrantServerConfig\n): Promise<string | null> {\n // Use custom token resolver if provided\n if (config.getToken) {\n const token = await config.getToken(request);\n if (token) return token;\n }\n\n // Try to extract from request (Express-style or Web API / NextRequest)\n const req = request as {\n headers?:\n { authorization?: string; cookie?: string } | { get: (name: string) => string | null };\n cookies?: Record<string, string>;\n };\n\n if (!req?.headers) return null;\n\n // Web API Headers (NextRequest, Request): headers.get(name)\n const headersGet = (req.headers as { get?: (name: string) => string | null }).get;\n if (typeof headersGet === 'function') {\n const authHeader = headersGet.call(req.headers, 'authorization');\n const bearerToken = extractBearerToken(authHeader ?? undefined);\n if (bearerToken) return bearerToken;\n const cookieName = config.cookieName || 'grant-access-token';\n const cookieHeader = headersGet.call(req.headers, 'cookie');\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n return null;\n }\n\n // Express-style: headers.authorization, headers.cookie\n const plainHeaders = req.headers as { authorization?: string; cookie?: string };\n const authHeader = plainHeaders.authorization;\n const bearerToken = extractBearerToken(authHeader);\n if (bearerToken) return bearerToken;\n\n const cookieName = config.cookieName || 'grant-access-token';\n\n if (req.cookies?.[cookieName]) {\n return req.cookies[cookieName];\n }\n\n const cookieHeader = plainHeaders.cookie;\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n\n return null;\n}\n","import type { AuthorizationResult, GrantServerConfig, PermissionCheckOptions } from './types';\nimport { extractTokenFromRequest } from './utils/token-extractor';\n\n/**\n * Grant Client for server-side applications\n *\n * Makes HTTP requests to the Grant API to check permissions.\n * Uses API-key exchanged tokens (scope in JWT claims); token-based and cookie-based extraction supported.\n * No caching - server-side caching is handled by grant-api.\n */\nexport class GrantClient {\n public readonly config: Required<Pick<GrantServerConfig, 'apiUrl'>> & GrantServerConfig;\n\n constructor(config: GrantServerConfig) {\n this.config = config;\n }\n\n // ============================================================================\n // Public API - Permission Checks\n // ============================================================================\n\n /**\n * Check if the current user has a specific permission\n *\n * @example\n * ```ts\n * const canEdit = await grant.isGranted('document', 'update', { scope });\n * if (canEdit) {\n * // Proceed with operation\n * }\n * ```\n */\n async isGranted(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<boolean> {\n const result = await this.isAuthorized(resource, action, options, request);\n return result.authorized;\n }\n\n /**\n * Check authorization with full result details\n *\n * @example\n * ```ts\n * const result = await grant.isAuthorized('document', 'update', { scope }, request);\n * if (!result.authorized) {\n * console.log('Denied:', result.reason);\n * }\n * ```\n */\n async isAuthorized(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<AuthorizationResult> {\n try {\n // API expects: { permission: { resource, action }, context: { resource?: any }, scope?: { tenant, id } }\n const response = await this.fetchWithAuth(\n '/api/auth/is-authorized',\n {\n method: 'POST',\n body: JSON.stringify({\n permission: {\n resource,\n action,\n },\n context: {\n resource: options?.context?.resource || null,\n },\n }),\n },\n request\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n return {\n authorized: false,\n reason: error.message || `API error: ${response.status}`,\n };\n }\n\n const json = await response.json();\n // API returns { success: true, data: { authorized, ... } }\n const result: AuthorizationResult = json.data ?? json;\n return result;\n } catch (error) {\n return {\n authorized: false,\n reason: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Extract token from a request object\n * Useful for middleware that needs to extract token before making authorization calls\n */\n async getTokenFromRequest(request: unknown): Promise<string | null> {\n return extractTokenFromRequest(request, this.config);\n }\n\n // ============================================================================\n // Private - HTTP & Authentication\n // ============================================================================\n\n /**\n * Make an authenticated fetch request (token from request; no refresh).\n */\n private async fetchWithAuth(\n url: string,\n init?: RequestInit,\n request?: unknown\n ): Promise<Response> {\n return this.doFetch(url, init, request);\n }\n\n /**\n * Perform the actual fetch request\n */\n private async doFetch(url: string, init?: RequestInit, request?: unknown): Promise<Response> {\n const fetchFn = this.config.fetch ?? globalThis.fetch;\n const fullUrl = url.startsWith('http') ? url : `${this.config.apiUrl}${url}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(init?.headers as Record<string, string>),\n };\n\n // Add authorization header if token is available\n if (request) {\n const token = await extractTokenFromRequest(request, this.config);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Forward cookies if using cookie-based auth\n const req = request as { headers?: { cookie?: string } };\n if (req.headers?.cookie) {\n headers['Cookie'] = req.headers.cookie;\n }\n }\n\n return fetchFn(fullUrl, {\n ...init,\n headers,\n // Include cookies for same-origin requests (supports cookie-based auth)\n credentials: this.config.credentials ?? 'include',\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -62,7 +62,7 @@ async function extractTokenFromRequest(request, config) {
|
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
64
|
//#endregion
|
|
65
|
-
//#region \0@oxc-project+runtime@0.
|
|
65
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/typeof.js
|
|
66
66
|
function _typeof(o) {
|
|
67
67
|
"@babel/helpers - typeof";
|
|
68
68
|
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
@@ -72,7 +72,7 @@ function _typeof(o) {
|
|
|
72
72
|
}, _typeof(o);
|
|
73
73
|
}
|
|
74
74
|
//#endregion
|
|
75
|
-
//#region \0@oxc-project+runtime@0.
|
|
75
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/toPrimitive.js
|
|
76
76
|
function toPrimitive(t, r) {
|
|
77
77
|
if ("object" != _typeof(t) || !t) return t;
|
|
78
78
|
var e = t[Symbol.toPrimitive];
|
|
@@ -84,13 +84,13 @@ function toPrimitive(t, r) {
|
|
|
84
84
|
return ("string" === r ? String : Number)(t);
|
|
85
85
|
}
|
|
86
86
|
//#endregion
|
|
87
|
-
//#region \0@oxc-project+runtime@0.
|
|
87
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/toPropertyKey.js
|
|
88
88
|
function toPropertyKey(t) {
|
|
89
89
|
var i = toPrimitive(t, "string");
|
|
90
90
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
91
91
|
}
|
|
92
92
|
//#endregion
|
|
93
|
-
//#region \0@oxc-project+runtime@0.
|
|
93
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/defineProperty.js
|
|
94
94
|
function _defineProperty(e, r, t) {
|
|
95
95
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
96
96
|
value: t,
|
|
@@ -202,4 +202,4 @@ var GrantClient = class {
|
|
|
202
202
|
//#endregion
|
|
203
203
|
export { extractTokenFromRequest as i, _defineProperty as n, extractBearerToken as r, GrantClient as t };
|
|
204
204
|
|
|
205
|
-
//# sourceMappingURL=grant-client-
|
|
205
|
+
//# sourceMappingURL=grant-client-DxGOMZu5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant-client-DxGOMZu5.js","names":[],"sources":["../src/utils/token-extractor.ts","../src/grant-client.ts"],"sourcesContent":["import type { GrantServerConfig } from '../types';\n\n/**\n * Extract Bearer token from Authorization header\n */\nexport function extractBearerToken(authHeader: string | undefined): string | null {\n if (!authHeader) return null;\n\n const parts = authHeader.trim().split(/\\s+/);\n if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {\n return null;\n }\n\n return parts[1];\n}\n\n/**\n * Parse cookie header string into key-value pairs\n */\nfunction parseCookieHeader(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n cookieHeader.split(';').forEach((cookie) => {\n const [name, ...rest] = cookie.trim().split('=');\n if (name && rest.length > 0) {\n cookies[name.trim()] = rest.join('=').trim();\n }\n });\n\n return cookies;\n}\n\n/**\n * Extract token from cookies\n */\nexport function extractTokenFromCookies(\n cookieHeader: string | undefined,\n cookieName: string\n): string | null {\n if (!cookieHeader) return null;\n\n const cookies = parseCookieHeader(cookieHeader);\n return cookies[cookieName] || null;\n}\n\n/**\n * Extract token from Express request\n */\nexport async function extractTokenFromRequest(\n request: unknown,\n config: GrantServerConfig\n): Promise<string | null> {\n // Use custom token resolver if provided\n if (config.getToken) {\n const token = await config.getToken(request);\n if (token) return token;\n }\n\n // Try to extract from request (Express-style or Web API / NextRequest)\n const req = request as {\n headers?:\n { authorization?: string; cookie?: string } | { get: (name: string) => string | null };\n cookies?: Record<string, string>;\n };\n\n if (!req?.headers) return null;\n\n // Web API Headers (NextRequest, Request): headers.get(name)\n const headersGet = (req.headers as { get?: (name: string) => string | null }).get;\n if (typeof headersGet === 'function') {\n const authHeader = headersGet.call(req.headers, 'authorization');\n const bearerToken = extractBearerToken(authHeader ?? undefined);\n if (bearerToken) return bearerToken;\n const cookieName = config.cookieName || 'grant-access-token';\n const cookieHeader = headersGet.call(req.headers, 'cookie');\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n return null;\n }\n\n // Express-style: headers.authorization, headers.cookie\n const plainHeaders = req.headers as { authorization?: string; cookie?: string };\n const authHeader = plainHeaders.authorization;\n const bearerToken = extractBearerToken(authHeader);\n if (bearerToken) return bearerToken;\n\n const cookieName = config.cookieName || 'grant-access-token';\n\n if (req.cookies?.[cookieName]) {\n return req.cookies[cookieName];\n }\n\n const cookieHeader = plainHeaders.cookie;\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n\n return null;\n}\n","import type { AuthorizationResult, GrantServerConfig, PermissionCheckOptions } from './types';\nimport { extractTokenFromRequest } from './utils/token-extractor';\n\n/**\n * Grant Client for server-side applications\n *\n * Makes HTTP requests to the Grant API to check permissions.\n * Uses API-key exchanged tokens (scope in JWT claims); token-based and cookie-based extraction supported.\n * No caching - server-side caching is handled by grant-api.\n */\nexport class GrantClient {\n public readonly config: Required<Pick<GrantServerConfig, 'apiUrl'>> & GrantServerConfig;\n\n constructor(config: GrantServerConfig) {\n this.config = config;\n }\n\n // ============================================================================\n // Public API - Permission Checks\n // ============================================================================\n\n /**\n * Check if the current user has a specific permission\n *\n * @example\n * ```ts\n * const canEdit = await grant.isGranted('document', 'update', { scope });\n * if (canEdit) {\n * // Proceed with operation\n * }\n * ```\n */\n async isGranted(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<boolean> {\n const result = await this.isAuthorized(resource, action, options, request);\n return result.authorized;\n }\n\n /**\n * Check authorization with full result details\n *\n * @example\n * ```ts\n * const result = await grant.isAuthorized('document', 'update', { scope }, request);\n * if (!result.authorized) {\n * console.log('Denied:', result.reason);\n * }\n * ```\n */\n async isAuthorized(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<AuthorizationResult> {\n try {\n // API expects: { permission: { resource, action }, context: { resource?: any }, scope?: { tenant, id } }\n const response = await this.fetchWithAuth(\n '/api/auth/is-authorized',\n {\n method: 'POST',\n body: JSON.stringify({\n permission: {\n resource,\n action,\n },\n context: {\n resource: options?.context?.resource || null,\n },\n }),\n },\n request\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n return {\n authorized: false,\n reason: error.message || `API error: ${response.status}`,\n };\n }\n\n const json = await response.json();\n // API returns { success: true, data: { authorized, ... } }\n const result: AuthorizationResult = json.data ?? json;\n return result;\n } catch (error) {\n return {\n authorized: false,\n reason: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Extract token from a request object\n * Useful for middleware that needs to extract token before making authorization calls\n */\n async getTokenFromRequest(request: unknown): Promise<string | null> {\n return extractTokenFromRequest(request, this.config);\n }\n\n // ============================================================================\n // Private - HTTP & Authentication\n // ============================================================================\n\n /**\n * Make an authenticated fetch request (token from request; no refresh).\n */\n private async fetchWithAuth(\n url: string,\n init?: RequestInit,\n request?: unknown\n ): Promise<Response> {\n return this.doFetch(url, init, request);\n }\n\n /**\n * Perform the actual fetch request\n */\n private async doFetch(url: string, init?: RequestInit, request?: unknown): Promise<Response> {\n const fetchFn = this.config.fetch ?? globalThis.fetch;\n const fullUrl = url.startsWith('http') ? url : `${this.config.apiUrl}${url}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(init?.headers as Record<string, string>),\n };\n\n // Add authorization header if token is available\n if (request) {\n const token = await extractTokenFromRequest(request, this.config);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Forward cookies if using cookie-based auth\n const req = request as { headers?: { cookie?: string } };\n if (req.headers?.cookie) {\n headers['Cookie'] = req.headers.cookie;\n }\n }\n\n return fetchFn(fullUrl, {\n ...init,\n headers,\n // Include cookies for same-origin requests (supports cookie-based auth)\n credentials: this.config.credentials ?? 'include',\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_grant_client = require("./grant-client-
|
|
3
|
-
const require_errors = require("./errors-
|
|
2
|
+
const require_grant_client = require("./grant-client-CDwjehfE.cjs");
|
|
3
|
+
const require_errors = require("./errors-BGaMqKLU.cjs");
|
|
4
4
|
exports.AuthenticationError = require_errors.AuthenticationError;
|
|
5
5
|
exports.AuthorizationError = require_errors.AuthorizationError;
|
|
6
6
|
exports.BadRequestError = require_errors.BadRequestError;
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { i as extractTokenFromRequest, r as extractBearerToken, t as GrantClient } from "./grant-client-
|
|
2
|
-
import { a as NotFoundError, i as GrantServerError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-
|
|
1
|
+
import { i as extractTokenFromRequest, r as extractBearerToken, t as GrantClient } from "./grant-client-DxGOMZu5.js";
|
|
2
|
+
import { a as NotFoundError, i as GrantServerError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-CYELoFEG.js";
|
|
3
3
|
export { AuthenticationError, AuthorizationError, BadRequestError, GrantClient, GrantServerError, NotFoundError, extractBearerToken, extractTokenFromRequest };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grant.guard.d.ts","sourceRoot":"","sources":["../../src/nest/grant.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAMjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAM9C,kJAAkJ;AAClJ,eAAO,MAAM,WAAW,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGtE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AA6BD,qBACa,UAAW,YAAW,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJ/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAoB;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAY;gBAGE,MAAM,EAAE,WAAW,EAC1D,kBAAkB,CAAC,EAAE,SAAS,GAAG,iBAAiB;IASpD,OAAO,CAAC,qBAAqB;
|
|
1
|
+
{"version":3,"file":"grant.guard.d.ts","sourceRoot":"","sources":["../../src/nest/grant.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAMjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAM9C,kJAAkJ;AAClJ,eAAO,MAAM,WAAW,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGtE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AA6BD,qBACa,UAAW,YAAW,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJ/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAoB;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAY;gBAGE,MAAM,EAAE,WAAW,EAC1D,kBAAkB,CAAC,EAAE,SAAS,GAAG,iBAAiB;IASpD,OAAO,CAAC,qBAAqB;IAqBvB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CA0E/D"}
|
package/dist/nest.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_grant_client = require("./grant-client-
|
|
2
|
+
const require_grant_client = require("./grant-client-CDwjehfE.cjs");
|
|
3
3
|
const require_debug = require("./debug-BZtvGkmP.cjs");
|
|
4
4
|
let _nestjs_common = require("@nestjs/common");
|
|
5
5
|
//#region src/nest/grant.decorator.ts
|
|
@@ -15,7 +15,7 @@ function Grant(resourceOrOptions, action) {
|
|
|
15
15
|
} : resourceOrOptions);
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
|
-
//#region \0@oxc-project+runtime@0.
|
|
18
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorate.js
|
|
19
19
|
function __decorate(decorators, target, key, desc) {
|
|
20
20
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
21
21
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -44,12 +44,12 @@ var GrantModule = _GrantModule = class GrantModule {
|
|
|
44
44
|
};
|
|
45
45
|
GrantModule = _GrantModule = __decorate([(0, _nestjs_common.Global)(), (0, _nestjs_common.Module)({})], GrantModule);
|
|
46
46
|
//#endregion
|
|
47
|
-
//#region \0@oxc-project+runtime@0.
|
|
47
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorateMetadata.js
|
|
48
48
|
function __decorateMetadata(k, v) {
|
|
49
49
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
50
50
|
}
|
|
51
51
|
//#endregion
|
|
52
|
-
//#region \0@oxc-project+runtime@0.
|
|
52
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorateParam.js
|
|
53
53
|
function __decorateParam(paramIndex, decorator) {
|
|
54
54
|
return function(target, key) {
|
|
55
55
|
decorator(target, key, paramIndex);
|
package/dist/nest.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nest.cjs","names":[],"sources":["../src/nest/grant.decorator.ts","../src/nest/grant.module.ts","../src/nest/grant.guard.ts"],"sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for Grant options (resource, action) on route handlers.\n * Used by GrantGuard when options are not passed in the constructor.\n */\nexport const GRANT_OPTIONS_KEY = 'grant:options';\n\n/**\n * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).\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}\n\n/**\n * Decorator to require a Grant permission on a route.\n * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.\n *\n * @example\n * ```ts\n * @Get()\n * @Grant('Document', 'Query')\n * @UseGuards(GrantGuard)\n * list() { ... }\n *\n * @Post()\n * @Grant('Document', 'Create')\n * @UseGuards(GrantGuard)\n * create(@Body() dto: CreateDocumentDto) { ... }\n * ```\n *\n * For dynamic resource resolution (resourceResolver), use the guard with explicit options:\n * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`\n */\nexport function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;\nexport function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;\nexport function Grant(\n resourceOrOptions: string | GrantOptions,\n action?: string\n): ReturnType<typeof SetMetadata> {\n const options: GrantOptions =\n typeof resourceOrOptions === 'string' && action !== undefined\n ? { resource: resourceOrOptions, action }\n : (resourceOrOptions as GrantOptions);\n return SetMetadata(GRANT_OPTIONS_KEY, options);\n}\n","import { DynamicModule, Global, Module } from '@nestjs/common';\n\nimport { GrantClient } from '../grant-client';\nimport type { GrantServerConfig } from '../types';\n\nexport const GRANT_CLIENT = 'GrantClient';\n\n/**\n * NestJS module that provides GrantClient for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * import { GrantModule } from '@grantjs/server/nest';\n *\n * @Module({\n * imports: [\n * GrantModule.forRoot({\n * apiUrl: process.env.GRANT_API_URL!,\n * getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * Then inject GrantClient in controllers and use with GrantGuard.\n */\n@Global()\n@Module({})\nexport class GrantModule {\n static forRoot(config: GrantServerConfig): DynamicModule {\n const client = new GrantClient(config);\n return {\n module: GrantModule,\n providers: [\n { provide: GrantClient, useValue: client },\n { provide: GRANT_CLIENT, useValue: client },\n ],\n exports: [GrantClient, GRANT_CLIENT],\n };\n }\n}\n","import {\n CanActivate,\n ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n NotFoundException,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\nimport { GRANT_OPTIONS_KEY } from './grant.decorator';\nimport { GRANT_CLIENT } from './grant.module';\n\n/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */\nexport const GRANT_GUARD = 'GrantGuard';\n\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport type { GrantOptions } from './grant.decorator';\n\n/**\n * Extended request type with Grant authorization result (for use in controllers).\n */\nexport interface AuthorizedRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Guard options when using explicit options (includes optional resourceResolver).\n */\nexport interface GrantGuardOptions {\n resource: string;\n action: string;\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * NestJS guard that checks Grant authorization before allowing access to a route.\n *\n * Use in one of two ways:\n *\n * 1. With decorator (resource/action from metadata):\n * - Import GrantModule.forRoot() in AppModule. In a feature module, add\n * `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).\n * - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).\n *\n * 2. With explicit options (supports resourceResolver):\n * - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))\n *\n * On success, the authorization result is attached to the request as request.authorization.\n * On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).\n */\nfunction isGrantGuardOptions(x: unknown): x is GrantGuardOptions {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'resource' in x &&\n 'action' in x &&\n typeof (x as GrantGuardOptions).resource === 'string' &&\n typeof (x as GrantGuardOptions).action === 'string'\n );\n}\n\n@Injectable()\nexport class GrantGuard implements CanActivate {\n private readonly explicitOptions?: GrantGuardOptions;\n private readonly reflector?: Reflector;\n\n constructor(\n @Inject(GRANT_CLIENT) private readonly client: GrantClient,\n reflectorOrOptions?: Reflector | GrantGuardOptions\n ) {\n if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) {\n this.explicitOptions = reflectorOrOptions;\n } else {\n this.reflector = reflectorOrOptions as Reflector | undefined;\n }\n }\n\n private getOptionsFromContext(context: ExecutionContext): GrantOptions | undefined {\n const handler = context.getHandler();\n const clazz = context.getClass();\n // Prefer Reflector when available (Nest DI)\n if (this.reflector) {\n const fromHandler = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, handler);\n const fromClass = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, clazz);\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n // Fallback: read metadata via Reflect (works when Reflector is not injected, e.g. ESM/CJS boundary)\n if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {\n const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler) as\n | GrantOptions\n | undefined;\n const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz) as GrantOptions | undefined;\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n return undefined;\n }\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n\n const options = this.explicitOptions ?? this.getOptionsFromContext(context);\n\n if (options?.resource && options?.action) {\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n hasReflector: !!this.reflector,\n handler: context.getHandler()?.name,\n controller: context.getClass()?.name,\n });\n }\n\n if (!options || !options.resource || !options.action) {\n throw new ForbiddenException(\n 'Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.'\n );\n }\n\n if (!this.client) {\n throw new ForbiddenException(\n 'GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.'\n );\n }\n\n const token = await extractTokenFromRequest(request, this.client.config);\n if (!token) {\n throw new UnauthorizedException({ error: 'Unauthorized', code: 'UNAUTHENTICATED' });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n const opts = options as GrantGuardOptions;\n if (opts.resourceResolver) {\n resolvedResource = await opts.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n throw new NotFoundException({ error: 'Resource not found', code: 'NOT_FOUND' });\n }\n }\n\n const result = await this.client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Nest', {\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 throw new ForbiddenException({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n }\n\n (request as AuthorizedRequest).authorization = result;\n return true;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nest.cjs","names":[],"sources":["../src/nest/grant.decorator.ts","../src/nest/grant.module.ts","../src/nest/grant.guard.ts"],"sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for Grant options (resource, action) on route handlers.\n * Used by GrantGuard when options are not passed in the constructor.\n */\nexport const GRANT_OPTIONS_KEY = 'grant:options';\n\n/**\n * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).\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}\n\n/**\n * Decorator to require a Grant permission on a route.\n * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.\n *\n * @example\n * ```ts\n * @Get()\n * @Grant('Document', 'Query')\n * @UseGuards(GrantGuard)\n * list() { ... }\n *\n * @Post()\n * @Grant('Document', 'Create')\n * @UseGuards(GrantGuard)\n * create(@Body() dto: CreateDocumentDto) { ... }\n * ```\n *\n * For dynamic resource resolution (resourceResolver), use the guard with explicit options:\n * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`\n */\nexport function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;\nexport function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;\nexport function Grant(\n resourceOrOptions: string | GrantOptions,\n action?: string\n): ReturnType<typeof SetMetadata> {\n const options: GrantOptions =\n typeof resourceOrOptions === 'string' && action !== undefined\n ? { resource: resourceOrOptions, action }\n : (resourceOrOptions as GrantOptions);\n return SetMetadata(GRANT_OPTIONS_KEY, options);\n}\n","import { DynamicModule, Global, Module } from '@nestjs/common';\n\nimport { GrantClient } from '../grant-client';\nimport type { GrantServerConfig } from '../types';\n\nexport const GRANT_CLIENT = 'GrantClient';\n\n/**\n * NestJS module that provides GrantClient for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * import { GrantModule } from '@grantjs/server/nest';\n *\n * @Module({\n * imports: [\n * GrantModule.forRoot({\n * apiUrl: process.env.GRANT_API_URL!,\n * getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * Then inject GrantClient in controllers and use with GrantGuard.\n */\n@Global()\n@Module({})\nexport class GrantModule {\n static forRoot(config: GrantServerConfig): DynamicModule {\n const client = new GrantClient(config);\n return {\n module: GrantModule,\n providers: [\n { provide: GrantClient, useValue: client },\n { provide: GRANT_CLIENT, useValue: client },\n ],\n exports: [GrantClient, GRANT_CLIENT],\n };\n }\n}\n","import {\n CanActivate,\n ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n NotFoundException,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\nimport { GRANT_OPTIONS_KEY } from './grant.decorator';\nimport { GRANT_CLIENT } from './grant.module';\n\n/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */\nexport const GRANT_GUARD = 'GrantGuard';\n\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport type { GrantOptions } from './grant.decorator';\n\n/**\n * Extended request type with Grant authorization result (for use in controllers).\n */\nexport interface AuthorizedRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Guard options when using explicit options (includes optional resourceResolver).\n */\nexport interface GrantGuardOptions {\n resource: string;\n action: string;\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * NestJS guard that checks Grant authorization before allowing access to a route.\n *\n * Use in one of two ways:\n *\n * 1. With decorator (resource/action from metadata):\n * - Import GrantModule.forRoot() in AppModule. In a feature module, add\n * `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).\n * - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).\n *\n * 2. With explicit options (supports resourceResolver):\n * - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))\n *\n * On success, the authorization result is attached to the request as request.authorization.\n * On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).\n */\nfunction isGrantGuardOptions(x: unknown): x is GrantGuardOptions {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'resource' in x &&\n 'action' in x &&\n typeof (x as GrantGuardOptions).resource === 'string' &&\n typeof (x as GrantGuardOptions).action === 'string'\n );\n}\n\n@Injectable()\nexport class GrantGuard implements CanActivate {\n private readonly explicitOptions?: GrantGuardOptions;\n private readonly reflector?: Reflector;\n\n constructor(\n @Inject(GRANT_CLIENT) private readonly client: GrantClient,\n reflectorOrOptions?: Reflector | GrantGuardOptions\n ) {\n if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) {\n this.explicitOptions = reflectorOrOptions;\n } else {\n this.reflector = reflectorOrOptions as Reflector | undefined;\n }\n }\n\n private getOptionsFromContext(context: ExecutionContext): GrantOptions | undefined {\n const handler = context.getHandler();\n const clazz = context.getClass();\n // Prefer Reflector when available (Nest DI)\n if (this.reflector) {\n const fromHandler = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, handler);\n const fromClass = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, clazz);\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n // Fallback: read metadata via Reflect (works when Reflector is not injected, e.g. ESM/CJS boundary)\n if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {\n const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler) as\n GrantOptions | undefined;\n const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz) as GrantOptions | undefined;\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n return undefined;\n }\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n\n const options = this.explicitOptions ?? this.getOptionsFromContext(context);\n\n if (options?.resource && options?.action) {\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n hasReflector: !!this.reflector,\n handler: context.getHandler()?.name,\n controller: context.getClass()?.name,\n });\n }\n\n if (!options || !options.resource || !options.action) {\n throw new ForbiddenException(\n 'Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.'\n );\n }\n\n if (!this.client) {\n throw new ForbiddenException(\n 'GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.'\n );\n }\n\n const token = await extractTokenFromRequest(request, this.client.config);\n if (!token) {\n throw new UnauthorizedException({ error: 'Unauthorized', code: 'UNAUTHENTICATED' });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n const opts = options as GrantGuardOptions;\n if (opts.resourceResolver) {\n resolvedResource = await opts.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n throw new NotFoundException({ error: 'Resource not found', code: 'NOT_FOUND' });\n }\n }\n\n const result = await this.client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Nest', {\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 throw new ForbiddenException({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n }\n\n (request as AuthorizedRequest).authorization = result;\n return true;\n }\n}\n"],"mappings":"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"}
|
package/dist/nest.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as extractTokenFromRequest, n as _defineProperty, t as GrantClient } from "./grant-client-
|
|
1
|
+
import { i as extractTokenFromRequest, n as _defineProperty, t as GrantClient } from "./grant-client-DxGOMZu5.js";
|
|
2
2
|
import { t as debugGrant } from "./debug-EjP-cuYr.js";
|
|
3
3
|
import { ForbiddenException, Global, Inject, Injectable, Module, NotFoundException, SetMetadata, UnauthorizedException } from "@nestjs/common";
|
|
4
4
|
//#region src/nest/grant.decorator.ts
|
|
@@ -14,7 +14,7 @@ function Grant(resourceOrOptions, action) {
|
|
|
14
14
|
} : resourceOrOptions);
|
|
15
15
|
}
|
|
16
16
|
//#endregion
|
|
17
|
-
//#region \0@oxc-project+runtime@0.
|
|
17
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorate.js
|
|
18
18
|
function __decorate(decorators, target, key, desc) {
|
|
19
19
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
20
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -43,12 +43,12 @@ var GrantModule = _GrantModule = class GrantModule {
|
|
|
43
43
|
};
|
|
44
44
|
GrantModule = _GrantModule = __decorate([Global(), Module({})], GrantModule);
|
|
45
45
|
//#endregion
|
|
46
|
-
//#region \0@oxc-project+runtime@0.
|
|
46
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorateMetadata.js
|
|
47
47
|
function __decorateMetadata(k, v) {
|
|
48
48
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
49
49
|
}
|
|
50
50
|
//#endregion
|
|
51
|
-
//#region \0@oxc-project+runtime@0.
|
|
51
|
+
//#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorateParam.js
|
|
52
52
|
function __decorateParam(paramIndex, decorator) {
|
|
53
53
|
return function(target, key) {
|
|
54
54
|
decorator(target, key, paramIndex);
|
package/dist/nest.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nest.mjs","names":[],"sources":["../src/nest/grant.decorator.ts","../src/nest/grant.module.ts","../src/nest/grant.guard.ts"],"sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for Grant options (resource, action) on route handlers.\n * Used by GrantGuard when options are not passed in the constructor.\n */\nexport const GRANT_OPTIONS_KEY = 'grant:options';\n\n/**\n * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).\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}\n\n/**\n * Decorator to require a Grant permission on a route.\n * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.\n *\n * @example\n * ```ts\n * @Get()\n * @Grant('Document', 'Query')\n * @UseGuards(GrantGuard)\n * list() { ... }\n *\n * @Post()\n * @Grant('Document', 'Create')\n * @UseGuards(GrantGuard)\n * create(@Body() dto: CreateDocumentDto) { ... }\n * ```\n *\n * For dynamic resource resolution (resourceResolver), use the guard with explicit options:\n * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`\n */\nexport function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;\nexport function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;\nexport function Grant(\n resourceOrOptions: string | GrantOptions,\n action?: string\n): ReturnType<typeof SetMetadata> {\n const options: GrantOptions =\n typeof resourceOrOptions === 'string' && action !== undefined\n ? { resource: resourceOrOptions, action }\n : (resourceOrOptions as GrantOptions);\n return SetMetadata(GRANT_OPTIONS_KEY, options);\n}\n","import { DynamicModule, Global, Module } from '@nestjs/common';\n\nimport { GrantClient } from '../grant-client';\nimport type { GrantServerConfig } from '../types';\n\nexport const GRANT_CLIENT = 'GrantClient';\n\n/**\n * NestJS module that provides GrantClient for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * import { GrantModule } from '@grantjs/server/nest';\n *\n * @Module({\n * imports: [\n * GrantModule.forRoot({\n * apiUrl: process.env.GRANT_API_URL!,\n * getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * Then inject GrantClient in controllers and use with GrantGuard.\n */\n@Global()\n@Module({})\nexport class GrantModule {\n static forRoot(config: GrantServerConfig): DynamicModule {\n const client = new GrantClient(config);\n return {\n module: GrantModule,\n providers: [\n { provide: GrantClient, useValue: client },\n { provide: GRANT_CLIENT, useValue: client },\n ],\n exports: [GrantClient, GRANT_CLIENT],\n };\n }\n}\n","import {\n CanActivate,\n ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n NotFoundException,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\nimport { GRANT_OPTIONS_KEY } from './grant.decorator';\nimport { GRANT_CLIENT } from './grant.module';\n\n/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */\nexport const GRANT_GUARD = 'GrantGuard';\n\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport type { GrantOptions } from './grant.decorator';\n\n/**\n * Extended request type with Grant authorization result (for use in controllers).\n */\nexport interface AuthorizedRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Guard options when using explicit options (includes optional resourceResolver).\n */\nexport interface GrantGuardOptions {\n resource: string;\n action: string;\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * NestJS guard that checks Grant authorization before allowing access to a route.\n *\n * Use in one of two ways:\n *\n * 1. With decorator (resource/action from metadata):\n * - Import GrantModule.forRoot() in AppModule. In a feature module, add\n * `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).\n * - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).\n *\n * 2. With explicit options (supports resourceResolver):\n * - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))\n *\n * On success, the authorization result is attached to the request as request.authorization.\n * On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).\n */\nfunction isGrantGuardOptions(x: unknown): x is GrantGuardOptions {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'resource' in x &&\n 'action' in x &&\n typeof (x as GrantGuardOptions).resource === 'string' &&\n typeof (x as GrantGuardOptions).action === 'string'\n );\n}\n\n@Injectable()\nexport class GrantGuard implements CanActivate {\n private readonly explicitOptions?: GrantGuardOptions;\n private readonly reflector?: Reflector;\n\n constructor(\n @Inject(GRANT_CLIENT) private readonly client: GrantClient,\n reflectorOrOptions?: Reflector | GrantGuardOptions\n ) {\n if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) {\n this.explicitOptions = reflectorOrOptions;\n } else {\n this.reflector = reflectorOrOptions as Reflector | undefined;\n }\n }\n\n private getOptionsFromContext(context: ExecutionContext): GrantOptions | undefined {\n const handler = context.getHandler();\n const clazz = context.getClass();\n // Prefer Reflector when available (Nest DI)\n if (this.reflector) {\n const fromHandler = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, handler);\n const fromClass = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, clazz);\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n // Fallback: read metadata via Reflect (works when Reflector is not injected, e.g. ESM/CJS boundary)\n if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {\n const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler) as\n | GrantOptions\n | undefined;\n const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz) as GrantOptions | undefined;\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n return undefined;\n }\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n\n const options = this.explicitOptions ?? this.getOptionsFromContext(context);\n\n if (options?.resource && options?.action) {\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n hasReflector: !!this.reflector,\n handler: context.getHandler()?.name,\n controller: context.getClass()?.name,\n });\n }\n\n if (!options || !options.resource || !options.action) {\n throw new ForbiddenException(\n 'Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.'\n );\n }\n\n if (!this.client) {\n throw new ForbiddenException(\n 'GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.'\n );\n }\n\n const token = await extractTokenFromRequest(request, this.client.config);\n if (!token) {\n throw new UnauthorizedException({ error: 'Unauthorized', code: 'UNAUTHENTICATED' });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n const opts = options as GrantGuardOptions;\n if (opts.resourceResolver) {\n resolvedResource = await opts.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n throw new NotFoundException({ error: 'Resource not found', code: 'NOT_FOUND' });\n }\n }\n\n const result = await this.client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Nest', {\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 throw new ForbiddenException({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n }\n\n (request as AuthorizedRequest).authorization = result;\n return true;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nest.mjs","names":[],"sources":["../src/nest/grant.decorator.ts","../src/nest/grant.module.ts","../src/nest/grant.guard.ts"],"sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for Grant options (resource, action) on route handlers.\n * Used by GrantGuard when options are not passed in the constructor.\n */\nexport const GRANT_OPTIONS_KEY = 'grant:options';\n\n/**\n * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).\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}\n\n/**\n * Decorator to require a Grant permission on a route.\n * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.\n *\n * @example\n * ```ts\n * @Get()\n * @Grant('Document', 'Query')\n * @UseGuards(GrantGuard)\n * list() { ... }\n *\n * @Post()\n * @Grant('Document', 'Create')\n * @UseGuards(GrantGuard)\n * create(@Body() dto: CreateDocumentDto) { ... }\n * ```\n *\n * For dynamic resource resolution (resourceResolver), use the guard with explicit options:\n * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`\n */\nexport function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;\nexport function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;\nexport function Grant(\n resourceOrOptions: string | GrantOptions,\n action?: string\n): ReturnType<typeof SetMetadata> {\n const options: GrantOptions =\n typeof resourceOrOptions === 'string' && action !== undefined\n ? { resource: resourceOrOptions, action }\n : (resourceOrOptions as GrantOptions);\n return SetMetadata(GRANT_OPTIONS_KEY, options);\n}\n","import { DynamicModule, Global, Module } from '@nestjs/common';\n\nimport { GrantClient } from '../grant-client';\nimport type { GrantServerConfig } from '../types';\n\nexport const GRANT_CLIENT = 'GrantClient';\n\n/**\n * NestJS module that provides GrantClient for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * import { GrantModule } from '@grantjs/server/nest';\n *\n * @Module({\n * imports: [\n * GrantModule.forRoot({\n * apiUrl: process.env.GRANT_API_URL!,\n * getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * Then inject GrantClient in controllers and use with GrantGuard.\n */\n@Global()\n@Module({})\nexport class GrantModule {\n static forRoot(config: GrantServerConfig): DynamicModule {\n const client = new GrantClient(config);\n return {\n module: GrantModule,\n providers: [\n { provide: GrantClient, useValue: client },\n { provide: GRANT_CLIENT, useValue: client },\n ],\n exports: [GrantClient, GRANT_CLIENT],\n };\n }\n}\n","import {\n CanActivate,\n ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n NotFoundException,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\nimport { GRANT_OPTIONS_KEY } from './grant.decorator';\nimport { GRANT_CLIENT } from './grant.module';\n\n/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */\nexport const GRANT_GUARD = 'GrantGuard';\n\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport type { GrantOptions } from './grant.decorator';\n\n/**\n * Extended request type with Grant authorization result (for use in controllers).\n */\nexport interface AuthorizedRequest {\n authorization?: AuthorizationResult;\n}\n\n/**\n * Guard options when using explicit options (includes optional resourceResolver).\n */\nexport interface GrantGuardOptions {\n resource: string;\n action: string;\n resourceResolver?: ResourceResolver;\n}\n\n/**\n * NestJS guard that checks Grant authorization before allowing access to a route.\n *\n * Use in one of two ways:\n *\n * 1. With decorator (resource/action from metadata):\n * - Import GrantModule.forRoot() in AppModule. In a feature module, add\n * `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).\n * - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).\n *\n * 2. With explicit options (supports resourceResolver):\n * - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))\n *\n * On success, the authorization result is attached to the request as request.authorization.\n * On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).\n */\nfunction isGrantGuardOptions(x: unknown): x is GrantGuardOptions {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'resource' in x &&\n 'action' in x &&\n typeof (x as GrantGuardOptions).resource === 'string' &&\n typeof (x as GrantGuardOptions).action === 'string'\n );\n}\n\n@Injectable()\nexport class GrantGuard implements CanActivate {\n private readonly explicitOptions?: GrantGuardOptions;\n private readonly reflector?: Reflector;\n\n constructor(\n @Inject(GRANT_CLIENT) private readonly client: GrantClient,\n reflectorOrOptions?: Reflector | GrantGuardOptions\n ) {\n if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) {\n this.explicitOptions = reflectorOrOptions;\n } else {\n this.reflector = reflectorOrOptions as Reflector | undefined;\n }\n }\n\n private getOptionsFromContext(context: ExecutionContext): GrantOptions | undefined {\n const handler = context.getHandler();\n const clazz = context.getClass();\n // Prefer Reflector when available (Nest DI)\n if (this.reflector) {\n const fromHandler = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, handler);\n const fromClass = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, clazz);\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n // Fallback: read metadata via Reflect (works when Reflector is not injected, e.g. ESM/CJS boundary)\n if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {\n const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler) as\n GrantOptions | undefined;\n const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz) as GrantOptions | undefined;\n if (fromHandler) return fromHandler;\n if (fromClass) return fromClass;\n }\n return undefined;\n }\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n\n const options = this.explicitOptions ?? this.getOptionsFromContext(context);\n\n if (options?.resource && options?.action) {\n debugGrant('Nest', {\n resource: options.resource,\n action: options.action,\n hasReflector: !!this.reflector,\n handler: context.getHandler()?.name,\n controller: context.getClass()?.name,\n });\n }\n\n if (!options || !options.resource || !options.action) {\n throw new ForbiddenException(\n 'Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.'\n );\n }\n\n if (!this.client) {\n throw new ForbiddenException(\n 'GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.'\n );\n }\n\n const token = await extractTokenFromRequest(request, this.client.config);\n if (!token) {\n throw new UnauthorizedException({ error: 'Unauthorized', code: 'UNAUTHENTICATED' });\n }\n\n let resolvedResource: Record<string, unknown> | null = null;\n\n const opts = options as GrantGuardOptions;\n if (opts.resourceResolver) {\n resolvedResource = await opts.resourceResolver({\n resourceSlug: options.resource,\n request,\n });\n if (!resolvedResource) {\n throw new NotFoundException({ error: 'Resource not found', code: 'NOT_FOUND' });\n }\n }\n\n const result = await this.client.isAuthorized(\n options.resource,\n options.action,\n {\n context: {\n resource: resolvedResource ?? undefined,\n },\n },\n request\n );\n\n debugGrant('Nest', {\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 throw new ForbiddenException({\n error: 'Forbidden',\n code: 'FORBIDDEN',\n reason: result.reason,\n });\n }\n\n (request as AuthorizedRequest).authorization = result;\n return true;\n }\n}\n"],"mappings":"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"}
|
package/dist/next.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_grant_client = require("./grant-client-
|
|
3
|
-
const require_errors = require("./errors-
|
|
2
|
+
const require_grant_client = require("./grant-client-CDwjehfE.cjs");
|
|
3
|
+
const require_errors = require("./errors-BGaMqKLU.cjs");
|
|
4
4
|
const require_debug = require("./debug-BZtvGkmP.cjs");
|
|
5
5
|
let next_server = require("next/server");
|
|
6
6
|
//#region src/next/with-grant.ts
|
package/dist/next.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-
|
|
2
|
-
import { a as NotFoundError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-
|
|
1
|
+
import { i as extractTokenFromRequest, t as GrantClient } from "./grant-client-DxGOMZu5.js";
|
|
2
|
+
import { a as NotFoundError, n as AuthorizationError, r as BadRequestError, t as AuthenticationError } from "./errors-CYELoFEG.js";
|
|
3
3
|
import { t as debugGrant } from "./debug-EjP-cuYr.js";
|
|
4
4
|
import { NextResponse } from "next/server";
|
|
5
5
|
//#region src/next/with-grant.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-extractor.d.ts","sourceRoot":"","sources":["../../src/utils/token-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAShF;AAkBD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAKf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"token-extractor.d.ts","sourceRoot":"","sources":["../../src/utils/token-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAShF;AAkBD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAKf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkDxB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grantjs/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Server SDK for Grant authorization - Middleware and guards for Express, Fastify, NestJS, and Next.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -72,22 +72,22 @@
|
|
|
72
72
|
"registry": "https://registry.npmjs.org/"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@grantjs/schema": "1.
|
|
75
|
+
"@grantjs/schema": "1.5.1"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@types/express": "^5.0.0",
|
|
79
|
-
"@types/node": "^
|
|
80
|
-
"@vitest/coverage-v8": "^4.1.
|
|
81
|
-
"eslint": "^10.
|
|
79
|
+
"@types/node": "^26.1.1",
|
|
80
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
81
|
+
"eslint": "^10.7.0",
|
|
82
82
|
"express": "^5.1.0",
|
|
83
|
-
"fastify": "^5.
|
|
84
|
-
"next": "^16.2.
|
|
85
|
-
"@nestjs/common": "^11.1.
|
|
86
|
-
"@nestjs/core": "^11.1.
|
|
87
|
-
"typescript": "^6",
|
|
88
|
-
"vite": "^8.0
|
|
89
|
-
"vite-plugin-dts": "^5.0.
|
|
90
|
-
"vitest": "^4.1.
|
|
83
|
+
"fastify": "^5.10.0",
|
|
84
|
+
"next": "^16.2.11",
|
|
85
|
+
"@nestjs/common": "^11.1.28",
|
|
86
|
+
"@nestjs/core": "^11.1.28",
|
|
87
|
+
"typescript": "^6.0.3",
|
|
88
|
+
"vite": "^8.2.0",
|
|
89
|
+
"vite-plugin-dts": "^5.0.3",
|
|
90
|
+
"vitest": "^4.1.10"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"express": "^4 || ^5",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"grant-client-B3ofrQqL.js","names":[],"sources":["../src/utils/token-extractor.ts","../src/grant-client.ts"],"sourcesContent":["import type { GrantServerConfig } from '../types';\n\n/**\n * Extract Bearer token from Authorization header\n */\nexport function extractBearerToken(authHeader: string | undefined): string | null {\n if (!authHeader) return null;\n\n const parts = authHeader.trim().split(/\\s+/);\n if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {\n return null;\n }\n\n return parts[1];\n}\n\n/**\n * Parse cookie header string into key-value pairs\n */\nfunction parseCookieHeader(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n cookieHeader.split(';').forEach((cookie) => {\n const [name, ...rest] = cookie.trim().split('=');\n if (name && rest.length > 0) {\n cookies[name.trim()] = rest.join('=').trim();\n }\n });\n\n return cookies;\n}\n\n/**\n * Extract token from cookies\n */\nexport function extractTokenFromCookies(\n cookieHeader: string | undefined,\n cookieName: string\n): string | null {\n if (!cookieHeader) return null;\n\n const cookies = parseCookieHeader(cookieHeader);\n return cookies[cookieName] || null;\n}\n\n/**\n * Extract token from Express request\n */\nexport async function extractTokenFromRequest(\n request: unknown,\n config: GrantServerConfig\n): Promise<string | null> {\n // Use custom token resolver if provided\n if (config.getToken) {\n const token = await config.getToken(request);\n if (token) return token;\n }\n\n // Try to extract from request (Express-style or Web API / NextRequest)\n const req = request as {\n headers?:\n | { authorization?: string; cookie?: string }\n | { get: (name: string) => string | null };\n cookies?: Record<string, string>;\n };\n\n if (!req?.headers) return null;\n\n // Web API Headers (NextRequest, Request): headers.get(name)\n const headersGet = (req.headers as { get?: (name: string) => string | null }).get;\n if (typeof headersGet === 'function') {\n const authHeader = headersGet.call(req.headers, 'authorization');\n const bearerToken = extractBearerToken(authHeader ?? undefined);\n if (bearerToken) return bearerToken;\n const cookieName = config.cookieName || 'grant-access-token';\n const cookieHeader = headersGet.call(req.headers, 'cookie');\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n return null;\n }\n\n // Express-style: headers.authorization, headers.cookie\n const plainHeaders = req.headers as { authorization?: string; cookie?: string };\n const authHeader = plainHeaders.authorization;\n const bearerToken = extractBearerToken(authHeader);\n if (bearerToken) return bearerToken;\n\n const cookieName = config.cookieName || 'grant-access-token';\n\n if (req.cookies?.[cookieName]) {\n return req.cookies[cookieName];\n }\n\n const cookieHeader = plainHeaders.cookie;\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n\n return null;\n}\n","import type { AuthorizationResult, GrantServerConfig, PermissionCheckOptions } from './types';\nimport { extractTokenFromRequest } from './utils/token-extractor';\n\n/**\n * Grant Client for server-side applications\n *\n * Makes HTTP requests to the Grant API to check permissions.\n * Uses API-key exchanged tokens (scope in JWT claims); token-based and cookie-based extraction supported.\n * No caching - server-side caching is handled by grant-api.\n */\nexport class GrantClient {\n public readonly config: Required<Pick<GrantServerConfig, 'apiUrl'>> & GrantServerConfig;\n\n constructor(config: GrantServerConfig) {\n this.config = config;\n }\n\n // ============================================================================\n // Public API - Permission Checks\n // ============================================================================\n\n /**\n * Check if the current user has a specific permission\n *\n * @example\n * ```ts\n * const canEdit = await grant.isGranted('document', 'update', { scope });\n * if (canEdit) {\n * // Proceed with operation\n * }\n * ```\n */\n async isGranted(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<boolean> {\n const result = await this.isAuthorized(resource, action, options, request);\n return result.authorized;\n }\n\n /**\n * Check authorization with full result details\n *\n * @example\n * ```ts\n * const result = await grant.isAuthorized('document', 'update', { scope }, request);\n * if (!result.authorized) {\n * console.log('Denied:', result.reason);\n * }\n * ```\n */\n async isAuthorized(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<AuthorizationResult> {\n try {\n // API expects: { permission: { resource, action }, context: { resource?: any }, scope?: { tenant, id } }\n const response = await this.fetchWithAuth(\n '/api/auth/is-authorized',\n {\n method: 'POST',\n body: JSON.stringify({\n permission: {\n resource,\n action,\n },\n context: {\n resource: options?.context?.resource || null,\n },\n }),\n },\n request\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n return {\n authorized: false,\n reason: error.message || `API error: ${response.status}`,\n };\n }\n\n const json = await response.json();\n // API returns { success: true, data: { authorized, ... } }\n const result: AuthorizationResult = json.data ?? json;\n return result;\n } catch (error) {\n return {\n authorized: false,\n reason: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Extract token from a request object\n * Useful for middleware that needs to extract token before making authorization calls\n */\n async getTokenFromRequest(request: unknown): Promise<string | null> {\n return extractTokenFromRequest(request, this.config);\n }\n\n // ============================================================================\n // Private - HTTP & Authentication\n // ============================================================================\n\n /**\n * Make an authenticated fetch request (token from request; no refresh).\n */\n private async fetchWithAuth(\n url: string,\n init?: RequestInit,\n request?: unknown\n ): Promise<Response> {\n return this.doFetch(url, init, request);\n }\n\n /**\n * Perform the actual fetch request\n */\n private async doFetch(url: string, init?: RequestInit, request?: unknown): Promise<Response> {\n const fetchFn = this.config.fetch ?? globalThis.fetch;\n const fullUrl = url.startsWith('http') ? url : `${this.config.apiUrl}${url}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(init?.headers as Record<string, string>),\n };\n\n // Add authorization header if token is available\n if (request) {\n const token = await extractTokenFromRequest(request, this.config);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Forward cookies if using cookie-based auth\n const req = request as { headers?: { cookie?: string } };\n if (req.headers?.cookie) {\n headers['Cookie'] = req.headers.cookie;\n }\n }\n\n return fetchFn(fullUrl, {\n ...init,\n headers,\n // Include cookies for same-origin requests (supports cookie-based auth)\n credentials: this.config.credentials ?? 'include',\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"grant-client-ByBQjwxH.cjs","names":[],"sources":["../src/utils/token-extractor.ts","../src/grant-client.ts"],"sourcesContent":["import type { GrantServerConfig } from '../types';\n\n/**\n * Extract Bearer token from Authorization header\n */\nexport function extractBearerToken(authHeader: string | undefined): string | null {\n if (!authHeader) return null;\n\n const parts = authHeader.trim().split(/\\s+/);\n if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {\n return null;\n }\n\n return parts[1];\n}\n\n/**\n * Parse cookie header string into key-value pairs\n */\nfunction parseCookieHeader(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n\n cookieHeader.split(';').forEach((cookie) => {\n const [name, ...rest] = cookie.trim().split('=');\n if (name && rest.length > 0) {\n cookies[name.trim()] = rest.join('=').trim();\n }\n });\n\n return cookies;\n}\n\n/**\n * Extract token from cookies\n */\nexport function extractTokenFromCookies(\n cookieHeader: string | undefined,\n cookieName: string\n): string | null {\n if (!cookieHeader) return null;\n\n const cookies = parseCookieHeader(cookieHeader);\n return cookies[cookieName] || null;\n}\n\n/**\n * Extract token from Express request\n */\nexport async function extractTokenFromRequest(\n request: unknown,\n config: GrantServerConfig\n): Promise<string | null> {\n // Use custom token resolver if provided\n if (config.getToken) {\n const token = await config.getToken(request);\n if (token) return token;\n }\n\n // Try to extract from request (Express-style or Web API / NextRequest)\n const req = request as {\n headers?:\n | { authorization?: string; cookie?: string }\n | { get: (name: string) => string | null };\n cookies?: Record<string, string>;\n };\n\n if (!req?.headers) return null;\n\n // Web API Headers (NextRequest, Request): headers.get(name)\n const headersGet = (req.headers as { get?: (name: string) => string | null }).get;\n if (typeof headersGet === 'function') {\n const authHeader = headersGet.call(req.headers, 'authorization');\n const bearerToken = extractBearerToken(authHeader ?? undefined);\n if (bearerToken) return bearerToken;\n const cookieName = config.cookieName || 'grant-access-token';\n const cookieHeader = headersGet.call(req.headers, 'cookie');\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n return null;\n }\n\n // Express-style: headers.authorization, headers.cookie\n const plainHeaders = req.headers as { authorization?: string; cookie?: string };\n const authHeader = plainHeaders.authorization;\n const bearerToken = extractBearerToken(authHeader);\n if (bearerToken) return bearerToken;\n\n const cookieName = config.cookieName || 'grant-access-token';\n\n if (req.cookies?.[cookieName]) {\n return req.cookies[cookieName];\n }\n\n const cookieHeader = plainHeaders.cookie;\n if (cookieHeader) {\n const cookieToken = extractTokenFromCookies(cookieHeader, cookieName);\n if (cookieToken) return cookieToken;\n }\n\n return null;\n}\n","import type { AuthorizationResult, GrantServerConfig, PermissionCheckOptions } from './types';\nimport { extractTokenFromRequest } from './utils/token-extractor';\n\n/**\n * Grant Client for server-side applications\n *\n * Makes HTTP requests to the Grant API to check permissions.\n * Uses API-key exchanged tokens (scope in JWT claims); token-based and cookie-based extraction supported.\n * No caching - server-side caching is handled by grant-api.\n */\nexport class GrantClient {\n public readonly config: Required<Pick<GrantServerConfig, 'apiUrl'>> & GrantServerConfig;\n\n constructor(config: GrantServerConfig) {\n this.config = config;\n }\n\n // ============================================================================\n // Public API - Permission Checks\n // ============================================================================\n\n /**\n * Check if the current user has a specific permission\n *\n * @example\n * ```ts\n * const canEdit = await grant.isGranted('document', 'update', { scope });\n * if (canEdit) {\n * // Proceed with operation\n * }\n * ```\n */\n async isGranted(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<boolean> {\n const result = await this.isAuthorized(resource, action, options, request);\n return result.authorized;\n }\n\n /**\n * Check authorization with full result details\n *\n * @example\n * ```ts\n * const result = await grant.isAuthorized('document', 'update', { scope }, request);\n * if (!result.authorized) {\n * console.log('Denied:', result.reason);\n * }\n * ```\n */\n async isAuthorized(\n resource: string,\n action: string,\n options?: PermissionCheckOptions,\n request?: unknown\n ): Promise<AuthorizationResult> {\n try {\n // API expects: { permission: { resource, action }, context: { resource?: any }, scope?: { tenant, id } }\n const response = await this.fetchWithAuth(\n '/api/auth/is-authorized',\n {\n method: 'POST',\n body: JSON.stringify({\n permission: {\n resource,\n action,\n },\n context: {\n resource: options?.context?.resource || null,\n },\n }),\n },\n request\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n return {\n authorized: false,\n reason: error.message || `API error: ${response.status}`,\n };\n }\n\n const json = await response.json();\n // API returns { success: true, data: { authorized, ... } }\n const result: AuthorizationResult = json.data ?? json;\n return result;\n } catch (error) {\n return {\n authorized: false,\n reason: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Extract token from a request object\n * Useful for middleware that needs to extract token before making authorization calls\n */\n async getTokenFromRequest(request: unknown): Promise<string | null> {\n return extractTokenFromRequest(request, this.config);\n }\n\n // ============================================================================\n // Private - HTTP & Authentication\n // ============================================================================\n\n /**\n * Make an authenticated fetch request (token from request; no refresh).\n */\n private async fetchWithAuth(\n url: string,\n init?: RequestInit,\n request?: unknown\n ): Promise<Response> {\n return this.doFetch(url, init, request);\n }\n\n /**\n * Perform the actual fetch request\n */\n private async doFetch(url: string, init?: RequestInit, request?: unknown): Promise<Response> {\n const fetchFn = this.config.fetch ?? globalThis.fetch;\n const fullUrl = url.startsWith('http') ? url : `${this.config.apiUrl}${url}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(init?.headers as Record<string, string>),\n };\n\n // Add authorization header if token is available\n if (request) {\n const token = await extractTokenFromRequest(request, this.config);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Forward cookies if using cookie-based auth\n const req = request as { headers?: { cookie?: string } };\n if (req.headers?.cookie) {\n headers['Cookie'] = req.headers.cookie;\n }\n }\n\n return fetchFn(fullUrl, {\n ...init,\n headers,\n // Include cookies for same-origin requests (supports cookie-based auth)\n credentials: this.config.credentials ?? 'include',\n });\n }\n}\n"],"mappings":"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"}
|