@pafrtds/nest-keycloak-connect
v1.0.2
Published
keycloak-nodejs-connect module for Nest - compatible with NestJS 11
Maintainers
Readme
@pafrtds/nest-keycloak-connect
A Keycloak authentication and authorization module for NestJS, powered by jose.
Compatibility
| Package version | NestJS version | | --------------- | -------------- | | 1.x | 10, 11 |
This package is fully compatible with NestJS 11 and uses
josefor JWT validation instead of the deprecatedkeycloak-nodejs-connect.
Features
- Protect your resources using Keycloak's Authorization Services.
- Simply add
@Resource,@Scopes,@Roles, or@Groupsin your controllers and you're good to go. - JWT validation via JWKS using
jose— no deprecated dependencies. - Online token validation cache to avoid hitting Keycloak on every request.
- Compatible with Fastify platform.
- Compatible with NestJS 11.
Installation
npm install @pafrtds/nest-keycloak-connect --saveGetting Started
Module registration
import {
KeycloakConnectModule,
PolicyEnforcementMode,
TokenValidation,
} from '@pafrtds/nest-keycloak-connect';
KeycloakConnectModule.register({
authServerUrl: 'http://localhost:8080',
realm: 'master',
clientId: 'my-nestjs-app',
secret: 'secret',
policyEnforcement: PolicyEnforcementMode.PERMISSIVE, // optional
tokenValidation: TokenValidation.ONLINE, // optional
});Async registration:
KeycloakConnectModule.registerAsync({
useExisting: KeycloakConfigService,
imports: [ConfigModule],
});KeycloakConfigService
import { Injectable } from '@nestjs/common';
import {
KeycloakConnectOptions,
KeycloakConnectOptionsFactory,
PolicyEnforcementMode,
TokenValidation,
} from '@pafrtds/nest-keycloak-connect';
@Injectable()
export class KeycloakConfigService implements KeycloakConnectOptionsFactory {
createKeycloakConnectOptions(): KeycloakConnectOptions {
return {
authServerUrl: 'http://localhost:8080',
realm: 'master',
clientId: 'my-nestjs-app',
secret: 'secret',
policyEnforcement: PolicyEnforcementMode.PERMISSIVE,
tokenValidation: TokenValidation.ONLINE,
};
}
}Guards
Register globally via APP_GUARD (recommended order):
providers: [
{ provide: APP_GUARD, useClass: AuthGuard },
{ provide: APP_GUARD, useClass: ResourceGuard },
{ provide: APP_GUARD, useClass: RoleGuard },
{ provide: APP_GUARD, useClass: GroupGuard },
];Or scoped to a controller:
@Controller('cats')
@UseGuards(AuthGuard, ResourceGuard)
export class CatsController {}Guards
AuthGuard
Returns 401 Unauthorized when the JWT is missing or invalid. Validates tokens against Keycloak's JWKS endpoint.
ResourceGuard
Enforces resource-level permissions via Keycloak's UMA authorization endpoint. Requires @Resource + @Scopes on the controller/method.
RoleGuard
Checks realm or client roles from the token claims. Requires @Roles on the method.
GroupGuard
Checks group membership from the groups claim in the JWT. Requires @Groups on the method.
To enable the
groupsclaim: Keycloak → Client → Client scopes → Add mapper → Group Membership → Token Claim Name:groups
Configuring controllers
import {
Resource, Roles, Scopes, Groups, Public,
RoleMatchingMode, GroupMatchingMode, GroupMatch,
} from '@pafrtds/nest-keycloak-connect';
@Controller()
@Resource('product')
export class ProductController {
@Get()
@Public()
async findAll() { ... }
@Get(':id')
@Scopes('view')
async findOne() { ... }
@Post()
@Scopes('create')
@Roles({ roles: ['manager', 'realm:admin'] })
async create() { ... }
@Delete(':id')
@Scopes('delete')
@Groups('/org/admins')
async remove() { ... }
@Put(':id')
@Scopes('edit')
@Groups('/org/admins', '/org/editors')
@GroupMatchingMode(GroupMatch.ALL) // must belong to BOTH groups
async update() { ... }
}Token Validation Cache
Caches online validation results to avoid calling Keycloak on every request. The TTL is derived from the token's exp claim.
Note: revoked tokens may be accepted until the cache entry expires. Use a short
maxTtlin environments requiring immediate revocation.
KeycloakConnectModule.register({
// ...
tokenValidation: TokenValidation.ONLINE,
tokenCache: {
enabled: true,
maxTtl: 30, // max 30 seconds, regardless of token lifetime
},
});To manually invalidate a token (e.g. after logout):
constructor(private readonly tokenCache: KeycloakTokenCacheService) {}
async logout(accessToken: string) {
this.tokenCache.invalidate(accessToken);
}Decorators
| Decorator | Description |
| -------------------- | ------------------------------------------------------------------------- |
| @KeycloakUser | Retrieves the current Keycloak user from the request. |
| @AccessToken | Retrieves the raw access token string from the request. |
| @ResolvedScopes | Retrieves the resolved scopes (used with @ConditionalScopes). |
| @EnforcerOptions | Keycloak enforcer options for ResourceGuard. |
| @Public | Allows unauthenticated access to the route. |
| @Resource | Keycloak resource name (used with ResourceGuard). |
| @Scopes | Required scopes on a resource (used with ResourceGuard). |
| @ConditionalScopes | Dynamic scopes resolved at request time. |
| @Roles | Required realm or client roles (used with RoleGuard). |
| @RoleMatchingMode | Sets RoleMatch.ANY (default) or RoleMatch.ALL for @Roles. |
| @Groups | Required Keycloak group membership (used with GroupGuard). |
| @GroupMatchingMode | Sets GroupMatch.ANY (default) or GroupMatch.ALL for @Groups. |
Token Validation Modes
| Mode | Description |
| --------- | -------------------------------------------------------------------------------------------- |
| ONLINE | Verifies JWT signature via JWKS and calls Keycloak's introspection endpoint per request. Detects revoked tokens. |
| OFFLINE | Verifies JWT signature via JWKS only. Fast, no per-request Keycloak calls. Does not detect revocation. |
| NONE | Skips all validation. Use only in development or internal trusted networks. |
Configuration options
| Option | Description | Default |
| ----------------- | --------------------------------------------------------------------------- | ------------ |
| authServerUrl | Keycloak server URL | required |
| realm | Realm name | required |
| clientId | Client/Application ID | required |
| secret | Client secret | required |
| cookieKey | Cookie key for JWT extraction | KEYCLOAK_JWT |
| policyEnforcement | PERMISSIVE or ENFORCING for ResourceGuard | PERMISSIVE |
| tokenValidation | ONLINE, OFFLINE, or NONE | ONLINE |
| tokenCache | Cache config: { enabled, maxTtl? } | disabled |
| multiTenant | Multi-tenant options | — |
| roleMerge | OVERRIDE or ALL for @Roles merge strategy | OVERRIDE |
Multi-tenant configuration
KeycloakConnectModule.register({
authServerUrl: 'http://localhost:8080',
clientId: 'nest-api',
secret: 'fallback-secret',
multiTenant: {
realmResolver: (request) => request.get('host').split('.')[0],
realmSecretResolver: (realm) => secretsMap[realm],
realmClientIdResolver: (realm) => clientIdMap[realm],
realmAuthServerUrlResolver: (realm) => authServerUrlMap[realm],
},
});License
MIT — Copyright (c) 2020 John Joshua Ferrer, 2026 Lucas Paes
