Tutorial

NestJS Integration

Protect NestJS controllers and methods with AuthMe guards, custom decorators, and role-based access control.

Installation

Terminal
npm install authme-sdk @nestjs/passport passport passport-jwt

AuthMe Module

Create a dedicated NestJS module that initialises the AuthMe client and exposes it for injection.

src/auth/authme.module.ts
import { Module } from '@nestjs/common';
import { AuthmeClient } from 'authme-sdk';
import { ConfigService } from '@nestjs/config';

export const AUTHME_CLIENT = 'AUTHME_CLIENT';

@Module({
  providers: [
    {
      provide: AUTHME_CLIENT,
      useFactory: (cfg: ConfigService) =>
        new AuthmeClient({
          url: cfg.getOrThrow('AUTHME_URL'),
          realm: cfg.getOrThrow('AUTHME_REALM'),
        }),
      inject: [ConfigService],
    },
  ],
  exports: [AUTHME_CLIENT],
})
export class AuthmeModule {}

AuthMe Guard

Implement a NestJS CanActivate guard that validates the Bearer token and attaches the user payload to the request.

src/auth/auth.guard.ts
import {
  CanActivate, ExecutionContext, Inject,
  Injectable, UnauthorizedException,
} from '@nestjs/common';
import { AuthmeClient } from 'authme-sdk';
import { AUTHME_CLIENT } from './authme.module';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(@Inject(AUTHME_CLIENT) private readonly authme: AuthmeClient) {}

  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 this.authme.verifyToken(token);
      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;
  }
}