Tutorial
NestJS Integration
Protect NestJS controllers and methods with Idenplane guards, custom decorators, and role-based access control.
Installation
Terminal
npm install idenplane-sdk Idenplane Config Provider
Provide the server config that verifyToken needs — no client instance to construct, since token verification is a standalone function.
src/auth/idenplane.module.ts
import { Module } from '@nestjs/common';
import type { IdenplaneServerConfig } from 'idenplane-sdk/server';
import { ConfigService } from '@nestjs/config';
export const IDENPLANE_CONFIG = 'IDENPLANE_CONFIG';
@Module({
providers: [
{
provide: IDENPLANE_CONFIG,
useFactory: (cfg: ConfigService): IdenplaneServerConfig => ({
issuerUrl: cfg.getOrThrow('IDENPLANE_URL'),
realm: cfg.getOrThrow('IDENPLANE_REALM'),
}),
inject: [ConfigService],
},
],
exports: [IDENPLANE_CONFIG],
})
export class IdenplaneModule {} Idenplane Guard
Implement a NestJS CanActivate guard around verifyToken, which performs full JWKS signature verification and attaches the decoded payload to the request.
src/auth/auth.guard.ts
import {
CanActivate, ExecutionContext, Inject,
Injectable, UnauthorizedException,
} from '@nestjs/common';
import { verifyToken } from 'idenplane-sdk/server';
import type { IdenplaneServerConfig } from 'idenplane-sdk/server';
import { IDENPLANE_CONFIG } from './idenplane.module';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(@Inject(IDENPLANE_CONFIG) private readonly config: IdenplaneServerConfig) {}
async canActivate(ctx: ExecutionContext): Promise<boolean> {
const req = ctx.switchToHttp().getRequest();
const token = this.extractToken(req);
if (!token) throw new UnauthorizedException();
try {
req.user = await verifyToken(token, this.config);
return true;
} catch {
throw new UnauthorizedException('Invalid token');
}
}
private extractToken(req: any): string | undefined {
const [type, token] = req.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
} CurrentUser Decorator
Create a parameter decorator to cleanly extract the authenticated user inside controller methods.
src/auth/current-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(_data: unknown, ctx: ExecutionContext) =>
ctx.switchToHttp().getRequest().user,
); Role-Based Access Control
Use a Roles decorator together with a RolesGuard to restrict controller methods to specific roles.
src/auth/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles); src/auth/roles.guard.ts
import {
CanActivate, ExecutionContext, Injectable,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from './roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(ctx: ExecutionContext): boolean {
const required = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
ctx.getHandler(),
ctx.getClass(),
});
if (!required?.length) return true;
const { user } = ctx.switchToHttp().getRequest();
return required.some((r) => user?.roles?.includes(r));
}
} src/users/users.controller.ts
@Controller('users')
@UseGuards(AuthGuard, RolesGuard)
export class UsersController {
@Get()
@Roles('admin')
findAll() { return this.usersService.findAll(); }
@Get('profile')
// No @Roles — any authenticated user can access
getProfile(@CurrentUser() user: any) {
return user;
}
}