NestJS Authentication with OAuth2.0: Express Local OAuth REST API
Tags: typescript, nestjs, tutorial, node
Published: 2023-01-20 12:55:08

## Series Intro

This series will cover the full implementation of OAuth2.0 Authentication in [NestJS](https://nestjs.com/) for the following types of APIs:

- [Express](https://expressjs.com/) REST API;
- [Fastify](https://www.fastify.io/) REST API;
- [Apollo GraphQL](https://www.apollographql.com/docs/apollo-server/) API.

And it is divided in 5 parts:

- Configuration and operations;
- Express Local OAuth REST API;
- Fastify Local OAuth REST API;
- Apollo Local OAuth GraphQL API;
- Adding External OAuth Providers to our API;

Lets start the second part of this series.

## Tutorial Intro

On this tutorial we will continue building on the previous [article](https://dev.to/tugascript/nestjs-authentication-with-oauth20-configuration-and-operations-41k), by creating an Express REST API.

<small><b>TLDR:</b> if you do not have 45 minutes to read the article, the code can be found on this [repo](https://github.com/tugascript/nestjs-oauth/tree/express-rest-api)</small>

## Set up

Start by creating controllers for both the `auth` and `users`:

```bash
$ nest g co auth
$ nest g co users
```

## Auth Module

Before implementing the endpoints we need both an auth guard, and some custom decorators:

- _Public_: to make an endpoint public;
- _Origin_: to get the origin from cross-origin requests:
- _CurrentUser_: to get the current logged user id;

### Decorators

Before creating decorators, since we will have a user field on our express `Request` interface install `express-serve-static-core`:

```bash
$ yarn add -D @types/express-serve-static-core
```

And extend express on a `types` folder:

```ts
// express.d.ts

import { Request as ExpressRequest } from 'express';

declare module 'express-serve-static-core' {
  interface Request extends ExpressRequest {
    user?: number;
  }
}
```

To be able to create a global auth guard we need to create the `Public` decorator first, this decorator can be found [here](https://docs.nestjs.com/security/authentication#enable-authentication-globally) in the docs, on the `decorators` folder add:

```ts
// public.decorator.ts

import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
```

To validate that our tokens come from the correct origin we need to get the domain from the the front-end:

```ts
// origin.decorator.ts

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { Request } from 'express-serve-static-core';

export const Origin = createParamDecorator(
  (_, context: ExecutionContext): string | undefined => {
    return context.switchToHttp().getRequest<Request>().headers?.origin;
  },
);

```

Finally we need to be able to get the current logged user ID:

```ts
// current-user.decorator.ts

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { Request } from 'express-serve-static-core';

export const CurrentUser = createParamDecorator(
  (_, context: ExecutionContext): number | undefined => {
    return context.switchToHttp().getRequest<Request>()?.user;
  },
);
```

### Guards

#### Auth Guard

We need a global `AuthGuard` to protect our endpoints, on a `guards` folder, we can create a [`CanActivate` guard](https://docs.nestjs.com/guards#authorization-guard):

```ts
import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { isJWT } from 'class-validator';
import { Request } from 'express';
import { isNull, isUndefined } from '../../common/utils/validation.util';
import { TokenTypeEnum } from '../../jwt/enums/token-type.enum';
import { JwtService } from '../../jwt/jwt.service';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly jwtService: JwtService,
  ) {}

  public async canActivate(context: ExecutionContext): Promise<boolean> {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    const activate = await this.setHttpHeader(
      context.switchToHttp().getRequest(),
      isPublic,
    );

    if (!activate) {
      throw new UnauthorizedException();
    }

    return activate;
  }

  /**
   * Sets HTTP Header
   *
   * Checks if the header has a valid Bearer token, validates it and sets the User ID as the user.
   */
  private async setHttpHeader(
    req: Request,
    isPublic: boolean,
  ): Promise<boolean> {
    const auth = req.headers?.authorization;

    if (isUndefined(auth) || isNull(auth) || auth.length === 0) {
      return isPublic;
    }

    const authArr = auth.split(' ');
    const bearer = authArr[0];
    const token = authArr[1];

    if (isUndefined(bearer) || isNull(bearer) || bearer !== 'Bearer') {
      return isPublic;
    }
    if (isUndefined(token) || isNull(token) || !isJWT(token)) {
      return isPublic;
    }

    try {
      const { id } = await this.jwtService.verifyToken(
        token,
        TokenTypeEnum.ACCESS,
      );
      req.user = id;
      return true;
    } catch (_) {
      return isPublic;
    }
  }
}
```

Finally add the guard to the `AppModule`:

```ts
// ...
import { CacheModule, Module } from '@nestjs/common';
// ...
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth/guards/auth.guard';
// ...

@Module({
  // ...
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
export class AppModule {}
```


#### (Optional) Throttler Guard

To protect our `auth` endpoints against brute force attacks we should add rate limiting to our API, NestJS has a [`ThrottlerModule`](https://docs.nestjs.com/security/rate-limiting#rate-limiting), start by installing it (and optionally the Redis storage for it):

```bash
$ yarn add @nestjs/throttler nestjs-throttler-storage-redis
```

Add the throttler options to `.env`:

```.env
# ...
# Throttler config
THROTTLE_TTL=60
THROTTLE_LIMIT=20
```

To the schema:

```ts
import Joi from 'joi';

export const validationSchema = Joi.object({
  // ...
  THROTTLE_TTL: Joi.number().required(),
  THROTTLE_LIMIT: Joi.number().required(),
});
```

To the `config.interface.ts` file:

```ts
// ...
import { ThrottlerModuleOptions } from '@nestjs/throttler';
// ...

export interface IConfig {
  // ...
  throttler: ThrottlerModuleOptions;
  // ...
}
```

And on the `index.ts` of the `config` folder:

```ts
// ...
import { IConfig } from './interfaces/config.interface';
// ...

export function config(): IConfig {
  // ...

  return {
    // ...
    throttler: {
      ttl: parseInt(process.env.THROTTLE_TTL, 10),
      limit: parseInt(process.env.THROTTLE_LIMIT, 10),
    },
    // ...
  };
}
```

Now add a config class for the throttler:

```ts
// throttler.config.ts

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
  ThrottlerModuleOptions,
  ThrottlerOptionsFactory,
} from '@nestjs/throttler';
import { RedisOptions } from 'ioredis';
import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis';

@Injectable()
export class ThrottlerConfig implements ThrottlerOptionsFactory {
  constructor(private readonly configService: ConfigService) {}

  createThrottlerOptions(): ThrottlerModuleOptions {
    return {
      ...this.configService.get<ThrottlerModuleOptions>('throttler'),
      storage: new ThrottlerStorageRedisService(
        this.configService.get<RedisOptions>('redis'),
      ),
    };
  }
}
```

Register the `ThrottlerModule` on the `AuthModule`:

```ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ThrottlerModule } from '@nestjs/throttler';
import { ThrottlerConfig } from '../config/throttler.config';
// ...

@Module({
  imports: [
    // ...
    ThrottlerModule.forRootAsync({
      imports: [ConfigModule],
      useClass: ThrottlerConfig,
    }),
  ],
  // ...
})
export class AuthModule {}
```

Finally add the `ThrottlerGuard` to the auth controller:

```ts
import { Controller, UseGuards } from '@nestjs/common';
import { ThrottlerGuard } from '@nestjs/throttler';

@Controller('auth')
@UseGuards(ThrottlerGuard)
export class AuthController {}
```

### Controller

#### Mappers

Some data is private and should never be sent by the response of our API, such as the user password and the refresh token. Therefore, we need to create mapper functions that reflect what we want to send as a response of our API.

**Interfaces**

There are two interfaces that we need, one with the user data that we want to send and one for the Auth Result.

_Auth Response User_

```ts
// auth-response-user.interface.ts

export interface IAuthResponseUser {
  id: number;
  name: string;
  username: string;
  email: string;
  createdAt: string;
  updatedAt: string;
}
```

_Auth Response_

```ts
// auth-response.interface.ts

import { IAuthResponseUser } from './auth-response-user.interface';

export interface IAuthResponse {
  user: IAuthResponseUser;
  accessToken: string;
}
```

**Classes**

Now for the mapper we can either add a static method to our controllers or create separate classes with a map static method. I recommend the latter as it is easier to add OpenAPI specifications latter on.

Create a new folder called `mappers` and add the following files:

1. `auth-response-user.mapper.ts`:
    ```ts
    import { IUser } from '../../users/interfaces/user.interface';
    import { IAuthResponseUser } from '../interfaces/auth-response-user.interface';

    export class AuthResponseUserMapper implements IAuthResponseUser {
      public id: number;
      public name: string;
      public username: string;
      public email: string;
      public createdAt: string;
      public updatedAt: string;

      constructor(values: IAuthResponseUser) {
        Object.assign(this, values);
      }

      public static map(user: IUser): AuthResponseUserMapper {
        return new AuthResponseUserMapper({
          id: user.id,
          name: user.name,
          username: user.username,
          email: user.email,
          createdAt: user.createdAt.toISOString(),
          updatedAt: user.updatedAt.toISOString(),
        });
      }
    }
    ```
2. `auth-response.mapper.ts`:
    ```ts
    import { IAuthResponse } from '../interfaces/auth-response.interface';
    import { IAuthResult } from '../interfaces/auth-result.interface';
    import { AuthResponseUserMapper } from './auth-response-user.mapper';

    export class AuthResponseMapper implements IAuthResponse {
      public user: AuthResponseUserMapper;
      public accessToken: string;

      constructor(values: IAuthResponse) {
        Object.assign(this, values);
      }

      public static map(result: IAuthResult): AuthResponseMapper {
        return new AuthResponseMapper({
          user: AuthResponseUserMapper.map(result.user),
          accessToken: result.accessToken,
        });
      }
    }
    ```

#### Methods

While most of the business logic resides on the service, we still need some logic to be added here on the controller.

Start by injecting the `AuthService` and `UserService`, and by getting the `COOKIE_NAME` and `JWT_REFRESH_TIME` from the `ConfigService`:

```ts
import { Controller, UseGuards } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { UsersService } from '../users/users.service';
import { AuthService } from './auth.service';
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  private readonly cookiePath = '/api/auth';
  private readonly cookieName: string;
  private readonly refreshTime: number;
  private readonly testing: boolean;

  constructor(
    private readonly authService: AuthService,
    private readonly usersService: UsersService,
    private readonly configService: ConfigService,
  ) {
    this.cookieName = this.configService.get<string>('REFRESH_COOKIE');
    this.refreshTime = this.configService.get<number>('jwt.refresh.time');
    this.testing = this.configService.get<string>('NODE_ENV') !== 'production';
  }
}
```

And add two methods:

1. Getting refresh token out of the request:
    ```ts
    // ...

    @Controller('api/auth')
    @UseGuards(ThrottlerGuard)
    export class AuthController {
      // ...

      private refreshTokenFromReq(req: Request): string {
        const token: string | undefined = req.signedCookies[this.cookieName];

        if (isUndefined(token)) {
          throw new UnauthorizedException();
        }

        return token;
      }
    }
    ```
2. Saving the refresh token in a cookie:
    ```ts
    @Controller('api/auth')
    @UseGuards(ThrottlerGuard)
    export class AuthController {
      // ...

      private saveRefreshCookie(res: Response, refreshToken: string): Response {
        return res.cookie(this.cookieName, refreshToken, {
          secure: !this.testing,
          httpOnly: true,
          signed: true,
          path: this.cookiePath,
          expires: new Date(Date.now() + this.refreshTime * 1000),
        });
      }
    }
    ```

#### Endpoints

The endpoints are the same as the public methods we have on our service.

_Sign Up_

```ts
import {
  Body,
  //...
  Post,
  // ...
} from '@nestjs/common';
// ...
import { Origin } from './decorators/origin.decorator';
import { Public } from './decorators/public.decorator';
// ...
import { SignUpDto } from './dtos/sign-up.dto';


@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Public()
  @Post('/sign-up')
  public async signUp(
    @Origin() origin: string | undefined,
    @Body() signUpDto: SignUpDto,
  ): Promise<IMessage> {
    return this.authService.signUp(signUpDto, origin);
  }

  // ...
}
```

_Sign In_

```ts
import {
  Body,
  HttpStatus,
  Res,
  // ...
} from '@nestjs/common';
import { SignInDto } from './dtos/sign-in.dto';
import { AuthResponseMapper } from './mappers/auth-response.mapper';

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Public()
  @Post('/sign-in')
  public async signIn(
    @Res() res: Response,
    @Origin() origin: string | undefined,
    @Body() singInDto: SignInDto,
  ): Promise<void> {
    const result = await this.authService.signIn(singInDto, origin);
    this.saveRefreshCookie(res, result.refreshToken)
      .status(HttpStatus.OK)
      .json(AuthResponseMapper.map(result));
  }

  // ...
}
```

_Refresh Access_

```ts
import {
  //...
  Req,
  // ...
} from '@nestjs/common';
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Public()
  @Post('/refresh-access')
  public async refreshAccess(
    @Req() req: Request,
    @Res() res: Response,
  ): Promise<void> {
    const token = this.refreshTokenFromReq(req);
    const result = await this.authService.refreshTokenAccess(
      token,
      req.headers.origin,
    );
    this.saveRefreshCookie(res, result.refreshToken)
      .status(HttpStatus.OK)
      .json(AuthResponseMapper.map(result));
  }

  // ...
}
```

_Logout_

```ts
import {
  //...
  HttpCode,
  // ...
} from '@nestjs/common';
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Post('/logout')
  @HttpCode(HttpStatus.OK)
  public async logout(
    @Req() req: Request,
    @Res() res: Response,
  ): Promise<void> {
    const token = this.refreshTokenFromReq(req);
    const message = await this.authService.logout(token);
    res
      .clearCookie(this.cookieName, { path: this.cookiePath })
      .status(HttpStatus.OK)
      .json(message);
  }

  // ...
}
```

_Confirm Email_

```ts
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Public()
  @Post('/confirm-email')
  public async confirmEmail(
    @Body() confirmEmailDto: ConfirmEmailDto,
    @Res() res: Response,
  ): Promise<void> {
    const result = await this.authService.confirmEmail(confirmEmailDto);
    this.saveRefreshCookie(res, result.refreshToken)
      .status(HttpStatus.OK)
      .json(AuthResponseMapper.map(result));
  }

  // ...
}
```

_Forgot Password_


```ts
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Post('/forgot-password')
  @HttpCode(HttpStatus.OK)
  public async forgotPassword(
    @Origin() origin: string | undefined,
    @Body() emailDto: EmailDto,
  ): Promise<IMessage> {
    return this.authService.resetPasswordEmail(emailDto, origin);
  }

  // ...
}
```

_Reset Password_

```ts
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Public()
  @Post('/reset-password')
  @HttpCode(HttpStatus.OK)
  public async resetPassword(
    @Body() resetPasswordDto: ResetPasswordDto,
  ): Promise<IMessage> {
    return this.authService.resetPassword(resetPasswordDto);
  }

  // ...
}
```

_Update Password_

```ts
import {
  // ...
  Patch,
  // ...
} from '@nestjs/common';
import { CurrentUser } from './decorators/current-user.decorator';
import { ChangePasswordDto } from './dtos/change-password.dto';
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Patch('/update-password')
  public async updatePassword(
    @CurrentUser() userId: number,
    @Origin() origin: string | undefined,
    @Body() changePasswordDto: ChangePasswordDto,
    @Res() res: Response,
  ): Promise<void> {
    const result = await this.authService.updatePassword(
      userId,
      changePasswordDto,
      origin,
    );
    this.saveRefreshCookie(res, result.refreshToken)
      .status(HttpStatus.OK)
      .json(AuthResponseMapper.map(result));
  }

  // ...
}
```

_Me_

This one is necessary to get the current logged user

```ts
import {
  // ...
  Get,
  // ...
} from '@nestjs/common';
import { IAuthResponseUser } from '../auth/interfaces/auth-response-user.interface';
import { AuthResponseUserMapper } from '../auth/mappers/auth-response-user.mapper';
// ...

@Controller('api/auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  // ...

  @Get('/me')
  public async getMe(@CurrentUser() id: number): Promise<IAuthResponseUser> {
    const user = await this.usersService.findOneById(id);
    return AuthResponseUserMapper.map(user);
  }

  // ...
}
```

## User Module

### User Service

Before adding our controller we need to make some changes to the service:

1. Add a `PasswordDto`:
    ```ts
    // password.dto.ts

    import { IsString, MinLength } from 'class-validator';

    export abstract class PasswordDto {
      @IsString()
      @MinLength(1)
      public password: string;
    }
    ```
2. Update the `ChangeEmailDto`:
    ```ts
    // change-email.dto.ts

    import { IsEmail, IsString, Length } from 'class-validator';
    import { PasswordDto } from './password.dto';

    export abstract class ChangeEmailDto extends PasswordDto {
      @ApiProperty({
        description: 'The email of the user',
        example: 'someone@gmail.com',
        minLength: 5,
        maxLength: 255,
        type: String,
      })
      @IsString()
      @IsEmail()
      @Length(5, 255)
      public email: string;
    }
    ```
3. Add password verification to `delete` method:
    ```ts
    // ...
    import {
      BadRequestException,
      // ...
    } from '@nestjs/common';
    import { compare, hash } from 'bcrypt';
    // ...
    import { PasswordDto } from './dtos/password.dto';
    import { UserEntity } from './entities/user.entity';

    @Injectable()
    export class UsersService {
      // ...

      public async delete(userId: number, dto: PasswordDto): Promise<UserEntity> {
        const user = await this.findOneById(userId);

        if (!(await compare(dto.password, user.password))) {
          throw new BadRequestException('Wrong password');
        }

        await this.commonService.removeEntity(this.usersRepository, user);
        return user;
      }

      // ...
    }
    ```
4. Add a method to find by id or username:
    ```ts
    // ...

    @Injectable()
    export class UsersService {
    // ...

      public async findOneByIdOrUsername(
        idOrUsername: string,
      ): Promise<UserEntity> {
        const parsedValue = parseInt(idOrUsername, 10);

        if (!isNaN(parsedValue) && parsedValue > 0 && isInt(parsedValue)) {
          return this.findOneById(parsedValue);
        }

        if (
          idOrUsername.length < 3 ||
          idOrUsername.length > 106 ||
          !SLUG_REGEX.test(idOrUsername)
        ) {
          throw new BadRequestException('Invalid username');
        }

        return this.findOneByUsername(idOrUsername);
      }

    // ...
    }
    ```

### User Controller

#### Mappers

The user return by the user controller should not have the email, so create a new interface:

```ts
// response-user.interface.ts

export interface IResponseUser {
  id: number;
  name: string;
  username: string;
  createdAt: string;
  updatedAt: string;
}
```

And implement it:

```ts
// response-user.mapper.ts

import { IResponseUser } from '../interfaces/response-user.interface';
import { IUser } from '../interfaces/user.interface';

export class ResponseUserMapper implements IResponseUser {
  public id: number;
  public name: string;
  public username: string;
  public createdAt: string;
  public updatedAt: string;

  constructor(values: IResponseUser) {
    Object.assign(this, values);
  }

  public static map(user: IUser): ResponseUserMapper {
    return new ResponseUserMapper({
      id: user.id,
      name: user.name,
      username: user.username,
      createdAt: user.createdAt.toISOString(),
      updatedAt: user.updatedAt.toISOString(),
    });
  }
}
```

#### Endpoints

Start by adding the `cookiePath` and `cookieName` for the delete route:

```ts
import { Controller } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { UsersService } from './users.service';

@Controller('api/users')
export class UsersController {
  private cookiePath = '/api/auth';
  private cookieName: string;

  constructor(
    private readonly usersService: UsersService,
    private readonly configService: ConfigService,
  ) {
    this.cookieName = this.configService.get<string>('COOKIE_NAME');
  }
}
```

User controller is comprised mostly by user Read, Update and Delete operations.

_Get User_

Start by creating a dto for the params:

```ts
// get-user.params.ts

import { IsString, Length } from 'class-validator';

export abstract class GetUserParams {
  @IsString()
  @Length(1, 106)
  public idOrUsername: string;
}
```

And add it as the type of the `Param` decorator:

```ts
import {
  // ...
  Get,
  Param,
  // ...
} from '@nestjs/common';
// ...
import { GetUserParams } from './dtos/get-user.params';
// ...

@Controller('api/users')
export class UsersController {
  // ...

  @Public()
  @Get('/:idOrUsername')
  public async getUser(@Param() params: GetUserParams): Promise<IResponseUser> {
    const user = await this.usersService.findOneByIdOrUsername(idOrUsername);
    return ResponseUserMapper.map(user);
  }

  // ...
}
```

_Update Email_

Since it updtes the email we need to return `AuthResponseUserMapper`:

```ts
import {
  Body,
  // ...
  Patch,
} from '@nestjs/common';
// ...
import { ChangeEmailDto } from './dtos/change-email.dto';
// ...

@Controller('api/users')
export class UsersController {
  // ...

  @Patch('/email')
  public async updateEmail(
    @CurrentUser() id: number,
    @Body() dto: ChangeEmailDto,
  ): Promise<IAuthResponseUser> {
    const user = await this.usersService.updateEmail(id, dto);
    return AuthResponseUserMapper.map(user);
  }

  // ...
}
```

_Update User_

```ts
// ...
import { UpdateUserDto } from './dtos/update-user.dto';
// ...

@Controller('api/users')
export class UsersController {
  // ...

  @Patch()
  public async updateUser(
    @CurrentUser() id: number,
    @Body() dto: UpdateUserDto,
  ): Promise<IResponseUser> {
    const user = await this.usersService.update(id, dto);
    return ResponseUserMapper.map(user);
  }

  // ...
}
```

_Delete User_

```ts
import {
  // ...
  Delete,
  // ...,
  HttpStatus,
  // ...
} from '@nestjs/common';
import { PasswordDto } from './dtos/password.dto';
// ...

@Controller('api/users')
export class UsersController {
  // ...

  @Delete()
  @ApiNoContentResponse({
    description: 'The user is deleted.',
  })
  @ApiBadRequestResponse({
    description: 'Something is invalid on the request body, or wrong password.',
  })
  @ApiUnauthorizedResponse({
    description: 'The user is not logged in.',
  })
  public async deleteUser(
    @CurrentUser() id: number,
    @Body() dto: PasswordDto,
    @Res() res: Response,
  ): Promise<void> {
    await this.usersService.delete(id, dto);
    res
      .clearCookie(this.cookieName, { path: this.cookiePath })
      .status(HttpStatus.NO_CONTENT)
      .send();
  }

  // ...
}
```

## Main file

Finally we need to set-up our main file, start by installing cookie-parser and helmet:

```bash
$ yarn add cookie-parser helmet
$ yarn add -D @types/cookie-parser
```

And add them, as well as a validation pipe to the main file:

```ts
import { ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import cookieParser from 'cookie-parser';
import helmet from 'helmet';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService);
  app.use(cookieParser(configService.get<string>('COOKIE_SECRET')));
  app.use(helmet());
  app.enableCors({
    credentials: true,
    origin: `https://${configService.get<string>('domain')}`,
  });
  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
    }),
  );
  await app.listen(configService.get<number>('port'));
}

bootstrap();
```

## (Optional) Swagger

How will our front-end team know how to use our API? This is where documentation is important, to create documentation automatic we can use [NestJS Swagger](https://docs.nestjs.com/openapi/introduction), start by installing it:

```bash
$ yarn add @nestjs/swagger
```

And add descriptions to all DTOS, Mappers and endpoints, I will exemplify with the `users`, but is exactly the same process for the `auth`.

### DTOs

Add `ApiProperty` decorators all dtos:

**Username DTO**

```ts
import { ApiProperty } from '@nestjs/swagger';
import { IsString, Length, Matches } from 'class-validator';
import { SLUG_REGEX } from '../../common/consts/regex.const';

export abstract class UsernameDto {
  @ApiProperty({
    description: 'The username of the user',
    minLength: 3,
    maxLength: 106,
    example: 'my-username',
    type: String,
  })
  @IsString()
  @Length(3, 106)
  @Matches(SLUG_REGEX, {
    message: 'Username must be a valid slugs',
  })
  public username: string;
}
```

**Password DTO**

```ts
import { ApiProperty } from '@nestjs/swagger';
import { IsString, MinLength } from 'class-validator';

export abstract class PasswordDto {
  @ApiProperty({
    description: 'The password of the user',
    minLength: 1,
    type: String,
  })
  @IsString()
  @MinLength(1)
  public password: string;
}
```

**Change Email DTO**

```ts
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsString, Length } from 'class-validator';
import { PasswordDto } from './password.dto';

export abstract class ChangeEmailDto extends PasswordDto {
  @ApiProperty({
    description: 'The email of the user',
    example: 'someone@gmail.com',
    minLength: 5,
    maxLength: 255,
    type: String,
  })
  @IsString()
  @IsEmail()
  @Length(5, 255)
  public email: string;
}
```

**Get User Params***

```ts
import { ApiProperty } from '@nestjs/swagger';
import { IsString, Length } from 'class-validator';

export abstract class GetUserParams {
  @ApiProperty({
    description: 'The id or username of the user',
    type: String,
    example: "1 or 'username'",
  })
  @IsString()
  @Length(1, 106)
  public idOrUsername: string;
}
```

### Mappers

Add `ApiProperty` decorators the `ResponseUserMapper`:

```ts
import { ApiProperty } from '@nestjs/swagger';
import { IResponseUser } from '../interfaces/response-user.interface';
import { IUser } from '../interfaces/user.interface';

export class ResponseUserMapper implements IResponseUser {
  @ApiProperty({
    description: 'User id',
    example: 123,
    minimum: 1,
    type: Number,
  })
  public id: number;

  @ApiProperty({
    description: 'User name',
    example: 'John Doe',
    minLength: 3,
    maxLength: 100,
    type: String,
  })
  public name: string;

  @ApiProperty({
    description: 'User username',
    example: 'john.doe1',
    minLength: 3,
    maxLength: 106,
    type: String,
  })
  public username: string;

  @ApiProperty({
    description: 'User creation date',
    example: '2021-01-01T00:00:00.000Z',
    type: String,
  })
  public createdAt: string;

  @ApiProperty({
    description: 'User last update date',
    example: '2021-01-01T00:00:00.000Z',
    type: String,
  })
  public updatedAt: string;

  constructor(values: IResponseUser) {
    Object.assign(this, values);
  }

  public static map(user: IUser): ResponseUserMapper {
    return new ResponseUserMapper({
      id: user.id,
      name: user.name,
      username: user.username,
      createdAt: user.createdAt.toISOString(),
      updatedAt: user.updatedAt.toISOString(),
    });
  }
}
```

### Enpoints

NestJS swagger exports an api response type for each HTTP status code, add one for each response an endpoint can have, with a type of a Mapper for the 200's status codes:

```ts
// ...
import {
  ApiBadRequestResponse,
  ApiNoContentResponse,
  ApiNotFoundResponse,
  ApiOkResponse,
  ApiTags,
  ApiUnauthorizedResponse,
} from '@nestjs/swagger';
// ...

@ApiTags('Users')
@Controller('api/users')
export class UsersController {
  // ...

  @Public()
  @Get('/:idOrUsername')
  @ApiOkResponse({
    type: ResponseUserMapper,
    description: 'The user is found and returned.',
  })
  @ApiBadRequestResponse({
    description: 'Something is invalid on the request body',
  })
  @ApiNotFoundResponse({
    description: 'The user is not found.',
  })
  public async getUser(
    // ...
  ): Promise<IResponseUser> {
    // ...
  }

  @Patch('/email')
  @ApiOkResponse({
    type: AuthResponseUserMapper,
    description: 'The email is updated, and the user is returned.',
  })
  @ApiBadRequestResponse({
    description: 'Something is invalid on the request body, or wrong password.',
  })
  @ApiUnauthorizedResponse({
    description: 'The user is not logged in.',
  })
  public async updateEmail(
    // ...
  ): Promise<IAuthResponseUser> {
    // ...
  }

  @Patch()
  @ApiOkResponse({
    type: ResponseUserMapper,
    description: 'The username is updated.',
  })
  @ApiBadRequestResponse({
    description: 'Something is invalid on the request body.',
  })
  @ApiUnauthorizedResponse({
    description: 'The user is not logged in.',
  })
  public async updateUser(
    // ...
  ): Promise<IResponseUser> {
    // ...
  }

  @Delete()
  @ApiNoContentResponse({
    description: 'The user is deleted.',
  })
  @ApiBadRequestResponse({
    description: 'Something is invalid on the request body, or wrong password.',
  })
  @ApiUnauthorizedResponse({
    description: 'The user is not logged in.',
  })
  public async deleteUser(
    // ...
  ): Promise<void> {
    // ...
  }
}
```

### Main file

Finally, add the document builder to your main file:

```ts
// ...
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
// ...

async function bootstrap() {
  // ...

  const swaggerConfig = new DocumentBuilder()
    .setTitle('NestJS Authentication API')
    .setDescription('A OAuth2.0 authentication API made with NestJS')
    .setVersion('0.0.1')
    .addBearerAuth()
    .addTag('Authentication API')
    .build();
  const document = SwaggerModule.createDocument(app, swaggerConfig);
  SwaggerModule.setup('api/docs', app, document);

  // ...
}

bootstrap();
``` 

## Conclusion

With this you now have a full functioning Authentication Microsservice to use on your REST apps. The github for this tutorial can be found [here](https://github.com/tugascript/nestjs-oauth/tree/express-rest-api).

## About the Author

Hey there! I am Afonso Barracha, your go-to econometrician who found his way into the world of back-end development with a soft spot for GraphQL. If you enjoyed reading this article, why not show some love by [buying me a coffee](https://www.buymeacoffee.com/barracha)?

Lately, I have been diving deep into more advanced subjects. As a result, I have switched from sharing my thoughts every week to posting once or twice a month. This way, I can make sure to bring you the highest quality content possible.

Do not miss out on any of my latest articles – follow me here on [dev](https://dev.to/tugascript) and [LinkedIn](https://www.linkedin.com/in/afonso-barracha/) to stay updated. I would be thrilled to welcome you to our ever-growing community! See you around!