How to pass jwt to prisma middleware in nestjs

Viewed 1328

I am using nestjs, graphql, & prisma. I am trying to figure out how to pass my jwt token for each database request to the prisma service iv created. Iv tried an object to the constructor but then wont compile saying I am missing a dependency injection for whatever I reference in the constructor paramter.

@Injectable()
export class PrismaService
  extends PrismaClient
  implements  OnModuleDestroy {
  constructor() {
    super();
    //TODO how do I pass my jwt token to this for each request?
    this.$use(async (params, next) => {
      if (params.action === 'create') {
        params.args.data['createdBy'] = 'jwt username goes here';
      }
      if (params.action === 'update') {
        params.args.data['updatedBy'] = 'jwt username goes here';
      }

      const result = await next(params);
      return result;
    });
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}
2 Answers

Are you using a nest middleware?

JWT is normally passed to a Controller, not a service.

Example:


@Injectable()
export class MyMiddleware implements NestMiddleware {
 private backend: any // This is your backend

 constructor() {
   this.backend = null // initialize your backend 
 }

 use(req: Request, res: Response, next: any) {
   const token = <string>req.headers.authorization


   if (token != null && token != '') {
     this.backend
       .auth()
       .verifyIdToken(<string>token.replace('Bearer ', ''))
       .then(async (decodedToken) => {
         const user = {
           email: decodedToken.email,
           uid: decodedToken.uid,
           tenantId: decodedToken.tenantId,
         }
         req['user'] = user
         next()
       })
       .catch((error) => {
         log.info('Token validation failed', error)
         this.accessDenied(req.url, res)
       })
   } else {
     log.info('No valid token provided', token)
     return this.accessDenied(req.url, res)
   }
 }

 private accessDenied(url: string, res: Response) {
   res.status(403).json({
     statusCode: 403,
     timestamp: new Date().toISOString(),
     path: url,
     message: 'Access Denied',
   })
 }
}

So every time I get an API call with a valid token, the token is added to the user[] in the request.

In my Controller Class I can then go ahead and use the data:


@Post()
  postHello(@Req() request: Request): string {
    return 'Hello ' + request['user']?.tenantId + '!'
  }

I just learned about an update in Nest.js which allows you to easily inject the header also in a Service. Maybe that is exactly what you need.

So in your service.ts:


import { Global, INestApplication, Inject, Injectable, OnModuleInit, Scope } from '@nestjs/common'
import { PrismaClient } from '@prisma/client'
import { REQUEST } from '@nestjs/core'

@Global()
@Injectable({ scope: Scope.REQUEST })
export class PrismaService extends PrismaClient implements OnModuleInit {
  constructor(@Inject(REQUEST) private readonly request: any) {
    super()
    console.log('request:', request?.user)
  }

  async onModuleInit() {
    // Multi Tenancy Middleware
    this.$use(async (params, next) => {
      // Check incoming query type
      console.log('params:', params)
      console.log('request:', this.request)
      return next(params)
    })

    await this.$connect()
  }

  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close()
    })
  }
}

As you can see in the log output, you have access to the entire request object.

Related