Skip to content

Azerothian/keycloak-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@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-api

Peer Dependencies

npm install @nestjs/common @nestjs/core @nestjs/config @nestjs/passport rxjs

Quick 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-realm

Protect 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-secret

Use 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 permissions
  • createServiceAccountToken() - For service-to-service authentication
  • createSuperadminToken() - Maximum privileges (superadmin + admin roles)
  • createMockJwtToken(payload, secret) - Custom token with arbitrary payload

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors