@PreSignupHook()
Package: @nauth-toolkit/nestjs
Type: Class Decorator
Class decorator that automatically registers a provider as a pre-signup hook. Pre-signup hooks execute before user creation and can block signups by throwing exceptions.
import { PreSignupHook } from '@nauth-toolkit/nestjs';
Overview
The @PreSignupHook() decorator enables automatic hook registration without manual setup. Classes decorated with this decorator are discovered at module initialization and registered with the HookRegistryService.
Key Features:
- Automatic hook discovery and registration
- No manual registry calls required
- Full dependency injection support
- Priority-based execution ordering
- Type-safe with TypeScript
When Pre-Signup Hooks Execute:
- Password signup (before user saved to database)
- Social signup (before user creation)
- Admin signup (
adminSignupandadminSignupSocial)
Usage
Basic Hook
import { Injectable } from '@nestjs/common';
import {
PreSignupHook,
IPreSignupHookProvider,
NAuthException,
AuthErrorCode,
} from '@nauth-toolkit/nestjs';
@Injectable()
@PreSignupHook()
export class DomainValidationHook implements IPreSignupHookProvider {
private readonly allowedDomains = ['company.com', 'partner.com'];
async execute(userData, signupMethod, providerId, adminSignup) {
// Skip validation for admin-initiated signups
if (adminSignup) return;
const domain = userData.email?.split('@')[1];
if (domain && !this.allowedDomains.includes(domain)) {
throw new NAuthException(
AuthErrorCode.PRESIGNUP_FAILED,
`Domain ${domain} not allowed`
);
}
}
}
With Priority
Control execution order using the priority option. Lower priority values execute first:
import { Injectable } from '@nestjs/common';
import { PreSignupHook, IPreSignupHookProvider } from '@nauth-toolkit/nestjs';
@Injectable()
@PreSignupHook({ priority: 1 }) // Executes first
export class DomainValidationHook implements IPreSignupHookProvider {
async execute(userData, signupMethod, providerId, adminSignup) {
// Domain validation logic
}
}
@Injectable()
@PreSignupHook({ priority: 2 }) // Executes second
export class InviteCodeHook implements IPreSignupHookProvider {
async execute(userData, signupMethod, providerId, adminSignup) {
// Invite code validation logic
}
}
Default Priority: If not specified, priority defaults to 100.
With Dependency Injection
Hooks support full NestJS dependency injection:
import { Injectable, Logger } from '@nestjs/common';
import {
PreSignupHook,
IPreSignupHookProvider,
NAuthException,
AuthErrorCode,
} from '@nauth-toolkit/nestjs';
import { InviteService } from '../services/invite.service';
import { ConfigService } from '@nestjs/config';
@Injectable()
@PreSignupHook()
export class InviteCodeHook implements IPreSignupHookProvider {
private readonly logger = new Logger(InviteCodeHook.name);
constructor(
private readonly inviteService: InviteService,
private readonly config: ConfigService,
) {}
async execute(userData, signupMethod, providerId, adminSignup) {
if (adminSignup) return;
const inviteCode = userData.metadata?.inviteCode;
if (!inviteCode) {
throw new NAuthException(
AuthErrorCode.PRESIGNUP_FAILED,
'Invite code required'
);
}
const isValid = await this.inviteService.validate(inviteCode);
if (!isValid) {
this.logger.warn(`Invalid invite code: ${inviteCode}`);
throw new NAuthException(
AuthErrorCode.PRESIGNUP_FAILED,
'Invalid invite code'
);
}
}
}
Module Registration
Register hooks using NAuthHooksModule.forFeature():
import { Module } from '@nestjs/common';
import { AuthModule, NAuthHooksModule } from '@nauth-toolkit/nestjs';
import { authConfig } from './auth.config';
import { DomainValidationHook } from './hooks/domain-validation.hook';
import { InviteCodeHook } from './hooks/invite-code.hook';
@Module({
imports: [
AuthModule.forRoot(authConfig),
NAuthHooksModule.forFeature([
DomainValidationHook,
InviteCodeHook,
]),
],
providers: [InviteService], // Don't forget hook dependencies
})
export class CustomAuthModule {}
Blocking Signups
Pre-signup hooks can block signups by throwing NAuthException with code PRESIGNUP_FAILED:
throw new NAuthException(
AuthErrorCode.PRESIGNUP_FAILED,
'Custom error message shown to user'
);
The user receives a 400 Bad Request response with your custom message.
Execution Behavior:
- First hook to throw an exception stops execution
- Subsequent hooks are skipped
- Signup is blocked
Testing
Hooks are regular NestJS providers and can be unit tested:
import { Test } from '@nestjs/testing';
import { DomainValidationHook } from './domain-validation.hook';
import { NAuthException, AuthErrorCode } from '@nauth-toolkit/core';
describe('DomainValidationHook', () => {
let hook: DomainValidationHook;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [DomainValidationHook],
}).compile();
hook = module.get(DomainValidationHook);
});
it('should allow valid domain', async () => {
const userData = { email: 'user@company.com' };
await expect(
hook.execute(userData, 'password', null, false)
).resolves.not.toThrow();
});
it('should block invalid domain', async () => {
const userData = { email: 'user@blocked.com' };
await expect(
hook.execute(userData, 'password', null, false)
).rejects.toThrow(NAuthException);
});
it('should skip validation for admin signups', async () => {
const userData = { email: 'user@blocked.com' };
await expect(
hook.execute(userData, 'password', null, true)
).resolves.not.toThrow();
});
});
Related APIs
IPreSignupHookProvider- Pre-signup hook interface@PostSignupHook()- Post-signup hook decoratorHookRegistryService- Hook registry serviceNAuthHooksModule- Hook registration module- Lifecycle Hooks Guide - Complete usage guide