@azerothian/keycloak-api
v0.0.1
Published
NestJS module for Keycloak authentication and administration
Readme
@azerothian/keycloak-api
NestJS module for Keycloak authentication and administration. Provides multi-realm JWT authentication, 100+ admin REST API methods, route protection decorators, JWKS key caching, and comprehensive testing utilities.
Features
- Multi-Realm JWT Authentication - Validate JWT tokens from any realm on a Keycloak instance with automatic JWKS key resolution and caching
- Comprehensive Admin API - 100+ methods for realm, user, group, role, session, and event management
- Route Protection Decorators -
@Public(),@CurrentUser(), and@Permissions()for fine-grained access control - JWKS Caching - Automatic signing key caching with configurable refresh intervals to minimize Keycloak calls
- Testing Utilities - Mock JWT tokens and strategies for integration testing without a running Keycloak instance
Installation
npm install @azerothian/keycloak-apiPeer Dependencies
npm install @nestjs/common @nestjs/core @nestjs/config @nestjs/passport rxjsQuick Start
1. Authentication Setup
Import the auth module and configure your Keycloak instance:
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { PassportModule } from '@nestjs/passport';
import { KeycloakStrategy, JwtAuthGuard } from '@azerothian/keycloak-api/auth';
@Module({
imports: [
ConfigModule.forRoot(),
PassportModule.register({ defaultStrategy: 'keycloak' }),
],
providers: [KeycloakStrategy, JwtAuthGuard],
})
export class AppModule {}Set environment variables:
KEYCLOAK_URL=https://keycloak.example.com
KEYCLOAK_REALM=your-realmProtect routes with the guard:
// users.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard, CurrentUser, Public } from '@azerothian/keycloak-api/auth';
import { AuthenticatedUser } from '@azerothian/keycloak-api/types';
@Controller('users')
@UseGuards(JwtAuthGuard)
export class UsersController {
@Get('profile')
getProfile(@CurrentUser() user: AuthenticatedUser) {
return {
keycloakId: user.keycloakId,
username: user.username,
roles: user.roles,
};
}
@Get('health')
@Public()
healthCheck() {
return { status: 'ok' };
}
}2. Admin API Setup
Import the admin module to manage realms, users, groups, and roles:
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { KeycloakAdminModule } from '@azerothian/keycloak-api/admin';
@Module({
imports: [
ConfigModule.forRoot(),
KeycloakAdminModule,
],
})
export class AppModule {}Set admin credentials in environment:
KEYCLOAK_URL=https://keycloak.example.com
KEYCLOAK_ADMIN_USERNAME=admin
KEYCLOAK_ADMIN_PASSWORD=password
KEYCLOAK_ADMIN_CLIENT_ID=admin-cli
KEYCLOAK_ADMIN_CLIENT_SECRET=your-client-secretUse the service in your application:
// realms.service.ts
import { Injectable } from '@nestjs/common';
import { KeycloakAdminService } from '@azerothian/keycloak-api/admin';
@Injectable()
export class RealmsService {
constructor(private kcAdmin: KeycloakAdminService) {}
async getAllRealms() {
return this.kcAdmin.getRealms();
}
async getRealmUsers(realmName: string) {
return this.kcAdmin.getUsers(realmName, { max: 100 });
}
async createUser(realmName: string, user: any) {
return this.kcAdmin.createUser(realmName, user);
}
}3. Using Decorators
Control access with decorators:
import { Controller, Post, UseGuards } from '@nestjs/common';
import { JwtAuthGuard, Permissions, CurrentUser } from '@azerothian/keycloak-api';
import { AuthenticatedUser } from '@azerothian/keycloak-api/types';
@Controller('networks')
@UseGuards(JwtAuthGuard)
export class NetworksController {
@Post()
@Permissions('networks:create')
createNetwork(@CurrentUser() user: AuthenticatedUser, @Body() dto: CreateNetworkDto) {
return {
createdBy: user.keycloakId,
...dto,
};
}
}Environment Variables
| Variable | Description | Example |
|----------|-------------|---------|
| KEYCLOAK_URL | Base URL of Keycloak instance | https://keycloak.example.com |
| KEYCLOAK_REALM | Primary realm for authentication | wirenet |
| KEYCLOAK_ADMIN_USERNAME | Admin account username (for admin API) | admin |
| KEYCLOAK_ADMIN_PASSWORD | Admin account password (for admin API) | password |
| KEYCLOAK_ADMIN_CLIENT_ID | Client ID for admin API authentication | admin-cli |
| KEYCLOAK_ADMIN_CLIENT_SECRET | Client secret for admin API (if using client credentials flow) | secret-key |
Subpath Imports
This package exports specialized modules. Import only what you need:
| Import Path | Exports | Purpose |
|-------------|---------|---------|
| @azerothian/keycloak-api | All exports | Root barrel - re-exports everything |
| @azerothian/keycloak-api/admin | KeycloakAdminService, KeycloakAdminModule | Keycloak Admin REST API |
| @azerothian/keycloak-api/auth | KeycloakStrategy, JwtAuthGuard | JWT authentication via Passport |
| @azerothian/keycloak-api/decorators | CurrentUser(), Public(), Permissions(), constants | Route protection decorators |
| @azerothian/keycloak-api/types | KeycloakTokenPayload, AuthenticatedUser, session/event types, SERVICE_PERMISSIONS | Type definitions |
| @azerothian/keycloak-api/testing | TestJwtStrategy, mock token factories | Integration testing utilities |
Testing
Create mock JWT tokens for integration tests without a running Keycloak instance:
import { Test } from '@nestjs/testing';
import {
TestJwtStrategy,
createAdminToken,
createUserToken,
} from '@azerothian/keycloak-api/testing';
describe('UsersController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
controllers: [UsersController],
providers: [UsersService, TestJwtStrategy],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('should allow admin to access protected route', async () => {
const adminToken = createAdminToken('user-id-123', 'realm-id-456');
return request(app.getHttpServer())
.get('/users/profile')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
});
it('should allow regular user with limited permissions', async () => {
const userToken = createUserToken('user-id-789', 'realm-id-456');
return request(app.getHttpServer())
.get('/users/profile')
.set('Authorization', `Bearer ${userToken}`)
.expect(200);
});
});Mock token factories create tokens with predefined roles and permissions:
createAdminToken()- Full system access (admin role with all permissions)createUserToken()- Regular user access (limited permissions)createPowerUserToken()- Elevated permissionscreateServiceAccountToken()- For service-to-service authenticationcreateSuperadminToken()- Maximum privileges (superadmin + admin roles)createMockJwtToken(payload, secret)- Custom token with arbitrary payload
License
MIT
