1.6.3 • Published 3 years ago

@tomasztrebacz/nest-auth-graphql-redis v1.6.3

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

Description

nest-auth-graphql-redis is inseparable part of fox.CMS app. This package handle authentication & authorization using key-value database and add needful layer between app and redis data store. This repository has also the CI/CD pipeline which test & deploy code to NPM and private Github Package Registry.

Getting started

  • Install package:
npm install @tomasztrebacz/nest-auth-graphql-redis
  • Create .env file with necessary variables:
ACCESS_JWT_SECRET=
ACCESS_JWT_EXP=
REDIS_HOST=
REDIS_PORT=
REDIS_DB=
REDIS_PASSWORD=
  • Create following files in ./config directory:
// redis.config.ts

export default registerAs("redis", () => ({
  host: process.env.REDIS_HOST,
  port: parseInt(process.env.REDIS_PORT),
  db: parseInt(process.env.REDIS_DB),
  password: process.env.REDIS_PASSWORD,
}));

// jwt.config.ts

export default registerAs("jwt", () => ({
  secret: process.env.ACCESS_JWT_SECRET,
  signOptions: { expiresIn: process.env.ACCESS_JWT_EXP },
}));
  • Import them & package in main module
imports: [
  ConfigModule.forRoot({
    isGlobal: true,
    load: [redisConfig, jwtConfig],
  }),
  AuthGraphqlRedisModule,
];
  • Inject needed parts, for example:
import { Injectable } from "@nestjs/common";
import {
  AuthGqlRedisService,
  RedisHandlerService,
} from "@tomasztrebacz/nest-auth-graphql-redis";

@Injectable()
export class xyzService {
  constructor(
    private authGqlRedisService: AuthGqlRedisService,
    private redisHandlerService: RedisHandlerService
  ) {}
}
  • HTTP header
{
  "Authorization": "Bearer <your_token>"
}

Usage

  • Guards & Decorators

    • GqlAuthGuard & Current User
      check if the user is logged & exist in db and get his/her data
    @Query('currentUser')
    @UseGuards(GqlAuthGuard)
    async currentUser(@CurrentUser() user: User) {
      return await this.usersService.findOneById(user.id);
    }
    • RolesGuard & Roles
      check if the user match given role in decorator
    @Query('users')
    @Roles(userRole.ADMIN, userRole.ROOT)
    @UseGuards(GqlAuthGuard, RolesGuard)
    findAll() {
      return this.usersService.findAll();
    }
    • Confirmed
      check if the user is confirmed
    @Query('users')
    @UseGuards(GqlAuthGuard, ConfirmedGuard)
    findAll() {
      return this.usersService.findAll();
    }
    • Auth Decorator - the most readable approach to use this package
      all of the above mentioned guards merged into one
    @Query('users')
    @Auth() // if you want to check also roles, use @Auth('role1', 'role2')
    findAll() {
      return this.usersService.findAll();
    }
    • AccessLevel guard with decorator
      this decorator is useful in scenarios when user with permissions want to change properties of other similar account, e.g, user with admin role provide mutation to API wherein they want to change properties of root user or account with the same role
    @Mutation()
    @AccessLevel()
    async changeRole(
      @Args('changeRoleInput') changeRoleData: ChangeRoleDto,
    ): Promise<boolean> {
      await this.authService.changeRole(changeRoleData);
    
      return true;
    }
  • Enum

    • An important fact is that app is based on the enum describing user roles:
    export enum userRole {
      ROOT = "root",
      ADMIN = "admin",
      USER = "user",
    }
  • Tokens

    • create access token (due to security reasons we reduce payload to user id)
    const accessToken = await this.authGqlRedisService.createDefaultJWT(user.id);
    • create custom tokens with necessary secret, exp date and informations provided in payload (in this example we will create refresh token)
    const payload = {
      id: user.id,
      count: user.count,
    };
    
    const refreshToken = await this.authGqlRedisService.createJWT(
      payload,
      process.env.REFRESH_JWT_SECRET,
      process.env.REFRESH_JWT_EXP
    );
    • verify token
    const { id } = await this.authGqlRedisService.verifyToken(
      exampleToken,
      process.env.EXAMPLE_JWT_SECRET
    );
  • Redis

    • setUser
      comprehensive function for saving new user in database and create/update fields for purposes like reset password, refresh token etc.

      const userProperties = new Map<string, string>([
        ["role", "batman"],
        ["count", "0"],
        ["confirmed", "false"],
        ["confirmtoken", "eyJhbGciOiJ[...]"],
      ]);
      
      await this.redisHandler.setUser(id, userProperties);

      note: all values in redis are stored as strings

    • getFields

      const keys: string[] = ["role", "count"];
      const user = await this.redisHandler.getFields(id, keys);
    • userExists

      await this.redisHandler.userExists(decodedJWT.id);
    • getValue

      const actualRole = await this.redisHandler.getValue(id, "role");
    • deleteField

      await this.redisHandler.deleteField(id, "confirmtoken");
    • deleteUser

      await this.redisHandler.deleteUser(id);

Real life scenario

  • Visit my profile => fox.CMS repository and see how to seize the opportunities of this package by adding functionalities like reset password by email/phone, confirmation link, refresh token etc.

Resources

  • Publishing NestJS Packages with npm | Author: John Biundo

Note!

The example featured in the above mentioned post is a bit outdated, but generally this article is a good point to start learning how to create own package.

License