To log in a user

Required implementation to log in a user

The concept of logging in a user is simple.

The user provides a username and a password, and the server verifies that the username and password are correct. If they are, the server returns a token that the user can use to access the API.

However, the implementation is a bit more complex. The server needs to store the user's password in a secure way, and it needs to verify that the password is correct. It also needs to generate a token that the user can use to access the API.

How to go about implementing this feature? Let's start by creating modules.

Modularization helps us organize the code by separating concerns. For example, the authentication module can be responsible for logging in a user, and the authorization module can be responsible for verifying that the user has access to a specific resource. This approach is very useful when the application grows and the codebase becomes more complex.

Here is a list of tasks that we need to complete to implement the login feature:

ENVIRONMENT SETUP

  1. set up a project repo

  2. install dependencies

  3. configure project settings (formatting, types, script commands)

  4. add a config module

  5. add a logger

  6. add a database module

  7. add a Dockerfile and a docker-compose file

IMPLEMENTATION

  1. add an authn module and implement a register endpoint

    • add a method to create a new user
    • add a method to send an email with a confirmation link
    • add a user schema
    • add a create user dto
    • add error handler
  2. add a verify confirmation code endpoint

    • handle exceptions (invalid link - invalid code, expired, already confirmed)
  3. add a login endpoint

    • add a method to find a user by email
    • add a method to compare passwords
    • add a method to generate a token
    • add a method to save a token

So far, the file structure looks like this:

❯ tree src/ -L 3
src/
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── authn
│   ├── authn.module.ts
│   ├── controllers
│   │   └── public.controller.v1.ts
│   ├── domain
│   │   ├── authn.service.ts
│   │   └── dto
│   └── services
│       ├── authn.service.ts
│       └── authn.service.v1.ts
├── config
│   ├── config.d.ts
│   └── config.validator.ts
├── database
│   ├── entity.repository.ts
│   └── mongoose.config.service.ts
├── email
│   ├── domain
│   │   └── payload.interface.ts
│   ├── email.module.ts
│   ├── email.service.ts
│   └── templates
├── filters
│   └── fallback-exception-filter.ts
├── interceptors
│   └── serialize.interceptor.ts
├── main.ts
├── role
│   ├── domain
│   │   └── role.service.ts
│   ├── entities
│   │   └── role.entity.ts
│   ├── role.module.ts
│   ├── role.repository.ts
│   └── services
│       └── role.service.v1.ts
├── token
│   ├── entities
│   │   └── token.entity.ts
│   ├── token.module.ts
│   └── token.repository.ts
├── user
│   ├── domain
│   │   └── user.service.ts
│   ├── entities
│   │   └── user.entity.ts
│   ├── services
│   │   └── user.service.v1.ts
│   ├── user.module.ts
│   └── user.repository.ts
├── utils
│   ├── api-error-codes.ts
│   ├── api-error-response.ts
│   ├── api-exception.ts
│   ├── index.ts
│   ├── path.ts
│   └── response.ts
└── winston
    ├── winston.constants.ts
    ├── winston.interface.ts
    ├── winston.module.ts
    └── winston.providers.ts

24 directories, 42 files

Whew. Enabling a simple functionality like logging in a user requires a lot of code. Let's take a short break here. In the next post, we will implement the Passport module with jwt strategy. We will use the passport-jwt module to implement the authentication strategy. The passport-jwt module is a Passport strategy for authenticating with a JSON Web Token (JWT). This module lets us authenticate endpoints using a JWT. It is intended to be used to secure RESTful endpoints without sessions.