Supertokens Session `undefined`
# support-questions-legacy
s

Supertokens Session is undefined

Setup

We're using supertokens as our authentication layer, and the core is a payed one hosted with supertokens. Our stack is entirely based on javascript, we're using NextJS on the FE and NestJS on the BE. I have a controller defined as follows:
Copy code
import { Controller, Get, Query, Session, UseGuards } from '@nestjs/common';
import { SessionContainer } from 'supertokens-node/recipe/session';

import { BasicAuthGuard } from '../auth/guard/base-auth.guard';
import { GetSubscriptionDetailsRequestDto } from './dto/get-subscription-details.dto';
import { PaymentsService } from './payments.service';

function getUserEmailFromSession(session: SessionContainer) {
  const { email } = session.getAccessTokenPayload();
  return email;
}

@UseGuards(BasicAuthGuard)
@Controller('payments')
export class PaymentsController {
  constructor(private readonly paymentsService: PaymentsService) {}


  @Get('details')
  public async getSubscriptionDetails(
    @Session() session: SessionContainer,
    @Query() input: GetSubscriptionDetailsRequestDto,
  ) {
    const subscriptionDetails = await this.getSubscriptionDetailsFromEmail(
      getUserEmailFromSession(session),
    );
    return subscriptionDetails;
  }
}
Here's the code for the auth guard
Copy code
import type { CanActivate, ExecutionContext } from '@nestjs/common';
import { Error as STError } from 'supertokens-node';
import { verifySession } from 'supertokens-node/recipe/session/framework/express';

export class BasicAuthGuard implements CanActivate {
  constructor() {}

  public 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({ checkDatabase: true, sessionRequired: true })(
      ctx.getRequest(),
      resp,
      (res) => {
        err = res;
      },
    );

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

    if (err) {
      throw err;
    }

    return true;
  }
}
This controller runs fine 95% of the time. But we're seeing a weird log on our servers that shouldn't be there.
Copy code
ALERT!!!! INTERNAL SERVER ERROR!!!
            STATUS: 500
            METHOD: GET
            PATH: /payments/details?checkoutCancelPath=%2Fbilling&checkoutSuccessPath=%2Fbilling&portalReturnPath=%2Fbilling
            REQUEST-ID: 492f101b-92e4-4762-8a82-4f131c995c3f
            MESSAGE: Cannot read properties of undefined (reading 'getAccessTokenPayload')
            ERROR: 'Unhandled Rejection'
            STACK: TypeError: Cannot read properties of undefined (reading 'getAccessTokenPayload')
    at PaymentsService.getUserEmailFromSession (/app/dist/modules/payments/payments.service.js:187:35)
    at PaymentsController.getSubscriptionDetails (/app/dist/modules/payments/payments.controller.js:27:37)
    at /app/node_modules/@nestjs/core/router/router-execution-context.js:38:29
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /app/node_modules/@nestjs/core/router/router-execution-context.js:46:28
    at async /app/node_modules/@nestjs/core/router/router-proxy.js:9:17
And I have no clue as to what is happening on the FE that's causing this. We've been unable to identify which user is having this issue. The authentication method is cookies, the access token is sent in the 'cookie' header. I would really appreciate if anyone can give insights into why we're getting a
session
that's
undefined
. In my limited knowledge, if the cookie header is not present the BasicAuthGuard should already throw a 401, and I've tested this manually and it is indeed the case. What escapes me is how can a request pass through the auth guard successfully and end up having a session that is
undefined
Thanks allot!
55 Views