{"_id":"@apdev/nest-keycloak-connect","name":"@apdev/nest-keycloak-connect","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@apdev/nest-keycloak-connect","version":"1.0.0","description":"keycloak-nodejs-connect module for Nest - compatible with NestJS 11","author":{"name":"Lucas Paes","email":"lucas.ma.paes@gmail.com"},"license":"MIT","scripts":{"start:dev":"tsc -w","clean":"rimraf dist","build":"tsc","format":"prettier src --write","lint":"eslint src"},"keywords":["nestjs","keycloak","typescript","nestjs11"],"repository":{"type":"git","url":"git+https://github.com/pafrtds/nest-keycloak-connect.git"},"bugs":{"url":"https://github.com/pafrtds/nest-keycloak-connect/issues"},"main":"./dist/index.js","types":"./dist/index.d.ts","typesVersions":{"*":{"dist/index.d.ts":["dist/index.d.ts"],"*":["dist/*"]}},"exports":{".":"./dist/index.js","./*":"./dist/*"},"dependencies":{"jose":"^6.2.2"},"peerDependencies":{"@nestjs/common":">=10.0.0 <13.0.0","@nestjs/core":">=10.0.0 <13.0.0","@nestjs/graphql":">=6"},"devDependencies":{"@nestjs/common":"^11.0.6","@nestjs/core":"^11.0.6","@nestjs/graphql":"^13.0.2","@types/express":"^4.17.21","@types/node":"^18.19.23","class-transformer":"0.5.1","class-validator":"0.14.1","cpr":"3.0.1","eslint":"8.57.0","eslint-config-prettier":"9.1.0","eslint-plugin-prettier":"5.1.3","graphql":"^16.10.0","husky":"^9.0.11","keycloak-connect":"24.0.1","lint-staged":"^15.2.2","prettier":"3.2.5","reflect-metadata":"0.2.1","rimraf":"3.0.2","rxjs":"7.8.1","ts-node":"10.8.2","typescript":"5.4.2","typescript-eslint":"^7.2.0"},"peerDependenciesMeta":{"@nestjs/graphql":{"optional":true}},"volta":{"node":"21.7.1"},"lint-staged":{"**/*.ts":["prettier --write","eslint"]},"_id":"@apdev/nest-keycloak-connect@1.0.0","gitHead":"93059306cb337e9cae19fe357bfe7a08f7e5d69f","homepage":"https://github.com/pafrtds/nest-keycloak-connect#readme","_nodeVersion":"20.18.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-kWfaqWCu0rYqZhR7AZCQg6BZUG1d0abq7uibwH3z+PGpN1ay1ZQg2n3yqBF+NkkraAz0LwaIZX1Zx0dPF/M4iw==","shasum":"9ac2583f2fbfc0b6aca97e0b06aa5b623a721b2d","tarball":"https://registry.npmjs.org/@apdev/nest-keycloak-connect/-/nest-keycloak-connect-1.0.0.tgz","fileCount":51,"unpackedSize":103721,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIC8ojpNn7H9RqJ0xOIh/2qRVpJtvlMo6fljt3zjY5uY3AiAXxWUpg0RUQv4k8FMdAj+YTKIHT4VIOq7QjlIk2AaLlQ=="}]},"_npmUser":{"name":"apdev","email":"cereso.rodrigues@anfitriaoprime.com.br"},"directories":{},"maintainers":[{"name":"apdev","email":"cereso.rodrigues@anfitriaoprime.com.br"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nest-keycloak-connect_1.0.0_1775676893966_0.12975887090986804"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-08T19:34:53.863Z","1.0.0":"2026-04-08T19:34:54.117Z","modified":"2026-04-08T19:34:54.347Z"},"maintainers":[{"name":"apdev","email":"cereso.rodrigues@anfitriaoprime.com.br"}],"description":"keycloak-nodejs-connect module for Nest - compatible with NestJS 11","homepage":"https://github.com/pafrtds/nest-keycloak-connect#readme","keywords":["nestjs","keycloak","typescript","nestjs11"],"repository":{"type":"git","url":"git+https://github.com/pafrtds/nest-keycloak-connect.git"},"author":{"name":"Lucas Paes","email":"lucas.ma.paes@gmail.com"},"bugs":{"url":"https://github.com/pafrtds/nest-keycloak-connect/issues"},"license":"MIT","readme":"<div align=\"center\">\n\n# @pafrtds/nest-keycloak-connect\n\nA Keycloak authentication and authorization module for NestJS, powered by [`jose`](https://github.com/panva/jose).\n\n![NPM Version](https://img.shields.io/npm/v/@pafrtds/nest-keycloak-connect?style=for-the-badge)\n![GitHub License](https://img.shields.io/github/license/pafrtds/nest-keycloak-connect?style=for-the-badge)\n\n</div>\n\n## Compatibility\n\n| Package version | NestJS version |\n| --------------- | -------------- |\n| 1.x             | 10, **11**     |\n\n> This package is fully compatible with **NestJS 11** and uses `jose` for JWT validation instead of the deprecated `keycloak-nodejs-connect`.\n\n## Features\n\n- Protect your resources using [Keycloak's Authorization Services](https://www.keycloak.org/docs/latest/authorization_services/).\n- Simply add `@Resource`, `@Scopes`, `@Roles`, or `@Groups` in your controllers and you're good to go.\n- JWT validation via JWKS using [`jose`](https://github.com/panva/jose) — no deprecated dependencies.\n- Online token validation cache to avoid hitting Keycloak on every request.\n- Compatible with [Fastify](https://github.com/fastify/fastify) platform.\n- **Compatible with NestJS 11.**\n\n## Installation\n\n```bash\nnpm install @pafrtds/nest-keycloak-connect --save\n```\n\n## Getting Started\n\n### Module registration\n\n```typescript\nimport {\n  KeycloakConnectModule,\n  PolicyEnforcementMode,\n  TokenValidation,\n} from '@pafrtds/nest-keycloak-connect';\n\nKeycloakConnectModule.register({\n  authServerUrl: 'http://localhost:8080',\n  realm: 'master',\n  clientId: 'my-nestjs-app',\n  secret: 'secret',\n  policyEnforcement: PolicyEnforcementMode.PERMISSIVE, // optional\n  tokenValidation: TokenValidation.ONLINE,             // optional\n});\n```\n\nAsync registration:\n\n```typescript\nKeycloakConnectModule.registerAsync({\n  useExisting: KeycloakConfigService,\n  imports: [ConfigModule],\n});\n```\n\n#### KeycloakConfigService\n\n```typescript\nimport { Injectable } from '@nestjs/common';\nimport {\n  KeycloakConnectOptions,\n  KeycloakConnectOptionsFactory,\n  PolicyEnforcementMode,\n  TokenValidation,\n} from '@pafrtds/nest-keycloak-connect';\n\n@Injectable()\nexport class KeycloakConfigService implements KeycloakConnectOptionsFactory {\n  createKeycloakConnectOptions(): KeycloakConnectOptions {\n    return {\n      authServerUrl: 'http://localhost:8080',\n      realm: 'master',\n      clientId: 'my-nestjs-app',\n      secret: 'secret',\n      policyEnforcement: PolicyEnforcementMode.PERMISSIVE,\n      tokenValidation: TokenValidation.ONLINE,\n    };\n  }\n}\n```\n\n### Guards\n\nRegister globally via `APP_GUARD` (recommended order):\n\n```typescript\nproviders: [\n  { provide: APP_GUARD, useClass: AuthGuard },\n  { provide: APP_GUARD, useClass: ResourceGuard },\n  { provide: APP_GUARD, useClass: RoleGuard },\n  { provide: APP_GUARD, useClass: GroupGuard },\n];\n```\n\nOr scoped to a controller:\n\n```typescript\n@Controller('cats')\n@UseGuards(AuthGuard, ResourceGuard)\nexport class CatsController {}\n```\n\n## Guards\n\n### AuthGuard\n\nReturns **401 Unauthorized** when the JWT is missing or invalid. Validates tokens against Keycloak's JWKS endpoint.\n\n### ResourceGuard\n\nEnforces resource-level permissions via Keycloak's UMA authorization endpoint. Requires `@Resource` + `@Scopes` on the controller/method.\n\n### RoleGuard\n\nChecks realm or client roles from the token claims. Requires `@Roles` on the method.\n\n### GroupGuard\n\nChecks group membership from the `groups` claim in the JWT. Requires `@Groups` on the method.\n\n> To enable the `groups` claim: Keycloak → Client → Client scopes → Add mapper → **Group Membership** → Token Claim Name: `groups`\n\n## Configuring controllers\n\n```typescript\nimport {\n  Resource, Roles, Scopes, Groups, Public,\n  RoleMatchingMode, GroupMatchingMode, GroupMatch,\n} from '@pafrtds/nest-keycloak-connect';\n\n@Controller()\n@Resource('product')\nexport class ProductController {\n\n  @Get()\n  @Public()\n  async findAll() { ... }\n\n  @Get(':id')\n  @Scopes('view')\n  async findOne() { ... }\n\n  @Post()\n  @Scopes('create')\n  @Roles({ roles: ['manager', 'realm:admin'] })\n  async create() { ... }\n\n  @Delete(':id')\n  @Scopes('delete')\n  @Groups('/org/admins')\n  async remove() { ... }\n\n  @Put(':id')\n  @Scopes('edit')\n  @Groups('/org/admins', '/org/editors')\n  @GroupMatchingMode(GroupMatch.ALL) // must belong to BOTH groups\n  async update() { ... }\n}\n```\n\n## Token Validation Cache\n\nCaches online validation results to avoid calling Keycloak on every request. The TTL is derived from the token's `exp` claim.\n\n> **Note:** revoked tokens may be accepted until the cache entry expires. Use a short `maxTtl` in environments requiring immediate revocation.\n\n```typescript\nKeycloakConnectModule.register({\n  // ...\n  tokenValidation: TokenValidation.ONLINE,\n  tokenCache: {\n    enabled: true,\n    maxTtl: 30, // max 30 seconds, regardless of token lifetime\n  },\n});\n```\n\nTo manually invalidate a token (e.g. after logout):\n\n```typescript\nconstructor(private readonly tokenCache: KeycloakTokenCacheService) {}\n\nasync logout(accessToken: string) {\n  this.tokenCache.invalidate(accessToken);\n}\n```\n\n## Decorators\n\n| Decorator            | Description                                                               |\n| -------------------- | ------------------------------------------------------------------------- |\n| `@KeycloakUser`      | Retrieves the current Keycloak user from the request.                     |\n| `@AccessToken`       | Retrieves the raw access token string from the request.                   |\n| `@ResolvedScopes`    | Retrieves the resolved scopes (used with `@ConditionalScopes`).           |\n| `@EnforcerOptions`   | Keycloak enforcer options for `ResourceGuard`.                            |\n| `@Public`            | Allows unauthenticated access to the route.                               |\n| `@Resource`          | Keycloak resource name (used with `ResourceGuard`).                       |\n| `@Scopes`            | Required scopes on a resource (used with `ResourceGuard`).                |\n| `@ConditionalScopes` | Dynamic scopes resolved at request time.                                  |\n| `@Roles`             | Required realm or client roles (used with `RoleGuard`).                   |\n| `@RoleMatchingMode`  | Sets `RoleMatch.ANY` (default) or `RoleMatch.ALL` for `@Roles`.           |\n| `@Groups`            | Required Keycloak group membership (used with `GroupGuard`).              |\n| `@GroupMatchingMode` | Sets `GroupMatch.ANY` (default) or `GroupMatch.ALL` for `@Groups`.        |\n\n## Token Validation Modes\n\n| Mode      | Description                                                                                  |\n| --------- | -------------------------------------------------------------------------------------------- |\n| `ONLINE`  | Verifies JWT signature via JWKS **and** calls Keycloak's introspection endpoint per request. Detects revoked tokens. |\n| `OFFLINE` | Verifies JWT signature via JWKS only. Fast, no per-request Keycloak calls. Does not detect revocation. |\n| `NONE`    | Skips all validation. Use only in development or internal trusted networks.                  |\n\n## Configuration options\n\n| Option            | Description                                                                 | Default      |\n| ----------------- | --------------------------------------------------------------------------- | ------------ |\n| `authServerUrl`   | Keycloak server URL                                                         | required     |\n| `realm`           | Realm name                                                                  | required     |\n| `clientId`        | Client/Application ID                                                       | required     |\n| `secret`          | Client secret                                                               | required     |\n| `cookieKey`       | Cookie key for JWT extraction                                               | `KEYCLOAK_JWT` |\n| `policyEnforcement` | `PERMISSIVE` or `ENFORCING` for `ResourceGuard`                           | `PERMISSIVE` |\n| `tokenValidation` | `ONLINE`, `OFFLINE`, or `NONE`                                              | `ONLINE`     |\n| `tokenCache`      | Cache config: `{ enabled, maxTtl? }`                                        | disabled     |\n| `multiTenant`     | Multi-tenant options                                                        | —            |\n| `roleMerge`       | `OVERRIDE` or `ALL` for `@Roles` merge strategy                             | `OVERRIDE`   |\n\n## Multi-tenant configuration\n\n```typescript\nKeycloakConnectModule.register({\n  authServerUrl: 'http://localhost:8080',\n  clientId: 'nest-api',\n  secret: 'fallback-secret',\n  multiTenant: {\n    realmResolver: (request) => request.get('host').split('.')[0],\n    realmSecretResolver: (realm) => secretsMap[realm],\n    realmClientIdResolver: (realm) => clientIdMap[realm],\n    realmAuthServerUrlResolver: (realm) => authServerUrlMap[realm],\n  },\n});\n```\n\n## License\n\nMIT — Copyright (c) 2020 John Joshua Ferrer, 2026 Lucas Paes\n","readmeFilename":"README.md","_rev":"1-1475569d1a1d6bb16e0f9efcdd95caba"}