Nest Auth
nestjs module to handle session based authentication
Installation
$ npm install --save @nest-auth/auth
Or
$ yarn add @nest-auth/auth
Install peer dependencies
$ npm install --save @nest-auth/cipher @nestjs/passport passport @nestjs/typeorm typeorm body-parser class-transformer class-validator cookie-parser csurf hbs passport-local
Or
$ yarn add @nest-auth/cipher @nestjs/passport passport @nestjs/typeorm typeorm body-parser class-transformer class-validator cookie-parser csurf hbs passport-local
Optionally install the password strength estimator
$ npm install --save zxcvbn
Or
$ yarn add zxcvbn
Create the User and Social Login entities
import { Entity } from 'typeorm';
import { AuthUser, AuthSocialLogin } from '@nest-auth/auth';
@Entity()
export class UserEntity extends AuthUser() {
@OneToMany(() => SocialLoginEntity, sl => sl.user, {
eager: true,
})
socialLogins: SocialLoginEntity[];
}
@Entity()
export class SocialLoginEntity extends AuthSocialLogin() {
@ManyToOne(() => UserEntity, u => u.socialLogins, {
onDelete: 'CASCADE',
})
user: Promise<UserEntity>;
}
Create the Auth controller
import { Controller } from '@nestjs/common';
import { AuthController } from '@nest-auth/auth';
@AuthController('auth')
export class AppAuthController {}
Import it in a module
import { Module } from '@nestjs/common';
import { AuthModule } from '@nest-auth/auth';
@Module({
imports: [
//...
AuthModule.forRoot({
// Required options
userEntity: UserEntity,
socialLoginEntity: SocialLoginEntity,
// Optional
connection: 'default', // Typeorm connection name
minPasswordScore: undefined, // if passed, ensures that the password meets the passed minimun score, uses the zxcvbn package
registerDto: RegisterDto, // Dto that should extend RegisterDto, useful to add more data to the user during registration
}),
// Or with Async configuration
AuthModule.forRootAsync({
import: [ConfigModule],
inject: [ConfigService],
useFactory: config => config.get('auth'),
}),
//...
],
controllers: [
// Do not forget to register the Auth controller
AppAuthController,
],
})
export class AppModule {}
Add auth middleware to the underlying Express adapter
// main.ts
import { NestApplication } from '@nestjs/core';
import { addAuthMiddlewares } from '@nest-auth/auth';
const app = await NestApplication.create(AppModule, {
//...
});
//...
addAuthMiddlewares(app, {
sessionSecret: 'supersecret',
clientDir: './client',
sessionStore: store, // Compatible express session store,
session: session, // compatible express session (`express-session`),
});
//...
await app.listen(3000);
Create authentication views
Create a views
directory inside the clientDir
directory (ex. client/views
)
Create the required views:
login.html
register.html
For now, the only supported view engine is handlebars
with .html
extension
This configuration created thw following url:
- GET
/login
: show login formlogin.html
view. It receives as data-
csrfToken
: required to be set as_csrf
form field -
action
: url to call as POST to handle authentication
-
- POST
/login
: handle the login request. Login the user and redirect to the homepage or the value of theredirect_uri
query param - GET
/register
: show the registration formregister.html
view. It receives as data-
csrfToken
: required to be set as_csrf
form field -
action
: url to call as POST to handle authentication
-
- POST
/register
: handle the registration request. Register the user, logs in him and redirect to the homepage or theredirect_uri
query param - POST
/logout
: logs out the user and redirect to the homepage or theredirect_uri
query param
Protect the routes
Guard a route with the AuthenticatedGuard
to ensure the user is authenticated.
It returns a standard 403 json error
To ensure the user is redirected to the Login page, add the Filter ForbiddenExceptionFilter
to the same route
Get the user
To get the user from session, use the @CurrentUser
param decorator
Example
import { Controller, Get, Render, UseFilters, UseGuards } from '@nestjs/common';
import { AuthenticatedGuard, ForbiddenExceptionFilter, CurrentUser } from '@nest-auth/auth';
import { User } from '../entities';
@Controller()
export class PrivateController {
@UseFilters(ForbiddenExceptionFilter)
@UseGuards(AuthenticatedGuard)
@Render('private')
@Get('/private')
private(
@CurrentUser() user: User,
) {
return {
message: 'You are authenticated',
user,
}
}
@UseGuards(AuthenticatedGuard)
@Get('/api/private')
apiPrivate(
@CurrentUser() user: User,
) {
return {
message: 'You are authenticated',
user,
}
}
}