Intro to Nest.js Auth Base
🔑 This is customizable module for JWT username-password authentication and authorization.
You don't need any adapters or additional modules to use your data-access tools. Just write a small service with your favorite ORM/instrument, library will handle the rest.
Install into your project
Recommendations
Suggest creating a new Nest.js 10 app with nest-cli
Also Node version 16 or higher is recommended
$ npm install nest-auth-base
$ npm install nest-auth-base
$ yarn add nest-auth-base
$ yarn add nest-auth-base
$ pnpm install nest-auth-base
$ pnpm install nest-auth-base
Setting up the module
Before you register this module, you should write some types and services, so Auth Base will know how to manage your accounts' datasource. Also you should know Nest.js fundamentals (services, controllers, guards).
Let's start with your account interface. Unique username and password are required. If you want to use roles guard, you must add the roles
property. Obviously, it's allowed to have some other properties. For example, we added reputation
field.
Note
You don't need to create account types if your database tool generates them by itself, like Prisma. Check out our Prisma + Auth Base guide
export interface Account {
username : string,
password : string,
roles : string[],
reputation : number
}
export interface Account {
username : string,
password : string,
roles : string[],
reputation : number
}
Then, create a service that extends AuthBaseAccountsService
and pass your account's type as generic. Here you can query your database, in this example we'll just use an array.
@Injectable()
export class AccountsService extends AuthBaseAccountsService<Account> {
private readonly accounts : Account[]
createAccount(credentials: ProcessedCredentials) {
const newAccount : Account = {
username: credentials.username,
password: credentials.hashedPassword,
roles: [ 'USER' ],
reputation: 0
}
this.accounts.push(newAccount)
return newAccount
}
getAccountByUsername(username: string) {
return this.accounts.find(account => account.username === username)
}
}
@Injectable()
export class AccountsService extends AuthBaseAccountsService<Account> {
private readonly accounts : Account[]
createAccount(credentials: ProcessedCredentials) {
const newAccount : Account = {
username: credentials.username,
password: credentials.hashedPassword,
roles: [ 'USER' ],
reputation: 0
}
this.accounts.push(newAccount)
return newAccount
}
getAccountByUsername(username: string) {
return this.accounts.find(account => account.username === username)
}
}
Remember when injecting other services in accounts service
If you need inject something into accounts service, you must specify it in imports
(for modules) or providers
(for services) fields in module options (scroll down)
Now you can finally register the module with some options. It's global so you can just import it in app module and use guards everywhere.
@Module({
imports: [
AuthBaseModule.register({
accountsService: AccountsService,
jwtSecretKey: 'YOUR_SECRET_KEY',
imports: [ ... ] // Import modules you want to use in AccountsService
providers: [ ... ] // Import services you want to use in AccountsService
})
],
controllers: [ AppController ],
})
export class AppModule {}
@Module({
imports: [
AuthBaseModule.register({
accountsService: AccountsService,
jwtSecretKey: 'YOUR_SECRET_KEY',
imports: [ ... ] // Import modules you want to use in AccountsService
providers: [ ... ] // Import services you want to use in AccountsService
})
],
controllers: [ AppController ],
})
export class AppModule {}
WARNING
It's strongly recommended to get your jwtSecretKey
from your enviroment variables via Nest.js config module
Now there are two endpoints available:
POST /auth/sign-up
Accepts body in this format:
{
"username": "USERNAME",
"password": "PASSWORD"
}
{
"username": "USERNAME",
"password": "PASSWORD"
}
POST /auth/log-in
Accepts body in this format:
{
"username": "USERNAME",
"password": "PASSWORD"
}
{
"username": "USERNAME",
"password": "PASSWORD"
}
To protect some endpoint, put a guard on it.
AuthGuard
checks if user is authenticated by verifying JWT token in bearer-type Authorization
header.
@Controller()
export class AppController {
@Get('me')
@UseGuards(AuthGuard)
async getAccount(@CurrentAccount() account : Account) {
return account
}
}
@Controller()
export class AppController {
@Get('me')
@UseGuards(AuthGuard)
async getAccount(@CurrentAccount() account : Account) {
return account
}
}
RolesGuard
checks if user is authenticated and has one of specified roles.
@Controller()
export class AppController {
@Get('me')
@AllowedRoles('ADMIN', 'DEVELOPER')
@UseGuards(RolesGuard)
async getAccount(@CurrentAccount() account : Account) {
return account
}
}
@Controller()
export class AppController {
@Get('me')
@AllowedRoles('ADMIN', 'DEVELOPER')
@UseGuards(RolesGuard)
async getAccount(@CurrentAccount() account : Account) {
return account
}
}
That's all now you have secured API.