1.6.3 • Published 3 years ago
@tomasztrebacz/nest-auth-graphql-redis v1.6.3
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; }
- GqlAuthGuard & Current User
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 );
- create access token (due to security reasons we reduce payload to
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.