@nestbolt/authentication
v0.1.1
Published
Frontend-agnostic authentication backend for NestJS with 2FA, password reset, email verification, and more
Downloads
26
Maintainers
Readme
A complete, database-agnostic authentication backend for NestJS with support for registration, login, password reset, email verification, profile management, password confirmation, and two-factor authentication (TOTP).
Inspired by Laravel Fortify.
Table of Contents
- Installation
- Quick Start
- Module Configuration
- Features
- Database Adapters
- API Routes
- Events
- Configuration Options
- Testing
- Changelog
- Contributing
- License
Installation
# pnpm
pnpm add @nestbolt/authentication
# npm
npm install @nestbolt/authentication
# yarn
yarn add @nestbolt/authenticationPeer Dependencies
pnpm add @nestjs/passport @nestjs/jwt passport passport-jwt passport-local class-validator class-transformer reflect-metadataOptional:
pnpm add @nestjs/event-emitterQuick Start
- Implement the
UserRepositoryinterface for your database:
import { Injectable } from "@nestjs/common";
import { UserRepository, AuthUser } from "@nestbolt/authentication";
@Injectable()
export class MyUserRepository implements UserRepository {
async findById(id: string): Promise<AuthUser | null> { /* ... */ }
async findByField(field: string, value: string): Promise<AuthUser | null> { /* ... */ }
async save(user: Partial<AuthUser> & { id: string }): Promise<AuthUser> { /* ... */ }
async create(data: Omit<AuthUser, "id">): Promise<AuthUser> { /* ... */ }
}- Import
AuthenticationModulein your app module:
import { AuthenticationModule, Feature } from "@nestbolt/authentication";
import { MyUserRepository } from "./my-user.repository";
@Module({
imports: [
AuthenticationModule.forRoot({
features: [
Feature.REGISTRATION,
Feature.RESET_PASSWORDS,
Feature.EMAIL_VERIFICATION,
Feature.UPDATE_PROFILE_INFORMATION,
Feature.UPDATE_PASSWORDS,
Feature.TWO_FACTOR_AUTHENTICATION,
],
userRepository: MyUserRepository,
jwtSecret: process.env.JWT_SECRET!,
refreshSecret: process.env.REFRESH_SECRET!,
encryptionKey: process.env.ENCRYPTION_KEY!, // 32-byte base64
appName: "MyApp",
}),
],
})
export class AppModule {}- That's it! All 19 auth routes are now available.
Module Configuration
Synchronous
AuthenticationModule.forRoot({
features: [Feature.REGISTRATION, Feature.TWO_FACTOR_AUTHENTICATION],
userRepository: TypeOrmUserRepository,
passwordResetRepository: TypeOrmPasswordResetRepository,
jwtSecret: "your-jwt-secret",
refreshSecret: "your-refresh-secret",
encryptionKey: "base64-encoded-32-byte-key",
appName: "MyApp",
});Asynchronous
AuthenticationModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
features: [Feature.REGISTRATION, Feature.TWO_FACTOR_AUTHENTICATION],
userRepository: TypeOrmUserRepository,
jwtSecret: config.get("JWT_SECRET"),
refreshSecret: config.get("REFRESH_SECRET"),
encryptionKey: config.get("ENCRYPTION_KEY"),
}),
});Features
Enable or disable features via the features array:
| Feature | Description |
|---------|-------------|
| Feature.REGISTRATION | User registration (POST /register) |
| Feature.RESET_PASSWORDS | Password reset flow (POST /forgot-password, POST /reset-password) |
| Feature.EMAIL_VERIFICATION | Email verification (GET /email/verify/:id/:hash) |
| Feature.UPDATE_PROFILE_INFORMATION | Profile updates (PUT /user/profile-information) |
| Feature.UPDATE_PASSWORDS | Password updates (PUT /user/password) |
| Feature.TWO_FACTOR_AUTHENTICATION | Full 2FA with TOTP, QR codes, and recovery codes |
Database Adapters
The package is database-agnostic. Implement UserRepository and optionally PasswordResetRepository for any database:
TypeORM (SQL)
@Injectable()
export class TypeOrmUserRepository implements UserRepository {
constructor(@InjectRepository(User) private repo: Repository<User>) {}
findById(id: string) { return this.repo.findOneBy({ id }); }
findByField(field: string, value: string) { return this.repo.findOneBy({ [field]: value }); }
save(user) { return this.repo.save(user); }
create(data) { return this.repo.save(this.repo.create(data)); }
}Mongoose (MongoDB)
@Injectable()
export class MongooseUserRepository implements UserRepository {
constructor(@InjectModel(User.name) private model: Model<UserDocument>) {}
findById(id: string) { return this.model.findById(id).lean().exec(); }
findByField(field: string, value: string) { return this.model.findOne({ [field]: value }).lean().exec(); }
save(user) { return this.model.findByIdAndUpdate(user.id, user, { new: true }).lean().exec(); }
create(data) { return this.model.create(data); }
}Prisma, MikroORM, DynamoDB, etc.
Same pattern - implement the interface for your ORM/driver.
API Routes
| Method | Route | Description | Auth | |--------|-------|-------------|------| | POST | /login | Authenticate user | No | | POST | /refresh | Refresh access token | Refresh Token | | POST | /logout | Log out | JWT | | POST | /register | Create new user | No | | POST | /forgot-password | Send reset link | No | | POST | /reset-password | Reset password | No | | GET | /email/verify/:id/:hash | Verify email | JWT | | POST | /email/verification-notification | Resend verification | JWT | | PUT | /user/profile-information | Update profile | JWT | | PUT | /user/password | Change password | JWT | | POST | /user/confirm-password | Confirm password | JWT | | GET | /user/confirmed-password-status | Check confirmation | JWT | | POST | /user/two-factor-authentication | Enable 2FA | JWT | | DELETE | /user/two-factor-authentication | Disable 2FA | JWT | | POST | /user/confirmed-two-factor-authentication | Confirm 2FA setup | JWT | | GET | /user/two-factor-qr-code | Get QR code SVG | JWT | | GET | /user/two-factor-secret-key | Get TOTP secret | JWT | | GET/POST | /user/two-factor-recovery-codes | Get/regenerate codes | JWT | | POST | /two-factor-challenge | Complete 2FA login | No |
Events
Subscribe to authentication events using @nestjs/event-emitter:
import { OnEvent } from "@nestjs/event-emitter";
import { AUTH_EVENTS, UserEvent } from "@nestbolt/authentication";
@Injectable()
export class AuthListener {
@OnEvent(AUTH_EVENTS.LOGIN)
handleLogin(payload: UserEvent) {
console.log(`User ${payload.user.email} logged in`);
}
}Available events: auth.login, auth.logout, auth.registered, auth.lockout, auth.password-reset, auth.password-updated, auth.email-verified, auth.two-factor-enabled, auth.two-factor-disabled, auth.two-factor-confirmed, auth.two-factor-challenged, auth.two-factor-failed, auth.valid-two-factor-code, auth.recovery-code-replaced, auth.recovery-codes-generated
Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| features | Feature[] | required | Enabled features |
| userRepository | Type<UserRepository> | required | User repository class |
| passwordResetRepository | Type<PasswordResetRepository> | - | Password reset token storage |
| jwtSecret | string | required | JWT signing secret |
| refreshSecret | string | required | Refresh token secret |
| encryptionKey | string | required | 32-byte base64 key for 2FA encryption |
| jwtExpiresIn | string | "15m" | Access token TTL |
| refreshExpiresIn | string | "7d" | Refresh token TTL |
| usernameField | string | "email" | Login username field |
| lowercaseUsernames | boolean | true | Lowercase usernames on login |
| loginRateLimit | { ttl, limit } | { 60000, 5 } | Login rate limiting |
| twoFactorRateLimit | { ttl, limit } | { 60000, 5 } | Two-factor challenge rate limiting |
| verificationRateLimit | { ttl, limit } | { 60000, 6 } | Email verification rate limiting |
| passwordTimeout | number | 900 | Password confirmation timeout (seconds) |
| appName | string | "NestBolt" | App name for TOTP QR codes |
| twoFactorOptions.confirm | boolean | false | Require 2FA confirmation step |
| twoFactorOptions.confirmPassword | boolean | false | Require password before 2FA changes |
| twoFactorOptions.window | number | 1 | TOTP time window |
| twoFactorOptions.secretLength | number | 20 | TOTP secret key length |
Testing
pnpm test # Run tests
pnpm test:watch # Watch mode
pnpm test:cov # Coverage reportChangelog
See CHANGELOG.md.
Contributing
See CONTRIBUTING.md.
Security
For security-related issues, please use the security label on GitHub Issues.
Credits
- Inspired by Laravel Fortify by Taylor Otwell
