Lifecycle Hooks
Add custom logic at specific points in the authentication flow --- block signups based on business rules, send notifications, sync to external systems, or audit events.
nauth-toolkit provides 13 hooks across user lifecycle, security, and account management events. See Lifecycle Hooks Concept for the full list, execution model, and API reference.
Prerequisites
- A working auth setup (Quick Start)
Step 1: Create Your Hook
This example sends a custom email when a user changes their password:
- NestJS
- Express
- Fastify
src/auth/hooks/password-changed-email.hook.ts
import { Injectable } from '@nestjs/common';
import {
PasswordChangedHook,
IPasswordChangedHook,
PasswordChangedMetadata,
} from '@nauth-toolkit/nestjs';
@Injectable()
@PasswordChangedHook({ priority: 1 })
export class PasswordChangedEmailHook implements IPasswordChangedHook {
constructor(private readonly emailService: EmailService) {}
async execute(metadata: PasswordChangedMetadata): Promise<void> {
await this.emailService.sendPasswordChangedAlert({
to: metadata.user.email,
changedBy: metadata.changedBy,
timestamp: new Date(),
sessionsRevoked: metadata.sessionsRevoked,
});
}
}
src/hooks/password-changed-email.hook.ts
import { IPasswordChangedHook } from '@nauth-toolkit/core';
export class PasswordChangedEmailHook implements IPasswordChangedHook {
constructor(private emailService) {}
async execute(metadata) {
await this.emailService.sendPasswordChangedAlert({
to: metadata.user.email,
changedBy: metadata.changedBy,
timestamp: new Date(),
sessionsRevoked: metadata.sessionsRevoked,
});
}
}
src/hooks/password-changed-email.hook.ts
import { IPasswordChangedHook } from '@nauth-toolkit/core';
export class PasswordChangedEmailHook implements IPasswordChangedHook {
constructor(private emailService) {}
async execute(metadata) {
await this.emailService.sendPasswordChangedAlert({
to: metadata.user.email,
changedBy: metadata.changedBy,
timestamp: new Date(),
sessionsRevoked: metadata.sessionsRevoked,
});
}
}
Step 2: Register Your Hook
- NestJS
- Express
- Fastify
src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthModule, NAuthHooksModule } from '@nauth-toolkit/nestjs';
import { authConfig } from './auth.config';
import { PasswordChangedEmailHook } from './hooks/password-changed-email.hook';
@Module({
imports: [
AuthModule.forRoot(authConfig),
NAuthHooksModule.forFeature([PasswordChangedEmailHook]),
],
})
export class CustomAuthModule {}
The hook is automatically discovered and registered.
src/index.ts
import { NAuth } from '@nauth-toolkit/core';
import { PasswordChangedEmailHook } from './hooks/password-changed-email.hook';
const nauth = await NAuth.create(authConfig, dataSource);
nauth.hookRegistry.registerPasswordChanged(
new PasswordChangedEmailHook(emailService)
);
app.use('/auth', nauth.routes);
src/index.ts
import { NAuth } from '@nauth-toolkit/core';
import { PasswordChangedEmailHook } from './hooks/password-changed-email.hook';
const nauth = await NAuth.create(authConfig, dataSource);
nauth.hookRegistry.registerPasswordChanged(
new PasswordChangedEmailHook(emailService)
);
fastify.register(nauth.routes, { prefix: '/auth' });
Best Practices
Keep hooks focused
Each hook should have a single responsibility:
// Good - Single responsibility
@PasswordChangedHook()
export class PasswordChangedEmailHook {}
@PasswordChangedHook()
export class PasswordChangedAnalyticsHook {}
// Bad - Multiple responsibilities
@PasswordChangedHook()
export class PasswordChangedEverythingHook {
// Sends email, logs to analytics, syncs to CRM — too many things
}
Handle errors gracefully
Non-blocking hooks should handle errors explicitly:
async execute(metadata) {
try {
await this.emailService.send(metadata.user.email);
} catch (error) {
this.logger.error('Email failed:', error);
await this.queueService.add('retry-email', { userId: metadata.user.id });
}
}
Use dependency injection
Leverage framework DI for testability:
@Injectable()
@PasswordChangedHook({ priority: 1 })
export class PasswordChangedEmailHook implements IPasswordChangedHook {
constructor(
private readonly emailService: EmailService,
private readonly logger: Logger,
) {}
}
What's Next
- Lifecycle Hooks Concept --- All 13 hooks, execution model, blocking behavior, and API reference
- Notifications & Templates --- Built-in email and SMS notifications (alternative to custom hooks)
- Email Templates --- Customize built-in email templates
- Challenge System --- Understanding authentication flows