@gskglobal/nestjs-azure-auth
v1.0.0
Published
Reusable NestJS package for Azure AD SSO authentication. Provides Passport strategy, guards, decorators, and a dynamic module that accepts pluggable user-resolution functions (no direct DB coupling).
Readme
@gskglobal/nestjs-azure-auth
NestJS package for Azure AD SSO authentication. Validates bearer tokens,
resolves the user via a pluggable callback, and attaches it to req.user —
no fixed user shape, zero DB coupling.
Installation
npm install @gskglobal/nestjs-azure-auth
npm install @nestjs/passport passport passport-azure-adUsage
1. Register
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AzureAuthModule } from '@gskglobal/nestjs-azure-auth';
@Module({
imports: [
ConfigModule,
AzureAuthModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
clientId: config.get('AZURE_AD_CLIENT_ID'),
tenantId: config.get('AZURE_AD_TENANT_ID'),
findUserByEmail: async (email) => myService.findByEmail(email),
}),
}),
],
})
export class AuthModule {}2. findUserByEmail
Receives the normalised (lowercased, trimmed) email from the token. Return a
user object or null to reject with 401. The shape is entirely up to you.
3. mapUser (optional)
Transform the user before attaching to req.user. Receives the raw user from
findUserByEmail and the decoded Azure AD token payload (for access to tid,
oid, etc.).
AzureAuthModule.forRoot({
clientId: '...',
tenantId: '...',
findUserByEmail: async (email) => db.findUser(email),
mapUser: (user, token) => ({
id: user.id,
email: user.email,
tenantId: token.tid,
azureUserId: token.oid,
}),
});If omitted, the raw result of findUserByEmail is used directly.
4. Protect routes
import { Controller, Get, UseGuards } from '@nestjs/common';
import {
AzureAuthGuard,
RolesGuard,
Roles,
Public,
CurrentUser,
} from '@gskglobal/nestjs-azure-auth';
@Controller('profile')
export class ProfileController {
@Get()
@UseGuards(AzureAuthGuard)
getProfile(@CurrentUser() user: { id: number; email: string }) {
return user;
}
@Get('admin')
@UseGuards(AzureAuthGuard, RolesGuard)
@Roles('admin')
adminOnly(@CurrentUser() user: { id: number; email: string; roles: string[] }) {
return { message: `Welcome admin ${user.email}` };
}
@Get('health')
@Public()
health() {
return { ok: true };
}
}API
| Export | Kind | Description |
|--------|------|-------------|
| AzureAuthModule | Module | Dynamic module with forRootAsync. |
| AzureAuthGuard | Guard | Validates bearer token via Azure AD strategy. Skips @Public() routes. |
| RolesGuard | Guard | Checks req.user.roles against roles set by @Roles(). |
| @Roles(...) | Decorator | Marks route with required roles. |
| @Public() | Decorator | Bypasses AzureAuthGuard. |
| @CurrentUser(prop?) | Param decorator | Injects user (or a single property) from req.user. |
Options
| Option | Type | Description |
|--------|------|-------------|
| clientId | string | Azure AD app (client) ID. Required. |
| tenantId | string | Azure AD tenant ID. Required. |
| findUserByEmail | (email) => Promise<unknown \| null> | User resolver. Required. |
| mapUser | (user, token) => unknown | Optional transform. Receives user + raw token payload. |
| logger | (context: string) => LoggerService | Optional logger factory. Routes package logs through your app's logger (winston, etc.). Silent by default. |
