{"id":17618,"library":"express-jwt-permissions","title":"Express JWT Permissions Middleware","description":"Express JWT Permissions is an authorization middleware for Node.js applications, designed to work in conjunction with JWT authentication solutions like `express-jwt`. It inspects a decoded JWT token, typically found on `req.user` (or a configurable property), for a permissions array or a space-delimited scope string. The library, currently at stable version 1.3.7, has a moderate release cadence, primarily focusing on security updates, dependency bumps, and TypeScript typing enhancements. Its key differentiator lies in its flexible permission checking logic, supporting simple strings, arrays for AND logic, and nested arrays for complex OR logic combinations of permissions. It also provides configurable options for `requestProperty` and `permissionsProperty` to accommodate diverse JWT payload structures, moving beyond the default `req.user.permissions` pattern, and facilitates custom error handling for permission denials.","status":"active","version":"1.3.7","language":"javascript","source_language":"en","source_url":"https://github.com/MichielDeMey/express-jwt-permissions","tags":["javascript","express","middleware","JWT","permissions","authorization","token","security","typescript"],"install":[{"cmd":"npm install express-jwt-permissions","lang":"bash","label":"npm"},{"cmd":"yarn add express-jwt-permissions","lang":"bash","label":"yarn"},{"cmd":"pnpm add express-jwt-permissions","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Provides conditional middleware execution, allowing routes to bypass permission checks. Upgraded to v2 in express-jwt-permissions v1.3.7.","package":"express-unless","optional":false},{"reason":"This package is designed to be used after `express-jwt` (or similar middleware) has decoded a JWT and attached its payload to the request object. It's a conceptual, not a direct runtime, dependency.","package":"express-jwt","optional":true}],"imports":[{"note":"The primary export is a factory function, typically imported as a default.","wrong":"import { guardFactory } from 'express-jwt-permissions';","symbol":"guardFactory","correct":"import guardFactory from 'express-jwt-permissions';"},{"note":"TypeScript type for configuration options.","wrong":"import GuardOptions from 'express-jwt-permissions';","symbol":"GuardOptions","correct":"import { GuardOptions } from 'express-jwt-permissions';"},{"note":"CommonJS require for the default factory function. The guard instance is then created via `const guard = guardFactory();`","wrong":"const { guardFactory } = require('express-jwt-permissions');","symbol":"require","correct":"const guardFactory = require('express-jwt-permissions');"}],"quickstart":{"code":"import express from 'express';\nimport { expressjwt as jwt } from 'express-jwt';\nimport guardFactory, { GuardOptions } from 'express-jwt-permissions';\n\nconst app = express();\nconst PORT = 3000;\n\n// IMPORTANT: Replace 'YOUR_JWT_SECRET' with a strong secret from environment variables\n// This mock JWT middleware simulates express-jwt's behavior.\nconst mockJwtMiddleware = jwt({\n  secret: process.env.JWT_SECRET || 'supersecretjwtkeythatshouldbemorecomplex', \n  algorithms: ['HS256'],\n  requestProperty: 'auth', // Where the decoded JWT payload will be placed (e.g., req.auth)\n  getToken: (req) => {\n    if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {\n      return req.headers.authorization.split(' ')[1];\n    }\n    return null;\n  }\n}).unless({ path: ['/public'] }); // Example: allow /public without a token\n\napp.use(mockJwtMiddleware);\n\n// Initialize the permission guard, configuring it to look for permissions\n// within the 'auth' property on the request object and specifically in a 'scope' field.\nconst guard = guardFactory({\n  requestProperty: 'auth',\n  permissionsProperty: 'scope'\n} as GuardOptions);\n\n// Public route, accessible without any specific permissions\napp.get('/public', (req, res) => {\n  res.send('Welcome to the public area!');\n});\n\n// Route requiring 'user:read' permission\napp.get('/user/profile', guard.check('user:read'), (req, res) => {\n  res.send(`User profile for ${req.auth?.sub || 'unknown'}. Access granted with user:read.`);\n});\n\n// Route requiring 'admin' OR ('user:write' AND 'user:delete') permissions\napp.post('/admin/manage', guard.check([\n  ['admin'],\n  ['user:write', 'user:delete']\n]), (req, res) => {\n  res.send(`Admin management area. User ${req.auth?.sub || 'unknown'} has required permissions.`);\n});\n\n// Global error handler for permission_denied errors\napp.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {\n  if (err.code === 'permission_denied') {\n    console.error('Permission denied:', err.message);\n    return res.status(403).send('Forbidden: Insufficient permissions.');\n  }\n  next(err); // Pass other errors to the next error handler\n});\n\napp.listen(PORT, () => {\n  console.log(`Server running on http://localhost:${PORT}`);\n  console.log('Test with valid JWTs in Authorization: Bearer <token>');\n  console.log('e.g., token with payload { \"sub\": \"user1\", \"scope\": \"user:read\" }');\n  console.log('e.g., token with payload { \"sub\": \"admin1\", \"scope\": \"admin user:write user:delete\" }');\n});","lang":"typescript","description":"This quickstart demonstrates how to set up `express-jwt-permissions` with Express.js, including a mock `express-jwt` middleware, configurable permission checking, and a global error handler for `permission_denied` errors. It shows single, array (AND), and nested array (OR) permission logic."},"warnings":[{"fix":"Ensure compatibility by testing your application with `express-jwt-permissions@1.3.7`. Direct code changes within `express-jwt-permissions` usage are typically not required, but verify `express-unless` v2 changes do not impact your specific environment or deeper integrations.","message":"Beginning with v1.3.7, `express-jwt-permissions` upgraded its internal `express-unless` dependency to v2. While the primary impact was internal typings, users indirectly relying on `express-unless`'s API or sensitive to transitive major version bumps should review the `express-unless` v2 changelog for potential breaking changes in their specific use cases.","severity":"breaking","affected_versions":">=1.3.7"},{"fix":"Ensure `express-jwt` or a similar JWT decoding middleware is applied *before* any `express-jwt-permissions` middleware. Configure `express-jwt-permissions`'s `requestProperty` if your JWT middleware uses a different property than `req.user` (e.g., `req.auth`).","message":"`express-jwt-permissions` *must* be used after a JWT authentication middleware (e.g., `express-jwt`) that successfully decodes the token and attaches the payload to `req.user` (or a configured `requestProperty`). Without a decoded token on the request object, permission checks will always fail.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Initialize the guard with appropriate configuration options: `const guard = require('express-jwt-permissions')({ requestProperty: 'identity', permissionsProperty: 'scope' });` to match your JWT payload structure.","message":"By default, the middleware expects permissions to be an array or string at `req.user.permissions`. If your decoded JWT token stores permissions in a different location (e.g., `req.auth.scope` or `req.identity.roles`), you *must* configure `requestProperty` and `permissionsProperty` when initializing the guard.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Implement a dedicated error handling middleware *after* all routes and `express-jwt-permissions` checks: `app.use(function (err, req, res, next) { if (err.code === 'permission_denied') { res.status(403).send('Forbidden'); } next(err); });`","message":"When a permission check fails, `express-jwt-permissions` throws an error with `err.code === 'permission_denied'`. If this error is not explicitly caught and handled by a custom Express error middleware, it can lead to an unhandled error, a generic 500 status, or unintended application behavior.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Upgrade to `express-jwt-permissions@1.3.2` or higher to resolve typing compatibility issues, or ensure `esModuleInterop: true` is set in your `tsconfig.json` for older versions.","message":"Prior to v1.3.2, TypeScript projects with `esModuleInterop: false` in `tsconfig.json` might have encountered issues with typings for `express-jwt-permissions` due to how default imports were handled, potentially leading to compilation errors or incorrect type inference.","severity":"deprecated","affected_versions":"<1.3.2"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"fix":"Implement a global Express error handler (after all routes) to specifically catch errors where `err.code === 'permission_denied'` and respond with an appropriate status (e.g., 403 Forbidden).","cause":"A permission check failed, and no Express error handling middleware was defined to catch the `permission_denied` error.","error":"UnhandledPromiseRejectionWarning: Error: permission_denied"},{"fix":"Verify that a JWT authentication middleware is running correctly *before* `express-jwt-permissions`. Ensure `guardFactory` is configured with `requestProperty` and `permissionsProperty` to match the actual location of permissions in your decoded JWT payload.","cause":"The `requestProperty` or `permissionsProperty` options are misconfigured, or the JWT authentication middleware failed to attach a decoded token to the request object, meaning `req.user` (or `req.auth`, etc.) or its `permissions` field is missing.","error":"TypeError: Cannot read properties of undefined (reading 'permissions')"},{"fix":"Ensure the guard factory is called to create an instance: `const guardFactory = require('express-jwt-permissions'); const guard = guardFactory();` or `import guardFactory from 'express-jwt-permissions'; const guard = guardFactory();`","cause":"The `express-jwt-permissions` module was imported or required incorrectly, failing to instantiate the guard factory function. For example, using `require('express-jwt-permissions').check` directly.","error":"TypeError: guard.check is not a function"},{"fix":"Augment the `express` Request type in a declaration file (e.g., `src/types/express.d.ts`): `declare namespace Express { interface Request { user?: { permissions?: string[] | string; [key: string]: any; }; auth?: { scope?: string | string[]; [key: string]: any; }; } }`. Adjust `user` to your `requestProperty` and `permissions` to your `permissionsProperty`.","cause":"When using TypeScript, the `Request` type does not inherently know about the properties added by `express-jwt` (like `req.user` or `req.auth`) or `express-jwt-permissions`.","error":"Property 'permissions' does not exist on type 'Request<ParamsDictionary, any, any, Query, Record<string, any>>'."}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}