NestJS | Passport: TypeError: Cannot read properties of undefined (reading 'logIn')

Viewed 2461

Situation:

  • Developing api in nest & grapqhql
  • Worked on one laptop, everything was working well
  • Then cloned my repo on other laptops, installed dependencies, created a new local database.
  • App is being built with no errors
  • When following localhost:4000 in browser to open graphql playground I'm receiving 500 error end next message:
    ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'logIn')
    TypeError: Cannot read properties of undefined (reading 'logIn')
        at authenticate (/home/gleb/Projects/artwine-api/node_modules/passport/lib/middleware/authenticate.js:96:21)
        at /home/gleb/Projects/artwine-api/node_modules/@nestjs/passport/dist/auth.guard.js:91:3
        at new Promise (<anonymous>)
        at /home/gleb/Projects/artwine-api/node_modules/@nestjs/passport/dist/auth.guard.js:83:83
        at JWTAccessAuthGuard.<anonymous> (/home/gleb/Projects/artwine-api/node_modules/@nestjs/passport/dist/auth.guard.js:49:36)
        at Generator.next (<anonymous>)
        at fulfilled (/home/gleb/Projects/artwine-api/node_modules/@nestjs/passport/dist/auth.guard.js:17:58)
        at processTicksAndRejections (node:internal/process/task_queues:96:5)
    

Code of a passport lib function where the error is caught:

return function authenticate(req, res, next) {
    req.login =
    req.logIn = req.logIn || IncomingMessageExt.logIn;
    req.logout =
    req.logOut = req.logOut || IncomingMessageExt.logOut;
    req.isAuthenticated = req.isAuthenticated || IncomingMessageExt.isAuthenticated;
    req.isUnauthenticated = req.isUnauthenticated || IncomingMessageExt.isUnauthenticated;
    
    req._sessionManager = passport._sm;
..............

Link to the repo: https://github.com/Gleb-Gaiduk/artwine-api Any ideas on what could go wrong after cloning the working repository?

3 Answers

You need to transform the ExecutionContext from Graphql to one Nestjs/Passport can read: https://docs.nestjs.com/graphql/other-features#execution-context

import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class MySuperGuard extends AuthGuard('jwt') {
  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }
}

try adding this into "subscriptions" section of GraphQL module initialization:

'subscriptions-transport-ws': {
    onConnect: (headersRaw: Record<string, unknown>) => {
        // Lowercase each header key
        const headers = Object.keys(headersRaw).reduce((dest, key) => {
            dest[key.toLowerCase()] = headersRaw[key];
            return dest;
        }, {});
        return {
            req: {
                headers: headers,
            },
        };
    },
},

I have no idea why it is not documented, but it worked for me.

You should look your Guard and strategy you are using and should handle errors from there for example:

@Injectable()
export class PassportLocalGuard extends AuthGuard('local') {
protected readonly logger = new Logger(PassportLocalGuard.name);

canActivate(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();

return super.canActivate(new ExecutionContextHost([req]));
   }

handleRequest(err: any, user: any) {
  if (err) {
   this.logger.error(`Auth Error! ${err.message}`);
   throw err;
   }

  if (!user) {
   this.logger.error('Auth Error! User not found');
   throw new AuthenticationError('Auth Error! User not found');
   }

  return user;
 }
}
Related