How To Build A Twitter Clone With NestJS, Prisma And React ( Part 2 )
Tags: node, nestjs, prisma, react
Published: 2020-09-14 13:57:28

> This tutorial has been originally published on my [Hashnode blog](https://darioielardi.hashnode.dev/how-to-build-a-twitter-clone-with-nestjs-prisma-and-react-part-2).

- [Part 1: ( Setup & first steps )](https://dev.to/darioielardi/how-to-build-a-twitter-clone-with-nestjs-prisma-and-react-part-1-lgd)
- Part 2: Authentication ( this article )
- Part 3 Coming Soon

---

## Authentication

There are a lot of different authentication strategies to protect our API endpoints.

Generally, I strongly suggest to delegate such a crucial feature to a dedicated service such as [Firebase Authentication](https://firebase.google.com/products/auth), [AWS Cognito](https://aws.amazon.com/cognito/) or [Auth0](https://auth0.com/).
However, today we're going to build a basic and **incomplete** authentication system to understand how Nest approaches the problem.

Let me say that again: this is not a complete solution, it's far from being secure and production-ready since it lacks a lot of essential features for a good authentication system.
We just want to explore the possibilities Nest gives us to implement authentication in our server and how it can integrate existing solutions.

The authentication system we are going to build is based on JSON Web Tokens ( JWT ). Those are essentially a standard and secure way to transmit information over the network, encrypted and signed by your server to be verified on every request.

The authentication flow is basically this:

1. A user will ask for a JWT sending a request to the `auth/login` endpoint with his username and password in the request body.
2. If that information is correct, the server will generate, encrypt and send back a signed JWT, which will carry the username and will have an expiration time.
3. On every subsequent request, the user will send the received JWT in the `Authorization` header, which will be verified by the server. If the token is valid and the expiration time has not passed, the server will proceed to handle the request, and it will know which user made it thanks to the username stored in the JWT.

Sending the access token for every request very much exposes it to _man-in-the-middle_ attacks, that's why this authentication system usually requires a very short token expiration time and a mechanism to _refresh_ the token.
Since this is beyond the scope of this tutorial, we will set an expiration time of one hour, after which the user will need to ask for another token sending his username and password to the `auth/login` endpoint again.

To learn more about JWT you can read [this](https://jwt.io/introduction/) well-crafted introduction.

## Guards

Nest provides a very versatile element to handle endpoints protection: [**guards**](https://docs.nestjs.com/guards).

A guard is just an `Injectable` class which implements the `CanActivate` interface. It can be _applied_ to any endpoint or a whole controller class.

Guards do not enforce a particular authentication strategy, they are just used to tell Nest to run some code before the request gets passed to the handler method.

To implement our first guard let's first generate the `auth` module.

```bash
nest generate module auth
nest generate service auth
```

We can now generate the guard in the same module.

```bash
nest generate guard auth/simple
```

Let's take a look at the generated file.

```typescript
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class SimpleGuard implements CanActivate {
  canActivate(
    context: ExecutionContext
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}
```

As you can see the only thing we need here is the `canActivate` method.
When this guard is applied to an endpoint or a controller, Nest calls the `canActivate` method before every request, and, based on its `boolean` return value, it either passes the request to the controller or returns a `403 Forbidden` response. Of course, we can throw any other exception and it will be caught and send back to the client.

The most powerful feature of this method is that it can access the request object, thanks to its `context` argument.

Let's update this guard to check the presence of an `MY_AUTH_TOKEN` string in the `Authorization` header.

```typescript
// ...
export class SimpleGuard implements CanActivate {
  canActivate(
    context: ExecutionContext
  ): boolean | Promise<boolean> | Observable<boolean> {
    const req: Request = context.switchToHttp().getRequest();

    const token = req.headers['authorization'];

    if (!token) {
      throw new UnauthorizedException('token_not_found');
    }

    if (token !== 'MY_AUTH_TOKEN') {
      throw new UnauthorizedException('invalid_token');
    }

    return true;
  }
}
```

To apply this guard to an endpoint or a controller we can use the `UseGuards` decorator. Let's do that with the `getHello` method in the `AppController`.

```typescript
// src/app.controller.ts

import {
  // ...
  UseGuards,
} from '@nestjs/common';
import { SimpleGuard } from './auth/simple.guard';
// ...

@Controller()
export class AppController {
  // ...

  @UseGuards(SimpleGuard)
  @Get('hello')
  getHello(): string {
    return this.appService.getHello();
  }
}
```

Let's test this out.

```bash
http localhost:3000/hello
```

```json
HTTP/1.1 401 Unauthorized

{
  "error": "Unauthorized",
  "message": "token_not_found",
  "statusCode": 401
}
```

```bash
http localhost:3000/hello Authorization:"INVALID_TOKEN"
```

```json
HTTP/1.1 401 Unauthorized

{
  "error": "Unauthorized",
  "message": "invalid_token",
  "statusCode": 401
}
```

```bash
http localhost:3000/hello Authorization:"MY_AUTH_TOKEN"
```

```json
HTTP/1.1 200 OK

Hello World!
```

We now know what a guard is and how to use it.

However, to implement our authentication system we are not going to write a guard, and that's because someone already wrote one for us.

## Passport

Nest provides us an additional module to integrate with [passport](http://www.passportjs.org/), the most popular and mature NodeJS authentication library.

**Passport** acts as a toolset capable of handling a lot of different authentication strategies. The key to make it work in a Nest application is, once again, to encapsulate the one we need in an injectable service. Once we do that, we can use a built-in guard exported by the `@nestjs/passport` library to let passport do its work for every incoming request.

Let's install everything we need.

```bash
npm install @nestjs/passport passport @nestjs/jwt passport-jwt
npm install @types/passport-jwt --save-dev
```

As you can see, we also installed `@nestjs/jwt`, which is a utility package to manipulate JWTs, thanks to the [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) library which it encapsulates.

We will now need some JWT configuration constants which we can store in the `auth/jwt.constants.ts` file.

```typescript
export const jwtConstants = {
  secret: 'secretKey',
};
```

The `secret` field is going to be used by passport to sign and verify every generated JWT. We usually want to provide a more robust and complicated secret.

Next, we are going to import the `PassportModule` and `JwtModule` provided by the `@nestjs/passport` and `@nestjs/jwt` packages in our `AuthModule`'s `imports`.

```typescript
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { jwtConstants } from './jwt.constants';

@Module({
  imports: [
    PassportModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '1h' },
    }),
  ],
  providers: [AuthService],
})
export class AuthModule {}
```

The `JwtModule.register` is a sort of factory to allow us to provide some configuration to the `JwtModule`. This technique is pretty frequent in the NestJS world, and we refer to it as [dynamic modules](https://docs.nestjs.com/fundamentals/dynamic-modules).

To be able to access the database in the `AuthService` we now need to import our `PrismaService` in the `AuthModule.providers` field.

```typescript
// ...
import { PrismaService } from '../prisma.service';
// ...
@Module({
  // ...
  providers: [AuthService, PrismaService],
  // ...
```

Next, we will create an `auth.dto.ts` file with a `LoginDto` class and an `AuthResponse`, and in our `AuthService` class we will implement the `login` method.
This method will then:

1. Check if a user with the provided username really exists.
2. Validate the password using the **bcrypt** library, comparing it with the hash in our database.
3. Generate and return a signed JWT along with the user object.

```typescript
// auth.dto.ts

import { IsString, Length } from 'class-validator';
import { User } from '@prisma/client';

export class LoginDto {
  @IsString()
  @Length(3, 30)
  username: string;

  @IsString()
  @Length(6, 30)
  password: string;
}

export class AuthResponse {
  token: string;
  user: User;
}
```

```typescript
import {
  Injectable,
  NotFoundException,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from '../prisma.service';
import { LoginDto } from './auth.dto';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(private db: PrismaService, private jwt: JwtService) {}

  async login(data: LoginDto): Promise<AuthResponse> {
    const { username, password } = data;

    const user = await this.db.user.findOne({
      where: { username },
    });

    if (!user) {
      throw new NotFoundException();
    }

    const passwordValid = await bcrypt.compare(password, user.password);

    if (!passwordValid) {
      throw new UnauthorizedException('invalid_password');
    }

    delete user.password;

    return {
      token: this.jwt.sign({ username }),
      user,
    };
  }
}
```

Everything here is pretty clear. Notice how we asked Nest to inject the `JwtService` from the `@nestjs/jwt` package to be used inside our class.
This is only possible because the `JwtService` is an exported provider in the `JwtModule` we imported in the `AuthModule`. We'll see how this mechanism works with a local module later on.

We can now generate our auth controller and implement the `auth/login` endpoint.

```bash
nest generate controller auth
```

```typescript
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginDto, AuthResponse } from './auth.dto';

@Controller('auth')
export class AuthController {
  constructor(private service: AuthService) {}

  @Post('login')
  login(@Body() data: LoginDto): Promise<AuthResponse> {
    return this.service.login(data);
  }
}
```

Let's test this out:

```bash
http POST localhost:3000/auth/login username="jack" password="invalid"
```

```json
HTTP/1.1 401 Unauthorized

{
  "error": "Unauthorized",
  "message": "invalid password",
  "statusCode": 401
}
```

```bash
http POST localhost:3000/auth/login username="jack" password="123456"
```

```json
HTTP/1.1 201 Created

{
  "token": "<a very long token>",
  "user": {
    "username": "jack",
    "displayName": "Jack"
  }
}
```

It definitely seems to work.

We now need to implement a _strategy_, extending the default one exported by `passport-jwt`, which will make passport able to verify the JWT on every request.

Let's create the `auth/jwt.strategy.ts` file.

```typescript
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { jwtConstants } from './jwt.constants';
import { PrismaService } from '../prisma.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private db: PrismaService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: jwtConstants.secret,
    });
  }

  async validate(payload: { username: string }) {
    const user = await this.db.user.findOne({
      where: { username: payload.username },
    });

    return user;
  }
}
```

Let's analyze what we're doing here:

- We're creating an injectable class extending the passport strategy exported from `passport-jwt` and wrapped by the `PassportStragey` utility function exported by `@nestjs/passport`.
- We're passing some configuration data to the strategy constructor, and injecting the `PrismaService` at the same time.
- The `validate` method will only be called by passport when a valid JWT has been found in the `Authorization` header. The return value of this method will be attached to the `request` object by passport, and will be accessible in every controller handler as `request.user`. Therefore we just need to fetch the user from the database and return it.

We can now add this new strategy class to the `providers` list of the `AuthModule`.

```typescript
// auth.module.ts

// ..
import { JwtStrategy } from './jwt.strategy';

@Module({
  // ...
  providers: [AuthService, PrismaService, JwtStrategy],
  // ...
```

We are now ready to apply our JWT authentication system to our endpoints through a guard.

The `@nestjs/passport` module exports a built-in `AuthGuard` to be used in our `UseGuards` decorator. Let's do that with our `UsersController`.

```typescript
// users.controller.ts

import {
  // ...
  UseGuards
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@UseGuards(AuthGuard('jwt'))
@Controller('users')
export class UsersController {
// ...
```

Passing the `jwt` string parameter, Nest will look for a provider class anywhere among our application's dependencies which extends the `Strategy` exported by the `passport-jwt` strategy, and it will find our `JwtStrategy` class.

Every endpoint in this controller is now protected. Let's test this out.

```bash
http localhost:3000/users/jack
```

```json
HTTP/1.1 401 Unauthorized

{
  "message": "Unauthorized",
  "statusCode": 401
}
```

As we can see, without an authentication token in the `Authorization` header we always receive a 401 error. Let's get one with our `auth/login` endpoint.

```bash
http POST localhost:3000/auth/login username="jack" password="123456"
```

```json
HTTP/1.1 201 Created

{
  "token": "<auth token>",
  "user": {...}
}
```

Just copy the received token and export it in an environment variable like this:

```bash
export TOKEN="<your token here>"
```

You can now use it for every request like this:

```bash
http localhost:3000/users/jack Authorization:"Bearer $TOKEN"
```

```json
HTTP/1.1 200 OK

{
  "displayName": "Jack",
  "password": "123456",
  "username": "jack"
}
```

Let's now see how we can access the authenticated user in a handler method.

### Custom decorators

As we already know, the `JwtStrategy` takes care of attaching the result of the `validate` function in the request object, which is the user we fetched from the database.

The request object is the same you may know if you ever used the [express](https://expressjs.com/) framework, which Nest is based on and which we got already installed by the Nest CLI.
To access it in a controller method we can use the `Req` decorator.
Let's implement a new protected endpoint `auth/me` to demonstrate that.

```typescript
// auth.controller.ts

import {
  // ...
  Get,
  UseGuards,
  Req,
} from '@nestjs/common';
import { User } from '@prisma/client';
import { AuthGuard } from '@nestjs/passport';
import { Request } from 'express';

// ...

  @UseGuards(AuthGuard('jwt'))
  @Get('me')
  me(@Req() req: Request): User {
    const user = req.user as User;
    delete user.password;
    return user;
  }

// ...
```

And let's test it.

```bash
http localhost:3000/auth/me Authorization:"Bearer $TOKEN"
```

```json
HTTP/1.1 200 OK

{
  "displayName": "Jack",
  "username": "jack",
}
```

As we can see there is something pretty disturbing in this implementation.
Every time we need to access the `user` object we have to cast it to the right `User` type and eventually remove the `password` field, which will become annoying as soon as our application grows.
This is a perfect use case for a **custom decorator**.

Let's create a new file `src/common/decorators/auth-user.decorator.ts`.

```typescript
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { User } from '@prisma/client';

const AuthUser = createParamDecorator((_, ctx: ExecutionContext) => {
  const request = ctx.switchToHttp().getRequest();
  const user = request.user as User;
  delete user.password;
  return user;
});

export default AuthUser;
```

While for a simple class or function decorator we could simply use the Typescript syntax, Nest provides us a `createParamDecorator` utility specifically for arguments of controllers' handlers.
We provide a function as the only argument, whose second argument is the server `ExecutionContext`, from which we can get the `request` object.

Now we can replace the `Req` decorator with our new `AuthUser` decorator in the `me` handler.

```typescript
// auth.controller.ts

// ...
import AuthUser from '../common/decorators/auth-user.decorator';
// ...

  @UseGuards(AuthGuard('jwt'))
  @Get('me')
  me(@AuthUser() user: User): User {
    return user;
  }

// ...
```

**Custom decorators** are a very powerful feature of Nest. More on that in the dedicated [page](https://docs.nestjs.com/custom-decorators) of the Nest documentation.

### User registration

The last thing we need to handle is user registration.
Right now is barely implemented in the `UsersController`, but we want to properly implement it in the `AuthController` as a new `auth/register` endpoint.

After the new user has been created we should generate and send back a JWT to let him authenticate on subsequent requests, without the need to call the `auth/login` endpoint.

Let's add a new `RegisterDto` class to the `auth.dto.ts` file, identical to the `CreateUserDto` ( you can actually copy that ).

```typescript
// auth.dto.ts

// ...
export class RegisterDto {
  @IsString()
  @Length(3, 30)
  username: string;

  @IsString()
  @Length(6, 30)
  password: string;

  @IsString()
  @Length(1, 50)
  displayName: string;
}
```

We can now implement our `register` method in the `AuthService`, and to do that we want to take advantage of the `create` method we have in the `UsersService`.
This means the `UsersModule` has to _expose_ that feature **exporting** the `UsersService` to be used by other modules.
To do that we just need to add an `exports` field to the `Module` decorator of the `UsersModule`, and put the `UsersService` inside.

```typescript
// ...
import { UsersService } from './users.service';

@Module({
  // ...
  exports: [UsersService],
})
export class UsersModule {}
```

This way, any other module can import the `UsersModule` to take advantage of any of the exported classes.

Let's do that with the `AuthModule`.

```typescript
// ...
import { UsersModule } from '../users/users.module';

@Module({
  imports: [
    UsersModule,
    // ...
  ],
  // ...
})
export class AuthModule {}
```

Now, thanks to the power of Nest, we can easily inject the `UsersService` into the `AuthService` and implement our `register` method.

```typescript
import { LoginDto, RegisterDto, AuthResponse } from './auth.dto';
import { UsersService } from '../users/users.service';

@Injectable()
export class AuthService {
  constructor(
    // ...
    private users: UsersService
  ) {}
  // ...
  async register(data: RegisterDto): Promise<AuthResponse> {
    const user = await this.users.create(data);
    return {
      token: this.jwt.sign({ username: user.username }),
      user,
    };
  }
}
```

Let's now wire our new method to the corresponding `auth/register` endpoint.

```typescript
// ...
import { LoginDto, RegisterDto, AuthResponse } from './auth.dto';

@Controller('auth')
export class AuthController {
  // ...
  @Post('register')
  register(@Body() data: RegisterDto): Promise<AuthResponse> {
    return this.service.register(data);
  }
  // ...
}
```

Finally, we just need to clean everything up removing the `create` method from the `UsersController`.

Let's test the new `auth/register` endpoint.

```bash
http POST localhost:3000/auth/register username="mary" displayName="Mary" password="secret"
```

```json
HTTP/1.1 201 Created

{
  "token": "<generated code>",
  "user": {
    "username": "mary",
    "displayName": "Mary"
  }
}
```

```bash
export TOKEN="<our new token>"
http localhost:3000/auth/me Authorization:"Bearer $TOKEN"
```

```json
HTTP/1.1 200 OK

{
  "displayName": "Mary",
  "username": "mary"
}
```

We are now ready to implement our main application feature: **tweets**.