@moveaxlab/nestjs-security
v10.1.2
Published
A NestJS library to handle JWT authentication for web and mobile apps.
Downloads
356
Readme
NestJS Security
This package contains security utilities for NestJS projects, for both REST and GraphQL API gateways. It supports both express and fastify.
Installation
yarn add @moveaxlab/nestjs-security
Setup with cookies
For web applications you can rely on cookies.
Include the SecurityModule
inside your application:
import { SecurityModule } from '@moveaxlab/nestjs-security';
@Module({
imports: [
SecurityModule.forRoot({
type: "cookie",
cookieDomain: "localhost", // the domain of your app
cookieExpirationMilliseconds: 15 * 60 * 1000,
jwtSecret: "secret",
// the name of your cookies
accessTokenCookieName: "access_token",
opaqueTokenCookieName: "opaque_token",
refreshTokenCookieName: "refresh_token",
})
]
})
export class AppModule;
Remember to enable cookie support for your application.
When using cookies, you can replace the access token with an opaque token if your access token may be too big for HTTP headers.
To enable the opaque token, install ioredis
as a dependency,
and configure the redis
option.
The access token will be stored on the configured redis server,
and will be replaced in the cookies with a randomly generated token.
Setup with headers
For mobile and desktop applications you can rely on authentication headers.
import { SecurityModule } from '@moveaxlab/nestjs-security';
@Module({
imports: [
SecurityModule.forRoot({
type: "header",
jwtSecret: "secret",
})
]
})
export class AppModule;
Custom token conversion logic
All configurations accept a tokenConverter
option to implement
custom transformations on the parsed access token.
Authenticating users
You can authenticate users based on their role (or token type) or based on the permission.
The library assumes that all access tokens contain a tokenType
field or a permissions
array.
Authentication can be applied on the class level or on the method level.
@Authenticated
The library will check that the token type is equal with one of the roles declared in the decorator
import { Authenticated } from "@moveaxlab/nestjs-security";
@Authenticated("admin", "user")
class MyController {
async firstMethod() {
// accessible to both admins and users
}
@Authenticated("admin")
async secondMethod() {
// only accessible to admins
}
}
In order check that the user has a valid accessToken, but without any required permission or roles you can use the @Authenticated
decorator without any tokenType.
import { HasPermission } from "@moveaxlab/nestjs-security";
import { Authenticated } from "./authenticated.decorator";
@Authenticated()
class MyController {
async getMyProfile() {
// only accessibile to authenticated user
}
}
@HasPermission
The library will search for the required permission in the permissions
array.
import { HasPermission } from "@moveaxlab/nestjs-security";
@HasPermission("myResource.read")
class MyController {
async firstMethod() {
// accessible to token with permission myResource.read
}
@HasPermission("myResource.write")
async secondMethod() {
// only accessible to token with the permissions myResourse.write
}
}
Setting cookies
Use the CookieService
to set and unset the access token and refresh token.
When using express:
import { CookieService } from "@movexlab/nestjs-security";
import { Request, Response } from "express";
class Controller {
constructor(private readonly cookieService: CookieService) {}
async login(@Res({ passthrough: true }) res: Response) {
await this.cookieService.setCookies(res, accessToken, refreshToken);
}
async logout(@Req() req: Request, @Res({ passthrough: true }) res: Response) {
await this.cookieService.clearCookies(req, res);
}
}
When using fastify:
import { CookieService } from "@movexlab/nestjs-security";
import { FastifyRequest, FastifyReply } from "fastify";
class Controller {
constructor(private readonly cookieService: CookieService) {}
async login(@Res({ passthrough: true }) res: FastifyReply) {
await this.cookieService.setCookies(res, accessToken, refreshToken);
}
async logout(
@Req() req: FastifyRequest,
@Res({ passthrough: true }) res: FastifyReply,
) {
await this.cookieService.clearCookies(req, res);
}
}
Using GraphQL
If you are using GraphQL, the request and response must be retrieved from the GraphQL context.
For express, setup your GraphQL module like this:
import { GraphQLModule } from '@nestjs/graphql';
import { Request, Response } from 'express';
@Module({
imports: [
GraphQLModule.forRoot({
// ...
context: ({ req, res }: { req: Request, res: Response }) => ({ req, res }),
})
]
})
export class AppModule;
With fastify, the setup should look like this:
import { GraphQLModule } from '@nestjs/graphql';
import { FastifyRequest, FastifyReply } from 'fastify';
@Module({
imports: [
GraphQLModule.forRoot({
// ...
context: (req: FastifyRequest, res: FastifyReply) => ({ req, res }),
})
]
})
export class AppModule;
Inside your resolvers you can access the request and response objects
using the @Context("req")
and @Context("res")
decorators.
If you are using fastify, you cannot access the response using
@Context("res")
due to a bug in@nestjs/core
. Access it instead with@Context() { res }: { res: FastifyReply }
.
Getting the tokens inside a controller or resolver
You can access the access token and refresh token inside your controllers and resolvers using decorators.
import { Authenticated, AccessToken } from "@moveaxlab/nestjs-security";
@Authenticated("admin")
class MyController {
async myMethod(@AccessToken() token: string) {
// use the token here
}
}
The refresh token can be accessed via decorators when using cookies.
Include the RefreshCookieInterceptor
to retrieve it.
import {
Authenticated,
RefreshToken,
RefreshCookieInterceptor,
} from "@moveaxlab/nestjs-security";
@Authenticated("admin")
@UseInterceptors(RefreshCookieInterceptor)
class MyController {
async myMethod(@RefreshToken() token: string) {
// use the token here
}
}
You can access the parsed access token using the @User
decorator.
import { Authenticated, HasPermission, User } from "@moveaxlab/nestjs-security";
interface UserType {
tokenType: "admin" | "user";
uid: string;
permission: string[];
// other information contained in the token
}
@Authenticated("admin")
class MyController {
async myMethod(@User() token: UserType) {
// use the token here
}
}
@HasPermission("myPermission")
class MySecondController {
async mySecondMethod(@User() token: UserType) {
// use the token here
}
}
Using different secrets based on the issuer
The jwtSecret
options can accept an object mapping the iss
key
contained in the token with the secret or key used to sign the token.