NestJS authentication for server side rendering with Handlebars
Tags: javascript, webdev, security, architecture
Published: 2019-10-14 09:01:24

---
title: NestJS authentication for server side rendering with Handlebars
published: true
tags: javascript, webdev, security, architecture
canonical_url: https://boobo94.xyz/tutorials/nestjs-authentication/?utm_source=rss&utm_medium=rss&utm_campaign=nestjs-authentication
---

_The post [NestJS authentication for server-side rendering with Handlebars](https://boobo94.xyz/tutorials/nestjs-authentication/) appeared first on [boobo94.xyz](https://boobo94.xyz). So if you want to read more articles like this please [subscribe to my newsletter](https://boobo94.xyz/#colophon) or follow me here_

Here is how I managed [NestJS](https://nestjs.com/) authentication for server side rendering with Handlebars. These days I had to implement a simple authentication system for login for a project with NestJS and the templates rendered with [Handlebars](http://handlebarsjs.com/installation.html).

Below you can see the folder structure that I want to present in the following lines:

└── src  
│ ├── main.ts  
│ ├── app.module.ts  
│ ├── auth  
│ │ ├── auth.controller.ts  
│ │ ├── auth.module.ts  
│ │ ├── auth.middleware.ts  
│ │ ├── auth.filters.ts  
│ │ └── auth.service.ts  
│ ├── users  
│ │ ├── user.dto.ts  
│ │ └── user.service.ts  
│ └── views  
│ │ └── login.hbs

### auth.module.ts

```
import { Module } from '@nestjs/common'; import { AuthService } from './auth.service'; import { UsersService } from '../users/users.service'; import { DatabaseModule } from '../database/database.module'; import { AuthController } from './auth.controller'; @Module({ imports: [DatabaseModule,], providers: [UsersService, AuthService,], controllers: [AuthController], }) export class AuthModule { }
```

### auth.controller.ts

```
import { Controller, Get, UseGuards, Render, Post, Body, Res, Req } from '@nestjs/common'; import { AuthService } from './auth.service'; import { UserDto } from '../users/user.dto'; import { ValidateRequest } from '../validation/validation'; import { Response, Request } from 'express'; @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) { } @Get('login') @Render('auth/login') async loginView() { } @Post('login') async login(@Body() userDto: UserDto, @Req() request, @Res() response: Response) { const payload = await ValidateRequest(UserDto, userDto) if (payload) { return response.render('auth/login', { ...payload, }) } try { const user = await this.authService.login(userDto); // store the username in session request.session.username = user.username; } catch (err) { return response.render('auth/login', { data: userDto, globalError: err.message, }) } return response.redirect(`/`) } @Get('logout') logout(@Req() request, @Res() response: Response) { request.session.destroy(); return response.redirect('/auth/login') } }
```

### auth.middleware.ts

```
import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common'; import { Response } from 'express'; @Injectable() export class AuthMiddleware implements NestMiddleware { use(req, res: Response, next: Function) { if (req.session &amp;&amp; req.session.username) return next(); throw new UnauthorizedException(); } }
```

### auth.service.ts

```
import { Injectable } from '@nestjs/common'; import { UsersService } from '../users/users.service'; import { UserDto } from 'src/users/user.dto'; const bcrypt = require('bcrypt'); @Injectable() export class AuthService { constructor( private readonly usersService: UsersService, ) { } async login(payload: UserDto): Promise\<UserDto\> { const user = await this.usersService.findByUsername(payload.username); if (!user) throw new Error('User is not registered') const match = await bcrypt.compare(payload.password, user.password); if (!match) throw new Error('Invalid username or password') return user } }
```

### app.module.ts

```
@Module({ imports: [DatabaseModule, AuthModule, UsersModule,], controllers: [AppController], providers: [AppService], }) export class AppModule implements NestModule { // add authorization middleware for the following routes configure(consumer: MiddlewareConsumer) { consumer .apply(AuthMiddleware) .forRoutes( 'demo', 'demo/create', 'demo/edit/:id', 'demo/delete/:id', // add here all the routes that you want to be protected ); } }
```

### user.dto.ts

```
import { IsNotEmpty } from "class-validator"; export class UserDto { id?: number; @IsNotEmpty() username: string; @IsNotEmpty() password: String; }
```

## user.service.ts

```
import { Injectable, Inject } from '@nestjs/common'; import { Op } from 'sequelize'; import { newTransaction } from '../../../src/database'; import { UserDto } from './user.dto'; @Injectable() export class UsersService { private model constructor( @Inject('SEQUELIZE') private readonly sequelize, ) { this.model = this.sequelize.models.users } async findByUsername(username: string): Promise\<UserDto\> { return await this.model.findOne({ where: { username: { [Op.eq]: username } } }) } }
```

### auth.filters.ts

```
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'; import { Response } from 'express'; @Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { if (exception.getStatus() === HttpStatus.UNAUTHORIZED) { const ctx = host.switchToHttp(); const response = ctx.getResponse\<Response\>(); return response.redirect('/auth/login') } } }
```

### main.ts

```
require('dotenv').config({ path: '../.env' }) // initialize dotenv import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; import { join } from 'path'; import { AppModule } from './app.module'; import { HttpExceptionFilter } from './auth/auth.filters'; import \* as session from 'express-session' const fs = require('fs'); const hbs = require('hbs'); async function bootstrap() { const app = await NestFactory.create\<NestExpressApplication\>(AppModule); // session app.use(session({ secret: process.env.SESSION\_KEY, cookie: { maxAge: 86400000, // 24h } })) app.useGlobalFilters(new HttpExceptionFilter()); // initialize handlebars js app.useStaticAssets(join(\_\_dirname, '..', 'public')); app.setBaseViewsDir(join(\_\_dirname, '..', 'views')); app.setViewEngine('hbs'); // load partials loadComponents('templates'); loadComponents('auth'); require('handlebars-form-helpers').register(hbs.handlebars); await app.listen(3000); } bootstrap(); function loadComponents(pathName: string) { const partialsDir = \_\_dirname + `/../views/${pathName}`; const filenames = fs.readdirSync(partialsDir); filenames.forEach(function (filename) { var matches = /^([^.]+).hbs$/.exec(filename); if (!matches) { return; } const name = `${pathName}_${matches[1]}`; const template = fs.readFileSync(partialsDir + '/' + filename, 'utf8'); hbs.registerPartial(name, template); }); }
```

### auth/login.hbs

```
{{#\> templates\_auth\_layout titlePage="Login" }} {{#form ""}} \<div class="form-group"\> {{label\_validation 'username' 'Username \*' errors class="control-label"}} {{input 'username' data.username class="form-control"}} \<small class="form-text text-danger"\>{{#field\_errors "username" errors}}{{this}}{{/field\_errors}}\</small\> \</div\> \<div class="form-group"\> {{label\_validation 'password' 'Password \*' errors class="control-label"}} {{input 'password' data.password class="form-control"}} \<small class="form-text text-danger"\>{{#field\_errors "password" errors}}{{this}}{{/field\_errors}}\</small\> \</div\> {{submit 'submit' 'Login' class="btn btn-primary"}} {{/form}} {{/templates\_auth\_layout}}
```

I hope this was useful. If you have any [question](/tutorials/pid-process-specific-port/) for how I implemented authentication please let me know right here in comments.

_The post [NestJS authentication for server side rendering with Handlebars](https://boobo94.xyz/tutorials/nestjs-authentication/) appeared first on [boobo94.xyz](https://boobo94.xyz). So if you want to read more articles like this please [subscribe to my newsletter](https://boobo94.xyz/#colophon) or follow me here_