I'm using Nestjs as backend, with both rest and gr...
# support-questions-legacy
n
I'm using Nestjs as backend, with both rest and graphql implementation. How should I set the token in graphql playground?
I followed step by step all the guide to make nestjs work with supertokens and it does. I use the pre-built ui in an angular test app and everythings work. the auth-guard in nestjs seems to work properly if applied to controllers, while it resolves in error if the request is sent via graphql playground, no matters is headers are set or not. this is the error:
Copy code
{
    "errors": [
        {
            "message": "Cannot read properties of undefined (reading 'method')",
            "locations": [
                {
                    "line": 2,
                    "column": 3
                }
            ],
            "path": [
                "tests"
            ],
            "extensions": {
                "code": "INTERNAL_SERVER_ERROR",
                "stacktrace": [
                    "TypeError: Cannot read properties of undefined (reading 'method')",
                    "    at ExpressRequest.getMethod (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/framework/express/framework.js:50:61)",
                    "    at Object.verifySession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/api/implementation.js:23:66)",
                    "    at SessionRecipe.verifySession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/recipe.js:154:39)",
                    "    at /Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/framework/express.js:34:47",
                    "    at AuthGuard.canActivate (/Users/nicola/Devel/temp/test-nest-app/src/auth/auth.guard.ts:17:44)",
                    "    at GuardsConsumer.tryActivate
...
the error is fired here
Copy code
await verifySession(this.verifyOptions)(
      ctx.getRequest(),
      resp,
      (res) => {
        err = res;
      },
    );
in the suggested auth guard code
r
Hey @nik2208.2208
What’s the ctx object here? Where are you calling the verifySession function?
n
i'm using the authguard provided in the docs
Copy code
typescript
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Error as STError } from "supertokens-node";

import { verifySession } from 'supertokens-node/recipe/session/framework/express';
import { VerifySessionOptions } from 'supertokens-node/recipe/session';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly verifyOptions?: VerifySessionOptions) { }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const ctx = context.switchToHttp();

    let err = undefined;
    const resp = ctx.getResponse();
    // You can create an optional version of this by passing {sessionRequired: false} to verifySession
    await verifySession(this.verifyOptions)(
      ctx.getRequest(),
      resp,
      (res) => {
        err = res;
      },
    );

    if (resp.headersSent) {
      throw new STError({
        message: "RESPONSE_SENT",
        type: "RESPONSE_SENT",
      });
    }

    if (err) {
      throw err;
    }

    return true;
  }
}
r
So the authguard doesn’t work specifically for the graphql endpoint, but it works for normal http endpoints?
n
yes it seems not to work, examining the requests for http and graphql they seem to differ.
r
Right
We have a separate guide for graphql
n
i read that
r
Maybe this auth guard doesn’t work for graphql cause the request object isn’t an express request object. Not sure.
n
I'm on it now, but the graphql section doesnt specifically apply to nestjs which has a structure that differs from pure express
r
How does the request object differ with graphql vs without graphql?
n
what I'm also not sure about, once I simply try something like this
Copy code
typescript
async findAll(@Context('req') req: Request, @Context('res') res: Response) {
    try {
      const session = await Session.getSession(req, res);
      const userId = session.getUserId();
      console.log(userId);
    } catch (err) {
      if (Session.Error.isErrorFromSuperTokens(err)) {
        throw new GraphQLError('Session related error', {
          extensions: {
            code: 'UNAUTHENTICATED',
            http: { status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401 },
          },
        });
      }
    }
`
how can i be sure to set correctly the cookie on the graphql playground which doesnt face a login process?
r
You can set the cookie name as sAccessToken, or also just use authorization bearer header
n
i tried both but they fail
r
With what error?
n
this is the error i get using cookies, trying to get session via session.getSession as per the code above
Copy code
bash
TypeError: Cannot read properties of undefined (reading 'wrapperUsed')
    at Object.getSessionFromRequest (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/sessionRequestFunctions.js:28:18)
    at Function.getSession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/index.js:151:42)
    at TestsResolver.findAll (/Users/nicola/Devel/temp/test-nest-app/src/tests/tests.resolver.ts:26:37)
    at /Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-context-creator.js:67:33
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async target (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-context-creator.js:74:28)
    at async Object.tests (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-proxy.js:9:24) {stack: 'TypeError: Cannot read properties of undefine…/@nestjs/core/helpers/external-proxy.js:9:24)', message: 'Cannot read properties of undefined (reading 'wrapperUsed')'}
`
r
This means the req object being passed to getSession is undefined.
You need to get the req object somehow
n
this is the error from the authguard as per the code provided by the docs
Copy code
bash
TypeError: Cannot read properties of undefined (reading 'method')
    at ExpressRequest.getMethod (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/framework/express/framework.js:50:61)
    at Object.verifySession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/api/implementation.js:23:66)
    at SessionRecipe.verifySession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/recipe.js:154:39)
    at /Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/framework/express.js:34:47
    at AuthGuard.canActivate (/Users/nicola/Devel/temp/test-nest-app/src/auth/auth.guard.ts:17:44)
    at GuardsConsumer.tryActivate (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/guards/guards-consumer.js:15:34)
    at canActivateFn (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-context-creator.js:155:59)
    at target (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-context-creator.js:73:37)
    at Object.tests (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-proxy.js:9:30)
    at /Users/nicola/Devel/temp/test-nest-app/node_modules/@apollo/server/src/utils/schemaInstrumentation.ts:82:22 {stack: 'TypeError: Cannot read properties of undefine…rver/src/utils/schemaInstrumentation.ts:82:22', message: 'Cannot read properties of undefined (reading 'method')'}
`
r
Huh? If you are using getSession, you don’t need to use auth guard
Im not quite sure what your setup is at the moment. You could just use any JWT verification lib instead of using our auth guard / get session for graphql endpoints. That would work too.
n
actually is not undefined
I made both the tries to answer u abt the difference of the request objects and the errors
the tries were mutual exclusive (meaning that in case of guard no session would have been verified and viceversa
r
what about the res object? thats undefined it seems
n
yes response is undefined
r
right. So thats the reason why calling getSession fails
n
how the response should be defined? i get the result of the api request anyway, even if res is undefined
r
doesn't nest provide you with a response object?
n
a get a response with data
*I get a response
r
if not, then you can switch to using regular JWT library auth instead of using getSession. It's simpler.
n
not sure abt what u mean
n
r
it does. But im not sure how it would work with nestjs since nestjs doesn;'t give a response object upfront (unlike express)
there are ways to give a mocked response and get it to work, but we don't have docs for it yet. So it's just easier to use normal JWT verification using any jwt verification lib
n
ok, I managed to get res vaoirised:
Copy code
bash
Error: Session does not exist. Are you sending the session tokens in the request with the appropriate token transfer method?
    at Object.getSession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/recipeImplementation.js:148:23)
    at Object.getSessionFromRequest (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/sessionRequestFunctions.js:123:47)
    at Function.getSession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/index.js:151:42)
    at TestsResolver.findAll (/Users/nicola/Devel/temp/test-nest-app/src/tests/tests.resolver.ts:26:37)
    at /Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-context-creator.js:67:33
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async target (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-context-creator.js:74:28)
    at async Object.tests (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-proxy.js:9:24) {type: 'UNAUTHORISED', payload: {…}, errMagic: 'ndskajfasndlfkj435234krjdsa', fromRecipe: 'session', stack: 'Error: Session does not exist. Are you sendin…/@nestjs/core/helpers/external-proxy.js:9:24)', …}
`
but, as i suspected, as the call comes from the graphql playground and not from the app (which faced a login process) the session does not exists
r
yup. You can add the access token as the header in the playground.
as authorization bearer token
or as sAccessToken cookie
n
now is set as sAccessToken cookie and it is not working
r
cookie?
whats the error now? Can you enable backend debug logs and show the output when you call this function?
n
r
This error is not coming from supertokens
So I’m not sure
Are you getting a session object back from getSession?
n
this is the error
Copy code
bash
Error: Failed to verify access token
    at Object.getInfoFromAccessToken (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/accessToken.js:194:15)
    at async Object.getSession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/sessionFunctions.js:92:27)
    at async Object.getSession (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/recipeImplementation.js:179:30)
    at async Object.getSessionFromRequest (/Users/nicola/Devel/temp/test-nest-app/node_modules/supertokens-node/lib/build/recipe/session/sessionRequestFunctions.js:123:21)
    at async TestsResolver.findAll (/Users/nicola/Devel/temp/test-nest-app/src/tests/tests.resolver.ts:26:23)
    at async target (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-context-creator.js:74:28)
    at async Object.tests (/Users/nicola/Devel/temp/test-nest-app/node_modules/@nestjs/core/helpers/external-proxy.js:9:24) {type: 'TRY_REFRESH_TOKEN', payload: undefined, errMagic: 'ndskajfasndlfkj435234krjdsa', fromRecipe: 'session', stack: 'Error: Failed to verify access token
    at O…/@nestjs/core/helpers/external-proxy.js:9:24)', …}
`
r
Have you made a mistake in copying the access token?
n
it was simply wrapped in this code
Copy code
catch (err) {
      if (Session.Error.isErrorFromSuperTokens(err)) {
        throw new GraphQLError('Session related error', {
          extensions: {
            code: 'UNAUTHENTICATED',
            http: { status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401 },
          },
        });
      }
    }
`
r
The access token shown on the browser storage is url encoded
Use postman to get a new access token and then use that in your request
n
since the cookie (for some reason) was stored in the browser, that had precedence to the bearer and gave error. after deleting the cookie i'm able to complete the request without errors
I ended up modifying both authguard and session decorator to switch depending on context type auth.guard.ts
Copy code
typescript
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { Error as STError } from 'supertokens-node';
import { verifySession } from 'supertokens-node/recipe/session/framework/express';
import { VerifySessionOptions } from 'supertokens-node/recipe/session';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly verifyOptions?: VerifySessionOptions) { }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const ctx = context.switchToHttp();

    if (ctx['contextType'] === 'http') {
      // Handle HTTP context
      const resp = ctx.getResponse();
      let err = undefined;

      await verifySession(this.verifyOptions)(
        ctx.getRequest(),
        resp,
        (res) => {
          err = res;
        },
      );

      if (resp.headersSent) {
        throw new STError({
          message: 'RESPONSE_SENT',
          type: 'RESPONSE_SENT',
        });
      }

      if (err) {
        throw err;
      }

      return true;
    } else if (ctx['contextType'] === 'graphql') {
      // Handle GraphQL context
      const gqlContext = GqlExecutionContext.create(context);
      const { req, res } = gqlContext.getContext();

      let err = undefined;

      await verifySession(this.verifyOptions)(
        req,
        res,
        (response) => {
          err = response;
        },
      );

      if (res.headersSent) {
        throw new STError({
          message: 'RESPONSE_SENT',
          type: 'RESPONSE_SENT',
        });
      }

      if (err) {
        throw err;
      }

      return true;
    }

    // Handle other types of contexts if needed
    return false; // For other context types, guard is not applicable
  }
}
session.decorator.ts
Copy code
typescript
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

export const Session = createParamDecorator(
    (data: unknown, ctx: ExecutionContext) => {
        let request = null;
        if (ctx['contextType'] === 'http') {
            request = ctx.switchToHttp().getRequest();
        } else if (ctx['contextType'] === 'graphql') {
            request = GqlExecutionContext.create(ctx).getContext().req;
        }
        return request.session;
    },
);
this way authguard and session decorator can be used in resolver as well
r
That’s cool!
55 Views