{"_id":"@bonniernews/bn-oidc-connector-express","_rev":"16-769795507411625ef496299409ed7809","name":"@bonniernews/bn-oidc-connector-express","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@bonniernews/bn-oidc-connector-express","version":"0.0.1","author":{"name":"Bonnier News AB"},"license":"MIT","_id":"@bonniernews/bn-oidc-connector-express@0.0.1","maintainers":[{"name":"markusn","email":"markus@botten.org"},{"name":"mxtr","email":"max.olofsson@bonniernews.se"},{"name":"matsrorbecker","email":"mats@rorbecker.com"},{"name":"dan.karlsson","email":"dan.karlsson@bonniernews.se"},{"name":"jonaswalden","email":"jonas.waldeen@gmail.com"},{"name":"indeedshouts","email":"indeedshouts@gmail.com"},{"name":"marie-winther","email":"marie.winther@bonniernews.se"},{"name":"marcusgronblad","email":"marcus.gronblad@bonniernews.se"},{"name":"axeljohanssonbonniernews","email":"axel.johansson@bonniernews.se"},{"name":"karlbergc","email":"christoffer.karlberg@bonniernews.se"},{"name":"chavah","email":"chavah.forler@bonniernews.se"},{"name":"joelekman","email":"joel.ekman@bonniernews.se"},{"name":"adam.hakansson","email":"adam.hakansson@bonniernews.se"},{"name":"hilleso","email":"marcus.hilleso@expressen.se"},{"name":"gusliden","email":"gusliden@gmail.com"},{"name":"andreas.samuelsson","email":"andreas.samuelsson@bonniernews.se"},{"name":"jzachrisson","email":"jesper.zachrisson@bonniernews.se"},{"name":"norla","email":"mattias.norlander@gmail.com"},{"name":"varneynz","email":"simon.varney@bonniernews.se"},{"name":"aliceboberg","email":"alice.boberg@bonniernews.se"},{"name":"johark","email":"johan.arkad@bonniernews.se"},{"name":"nifo","email":"niklas.forsstrom@bonniernews.se"},{"name":"herbola","email":"herman.jansson@bonniernews.se"},{"name":"vitryssen","email":"andre.nordlund@bonniernews.se"},{"name":"takolander","email":"william.takolander@bonniernews.se"},{"name":"morre","email":"marten.persson@hdsydsvenskan.se"},{"name":"mikael.mattsson","email":"mikael.mattsson@bonniernews.se"},{"name":"schristianssonbn","email":"seb.christiansson@bonniernews.se"},{"name":"mattiasobn","email":"mattias.olla@bonniernews.se"},{"name":"daghall","email":"markus@daghall.se"},{"name":"erandersson","email":"ericandersson1@gmail.com"},{"name":"jonaek","email":"jonathan.ek@bonniernews.se"},{"name":"amundsentb","email":"amundsentb@gmail.com"},{"name":"drgeobn","email":"daniel.rasmussen@bonniernews.se"},{"name":"oscartholander","email":"oscar@tholander.nu"},{"name":"peterpettersson","email":"peter.pettersson@bonniernews.se"},{"name":"jackesa","email":"jakob.pedersen@bonniernews.se"},{"name":"kristofferjansson","email":"kristoffer.jansson@bonniernews.se"},{"name":"kirinja","email":"blom.niklas@gmail.com"},{"name":"kitgus","email":"kit.gustavsson@bonniernews.se"},{"name":"odynvolk","email":"alexi.rahman@r76.se"}],"homepage":"https://github.com/BonnierNews/bn-oidc-connector-express#readme","bugs":{"url":"https://github.com/BonnierNews/bn-oidc-connector-express/issues"},"dist":{"shasum":"c5579f2d068aef0bc935fde10079441b7a1d7fee","tarball":"https://registry.npmjs.org/@bonniernews/bn-oidc-connector-express/-/bn-oidc-connector-express-0.0.1.tgz","fileCount":8,"integrity":"sha512-oc9Iv0iNsvMQWSx7WwSblKrAsz/roXsQJkff4EPaiiXayoxtGj0tUsrSPGnSBUjgUYAwu+ZgdU0MYdm5bCtsOg==","signatures":[{"sig":"MEUCICckKJipZCzoX/BRS2Va3SXxUQwkY91MWdQR/SRKIoz1AiEA79SxWsEq8XxPPez0XwqCEqQQCAdv2I8k4DdGDReQg+U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":29694},"main":"dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{"import":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","require":"./dist/index.cjs"}},"gitHead":"2b439f5627515c8d661899c77c2c8e9d334069fd","scripts":{"lint":"eslint . --cache && npm run typecheck","test":"node --test --test-force-exit --test-reporter ${REPORTER:-spec} --import \"tsx/esm\" --import \"./test/setup.ts\" \"test/**/*.{test,unit,feature}.ts\"","build":"tsup","format":"eslint --fix .","coverage":"c8 npm test && c8 check-coverage","posttest":"npm run lint","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"peterpettersson","email":"peter.pettersson@bonniernews.se"},"repository":{"url":"git+https://github.com/BonnierNews/bn-oidc-connector-express.git","type":"git"},"_npmVersion":"11.6.2","description":"Express middleware for handling user authentication from Bonnier News Fastly Compute OIDC headers","directories":{},"_nodeVersion":"25.2.0","dependencies":{"joi":"^18.0.0","tsx":"^4.3.0","express":"^5.0.0","jwks-rsa":"^3.2.0","jsonwebtoken":"^9.0.2","cookie-parser":"^1.4.7"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","chai":"^6.0.0","nock":"^14.0.4","tsup":"^8.5.0","eslint":"^9.19.0","pem-jwk":"^2.0.0","prettier":"^3.5.3","supertest":"^7.1.1","typescript":"^5.3.0","@types/chai":"^5.0.0","@types/node":"^22.0.0","@types/mocha":"^10.0.1","@types/express":"^5.0.0","@types/pem-jwk":"^2.0.2","@types/supertest":"^6.0.3","@types/cookie-parser":"^1.4.8","@bonniernews/tsconfig":"^0.0.2","@bonniernews/eslint-config":"^2.0.2","@bonniernews/node-test-bdd":"^0.0.4"},"_npmOperationalInternal":{"tmp":"tmp/bn-oidc-connector-express_0.0.1_1763126663353_0.28796070384788686","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"0.0.2":{"name":"@bonniernews/bn-oidc-connector-express","version":"0.0.2","author":{"name":"Bonnier News AB"},"license":"MIT","_id":"@bonniernews/bn-oidc-connector-express@0.0.2","maintainers":[{"name":"markusn","email":"markus@botten.org"},{"name":"mxtr","email":"max.olofsson@bonniernews.se"},{"name":"matsrorbecker","email":"mats@rorbecker.com"},{"name":"dan.karlsson","email":"dan.karlsson@bonniernews.se"},{"name":"jonaswalden","email":"jonas.waldeen@gmail.com"},{"name":"indeedshouts","email":"indeedshouts@gmail.com"},{"name":"marie-winther","email":"marie.winther@bonniernews.se"},{"name":"marcusgronblad","email":"marcus.gronblad@bonniernews.se"},{"name":"axeljohanssonbonniernews","email":"axel.johansson@bonniernews.se"},{"name":"karlbergc","email":"christoffer.karlberg@bonniernews.se"},{"name":"chavah","email":"chavah.forler@bonniernews.se"},{"name":"joelekman","email":"joel.ekman@bonniernews.se"},{"name":"adam.hakansson","email":"adam.hakansson@bonniernews.se"},{"name":"hilleso","email":"marcus.hilleso@expressen.se"},{"name":"gusliden","email":"gusliden@gmail.com"},{"name":"andreas.samuelsson","email":"andreas.samuelsson@bonniernews.se"},{"name":"jzachrisson","email":"jesper.zachrisson@bonniernews.se"},{"name":"norla","email":"mattias.norlander@gmail.com"},{"name":"varneynz","email":"simon.varney@bonniernews.se"},{"name":"aliceboberg","email":"alice.boberg@bonniernews.se"},{"name":"johark","email":"johan.arkad@bonniernews.se"},{"name":"nifo","email":"niklas.forsstrom@bonniernews.se"},{"name":"herbola","email":"herman.jansson@bonniernews.se"},{"name":"vitryssen","email":"andre.nordlund@bonniernews.se"},{"name":"takolander","email":"william.takolander@bonniernews.se"},{"name":"morre","email":"marten.persson@hdsydsvenskan.se"},{"name":"mikael.mattsson","email":"mikael.mattsson@bonniernews.se"},{"name":"schristianssonbn","email":"seb.christiansson@bonniernews.se"},{"name":"mattiasobn","email":"mattias.olla@bonniernews.se"},{"name":"daghall","email":"markus@daghall.se"},{"name":"erandersson","email":"ericandersson1@gmail.com"},{"name":"jonaek","email":"jonathan.ek@bonniernews.se"},{"name":"amundsentb","email":"amundsentb@gmail.com"},{"name":"drgeobn","email":"daniel.rasmussen@bonniernews.se"},{"name":"oscartholander","email":"oscar@tholander.nu"},{"name":"peterpettersson","email":"peter.pettersson@bonniernews.se"},{"name":"jackesa","email":"jakob.pedersen@bonniernews.se"},{"name":"kristofferjansson","email":"kristoffer.jansson@bonniernews.se"},{"name":"kirinja","email":"blom.niklas@gmail.com"},{"name":"kitgus","email":"kit.gustavsson@bonniernews.se"},{"name":"odynvolk","email":"alexi.rahman@r76.se"}],"homepage":"https://github.com/BonnierNews/bn-oidc-connector-express#readme","bugs":{"url":"https://github.com/BonnierNews/bn-oidc-connector-express/issues"},"dist":{"shasum":"a46a22f9cbd5d1a170efce9440cf555bad1eefb9","tarball":"https://registry.npmjs.org/@bonniernews/bn-oidc-connector-express/-/bn-oidc-connector-express-0.0.2.tgz","fileCount":8,"integrity":"sha512-VgVd8VlpvXSyRoowj3DeVTGFRJKbouS3iOzLTug+pVQF0+a1u3E3ZBdjyrtkKQWqaVdfBgNeV0y5i2CPsSOx+w==","signatures":[{"sig":"MEUCIQDKwvhOcZAGW669AJwBsN5lzOEaCeilA7voWw+iALepBgIgUQdvOD9qV2s1f94Zn22WqWYLDgLZEKEAIC2VRULk1AY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bonniernews%2fbn-oidc-connector-express@0.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":29694},"main":"dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{"import":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","require":"./dist/index.cjs"}},"gitHead":"0538a4fa9c19559ccbb96380a79ec2c93ff9ee77","scripts":{"lint":"eslint . --cache && npm run typecheck","test":"node --test --test-force-exit --test-reporter ${REPORTER:-spec} --import \"tsx/esm\" --import \"./test/setup.ts\" \"test/**/*.{test,unit,feature}.ts\"","build":"tsup","format":"eslint --fix .","coverage":"c8 npm test && c8 check-coverage","posttest":"npm run lint","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d034f41f-40b3-46e9-9b3b-b787ca85daef"}},"repository":{"url":"git+https://github.com/BonnierNews/bn-oidc-connector-express.git","type":"git"},"_npmVersion":"11.6.2","description":"Express middleware for handling user authentication from Bonnier News Fastly Compute OIDC headers","directories":{},"_nodeVersion":"22.21.1","dependencies":{"joi":"^18.0.0","tsx":"^4.3.0","express":"^5.0.0","jwks-rsa":"^3.2.0","jsonwebtoken":"^9.0.2","cookie-parser":"^1.4.7"},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","chai":"^6.0.0","nock":"^14.0.4","tsup":"^8.5.0","eslint":"^9.19.0","pem-jwk":"^2.0.0","prettier":"^3.5.3","supertest":"^7.1.1","typescript":"^5.3.0","@types/chai":"^5.0.0","@types/node":"^22.0.0","@types/mocha":"^10.0.1","@types/express":"^5.0.0","@types/pem-jwk":"^2.0.2","@types/supertest":"^6.0.3","@types/cookie-parser":"^1.4.8","@bonniernews/tsconfig":"^0.0.2","@bonniernews/eslint-config":"^2.0.2","@bonniernews/node-test-bdd":"^0.0.4"},"_npmOperationalInternal":{"tmp":"tmp/bn-oidc-connector-express_0.0.2_1763127159387_0.18946186567380097","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2025-11-14T13:24:23.259Z","modified":"2026-09-30T09:05:27.077Z","0.0.1":"2025-11-14T13:24:23.558Z","0.0.2":"2025-11-14T13:32:39.560Z"},"bugs":{"url":"https://github.com/BonnierNews/bn-oidc-connector-express/issues"},"author":{"name":"Bonnier News AB"},"license":"MIT","homepage":"https://github.com/BonnierNews/bn-oidc-connector-express#readme","repository":{"url":"git+https://github.com/BonnierNews/bn-oidc-connector-express.git","type":"git"},"description":"Express middleware for handling user authentication from Bonnier News Fastly Compute OIDC headers","maintainers":[{"name":"adam.hakansson","email":"adam.hakansson@bonniernews.se"},{"name":"adil.aboulkacim","email":"adil.aboulkacim@bonniernews.se"},{"name":"aliceboberg","email":"alice.boberg@bonniernews.se"},{"name":"amundsentb","email":"amundsentb@gmail.com"},{"name":"andreas.samuelsson","email":"andreas.samuelsson@bonniernews.se"},{"name":"axeljohanssonbonniernews","email":"axel.johansson@bonniernews.se"},{"name":"chavah","email":"chavah.forler@bonniernews.se"},{"name":"daghall","email":"markus@daghall.se"},{"name":"dan.karlsson","email":"dan.arola@bonniernews.se"},{"name":"drgeobn","email":"daniel.rasmussen@bonniernews.se"},{"name":"emilbjorklund","email":"emil@thatemil.com"},{"name":"erandersson","email":"ericandersson1@gmail.com"},{"name":"gusliden","email":"gusliden@gmail.com"},{"name":"herbola","email":"herman.jansson@bonniernews.se"},{"name":"hilleso","email":"marcus.hilleso@expressen.se"},{"name":"jackesa","email":"jakob.pedersen@bonniernews.se"},{"name":"joelekman","email":"joel.ekman@bonniernews.se"},{"name":"johankasperi","email":"johan@kasperi.se"},{"name":"johark","email":"johan.arkad@bonniernews.se"},{"name":"jon.eldeklint","email":"jon.eldeklint@bonniernews.se"},{"name":"jonaek","email":"jonathan.ek@bonniernews.se"},{"name":"jzachrisson","email":"jesper.zachrisson@bonniernews.se"},{"name":"karlbergc","email":"christoffer.karlberg@bonniernews.se"},{"name":"kitgus","email":"kit.gustavsson@bonniernews.se"},{"name":"kristofferjansson","email":"kristoffer.jansson@bonniernews.se"},{"name":"lilianj","email":"japlilian@gmail.com"},{"name":"liroliro","email":"basistoscar@gmail.com"},{"name":"marcusgronblad","email":"marcus.gronblad@bonniernews.se"},{"name":"marie-winther","email":"marie.winther@bonniernews.se"},{"name":"markusn","email":"markus@botten.org"},{"name":"matsrorbecker","email":"mats@rorbecker.com"},{"name":"mattiasobn","email":"mattias.olla@bonniernews.se"},{"name":"mikael.mattsson","email":"mikael.mattsson@bonniernews.se"},{"name":"morre","email":"marten.persson@hdsydsvenskan.se"},{"name":"mxtr","email":"max.olofsson@bonniernews.se"},{"name":"nblom","email":"niklas.blom@bonniernews.se"},{"name":"nifo","email":"niklas.forsstrom@bonniernews.se"},{"name":"norla","email":"mattias.norlander@gmail.com"},{"name":"odynvolk","email":"alexi.rahman@r76.se"},{"name":"ollenolle","email":"olle.nordin@bonniernews.se"},{"name":"oscartholander","email":"oscar@tholander.nu"},{"name":"peterpettersson","email":"peter.pettersson@bonniernews.se"},{"name":"schristianssonbn","email":"seb.christiansson@bonniernews.se"},{"name":"takolander","email":"william.takolander@bonniernews.se"},{"name":"torkelberget","email":"andreas.egneblad@bonniernews.se"},{"name":"varneynz","email":"simon.varney@bonniernews.se"},{"name":"vitryssen","email":"andre.nordlund@bonniernews.se"}],"readme":"# @bonniernews/bn-oidc-connector-express\n\nExpress middleware for handling user authentication from Bonnier News Fastly Compute OIDC headers.\n\n## Overview\n\nThis library provides Express.js middleware to handle authentication and authorization for applications running behind Bonnier News's Fastly Compute infrastructure. The Fastly Compute service performs complete OIDC (OpenID Connect) authentication and passes user information to your Node.js application via HTTP headers.\n\n## Installation\n\n```bash\nnpm install @bonniernews/bn-oidc-connector-express\n```\n\n## Quick Start\n\n```typescript\nimport express from \"express\";\nimport { auth, isAuthenticated, isEntitled } from \"@bonniernews/bn-oidc-connector-express\";\n\nconst app = express();\n\n// Apply OIDC middleware to parse headers from Fastly Compute\napp.use(auth());\n\n// Protected route requiring authentication\napp.get(\"/profile\", isAuthenticated, (req, res) => {\n  res.json({\n    user: req.oidc.user,\n    claims: req.oidc.idTokenClaims\n  });\n});\n\n// Protected route requiring specific entitlements\napp.get(\"/premium-content\", isEntitled([\"premium\", \"subscriber\"]), (req, res) => {\n  res.json({ content: \"Premium content here\" });\n});\n\n// Handle authentication/authorization errors\napp.use((err, req, res, next) => {\n  if (err.name === \"UnauthenticatedError\") {\n    return res.status(401).json({ error: \"Authentication required\" });\n  }\n  if (err.name === \"UnauthorizedError\") {\n    return res.status(403).json({ error: \"Insufficient permissions\" });\n  }\n  next(err);\n});\n\napp.listen(3000);\n```\n\n## API Reference\n\n### `auth(options?)`\n\nMain middleware function that sets up OIDC context and parses user headers from Fastly Compute.\n\n**Parameters:**\n- `options` (optional): Configuration object\n  - `headers.user` (string, default: `\"x-bnlogin-user\"`): Name of the HTTP header containing user information\n\n**Returns:** Express Router with OIDC middleware configured\n\n**Example:**\n```typescript\n// Use default header name\napp.use(auth());\n\n// Use custom header name\napp.use(auth({ headers: { user: \"x-custom-user\" } }));\n```\n\n### `isAuthenticated`\n\nMiddleware that requires the user to be authenticated. Throws `UnauthenticatedError` if the user is not logged in.\n\n**Example:**\n```typescript\napp.get(\"/protected\", isAuthenticated, (req, res) => {\n  res.json({ message: \"You are authenticated!\" });\n});\n```\n\n### `isEntitled(validEntitlements)`\n\nMiddleware factory that requires the user to have specific entitlements.\n\n**Parameters:**\n- `validEntitlements` (string[]): Array of entitlement strings. User needs at least one of these entitlements.\n\n**Returns:** Express middleware function\n\n**Throws:**\n- `UnauthenticatedError` if user is not authenticated\n- `UnauthorizedError` if user lacks required entitlements\n\n**Example:**\n```typescript\n// Require any of the specified entitlements\napp.get(\"/admin\", isEntitled([\"admin\", \"superuser\"]), (req, res) => {\n  res.json({ message: \"Admin access granted\" });\n});\n\n// Empty array allows all authenticated users\napp.get(\"/members\", isEntitled([]), (req, res) => {\n  res.json({ message: \"Members only\" });\n});\n```\n\n## Request Object Extension\n\nThe middleware extends the Express Request object with an `oidc` property:\n\n```typescript\ninterface Request {\n  oidc: {\n    isAuthenticated: boolean;\n    user?: {\n      id: string;\n      email?: string;\n    };\n    idTokenClaims?: Record<string, any>;\n    isEntitled: (validEntitlements: string[]) => boolean;\n  };\n}\n```\n\n### Properties\n\n- **`isAuthenticated`**: Boolean indicating if the user is authenticated\n- **`user`**: User object containing basic user information\n  - `id`: User's unique identifier (from `sub` claim)\n  - `email`: User's email address (if available)\n- **`idTokenClaims`**: Raw JWT claims from the ID token\n- **`isEntitled(validEntitlements)`**: Function to check if user has required entitlements\n\n### Example Usage\n\n```typescript\napp.get(\"/api/user\", (req, res) => {\n  if (!req.oidc.isAuthenticated) {\n    return res.status(401).json({ error: \"Not authenticated\" });\n  }\n\n  // Check entitlements programmatically\n  if (req.oidc.isEntitled([\"premium\"])) {\n    // User has premium access\n    return res.json({\n      user: req.oidc.user,\n      isPremium: true,\n      allClaims: req.oidc.idTokenClaims\n    });\n  }\n\n  res.json({\n    user: req.oidc.user,\n    isPremium: false\n  });\n});\n```\n\n## Error Handling\n\nThe library provides specific error types for different authentication/authorization scenarios:\n\n```typescript\nimport {\n  UnauthenticatedError,\n  UnauthorizedError\n} from \"@bonniernews/bn-oidc-connector-express\";\n\napp.use((err, req, res, next) => {\n  if (err instanceof UnauthenticatedError) {\n    return res.status(401).json({\n      error: \"Authentication required\",\n      message: err.message\n    });\n  }\n\n  if (err instanceof UnauthorizedError) {\n    return res.status(403).json({\n      error: \"Insufficient permissions\",\n      message: err.message\n    });\n  }\n\n  // Handle other errors\n  next(err);\n});\n```\n\n### Error Types\n\n- **`UnauthenticatedError`**: User is not logged in\n- **`UnauthorizedError`**: User is authenticated but lacks required permissions\n- **`OidcError`**: Base error class for OIDC-related errors\n\n## How It Works\n\n1. **Fastly Compute OIDC Service**: Handles the complete OIDC flow (authorization, token exchange, validation)\n2. **Header Transmission**: Fastly sets the `x-bnlogin-user` header with JWT claims as JSON\n3. **Middleware Processing**: This library parses the header and creates the OIDC context\n4. **Request Enhancement**: Each request gets an `oidc` object with user information and helper methods\n\n## TypeScript Support\n\nThe library is written in TypeScript and includes full type definitions. The Express Request interface is automatically extended when you import the library.\n\n```typescript\nimport { Request } from \"express\";\n\n// The oidc property is automatically available and typed\nfunction handleRequest(req: Request) {\n  // TypeScript knows about req.oidc\n  if (req.oidc.isAuthenticated) {\n    console.log(`User ID: ${req.oidc.user?.id}`);\n  }\n}\n```\n\n## Configuration\n\n### Custom Header Names\n\nIf your Fastly Compute service uses different header names, you can configure them:\n\n```typescript\napp.use(auth({\n  headers: {\n    user: \"x-custom-user-header\"\n  }\n}));\n```\n\n## Examples\n\n### Basic Authentication Check\n\n```typescript\napp.get(\"/dashboard\", isAuthenticated, (req, res) => {\n  res.render(\"dashboard\", {\n    user: req.oidc.user,\n    userClaims: req.oidc.idTokenClaims\n  });\n});\n```\n\n### Role-Based Access Control\n\n```typescript\n// Admin-only route\napp.get(\"/admin\", isEntitled([\"admin\"]), (req, res) => {\n  res.json({ message: \"Admin dashboard\" });\n});\n\n// Multiple role options\napp.get(\"/content\", isEntitled([\"editor\", \"admin\", \"content-manager\"]), (req, res) => {\n  res.json({ message: \"Content management\" });\n});\n```\n\n### Conditional Logic\n\n```typescript\napp.get(\"/article/:id\", (req, res) => {\n  const article = getArticle(req.params.id);\n\n  // Public article\n  if (!article.isPremium) {\n    return res.json(article);\n  }\n\n  // Premium article - check authentication and entitlements\n  if (!req.oidc.isAuthenticated) {\n    return res.status(401).json({ error: \"Login required for premium content\" });\n  }\n\n  if (!req.oidc.isEntitled([\"premium\", \"subscriber\"])) {\n    return res.status(403).json({ error: \"Premium subscription required\" });\n  }\n\n  res.json(article);\n});\n```\n\n### Programmatic Entitlement Checking\n\n```typescript\napp.get(\"/features\", (req, res) => {\n  const features = {\n    basicFeatures: true,\n    premiumFeatures: req.oidc.isEntitled([\"premium\"]),\n    adminFeatures: req.oidc.isEntitled([\"admin\"]),\n    betaFeatures: req.oidc.isEntitled([\"beta-tester\"])\n  };\n\n  res.json(features);\n});\n```\n","readmeFilename":"README.md"}