Session Verification - SuperTokens Docs

Session Verification

Verify user sessions when integrating SuperTokens with AWS Lambda.

The following page shows three ways to verify sessions in a Lambda integration. Choose the one that works best based on the particularities of your use case.

Using Session Verification

When building your own APIs, you may need to verify the session of the user before proceeding further. SuperTokens SDK exposes a verifySession function that can be utilized for this. In this guide, we will be creating a /user GET route that will return the current session information.

1. Add /user GET route in your API Gateway

Create a /user resource and then GET method in your API Gateway. Configure the lambda integration and CORS just like we did for the auth routes.

2. Create a file in your Lambda function to handle the /user route

Node.js

An example of this is here.

import supertokens from "supertokens-node";
import { getBackendConfig } from "./config.mjs";
import { verifySession } from "supertokens-node/recipe/session/framework/awsLambda";
import middy from "@middy/core";
import cors from "@middy/http-cors";

supertokens.init(getBackendConfig());

const lambdaHandler = async (event) => {
  return {
    body: JSON.stringify({
      sessionHandle: event.session?.getHandle(),
      userId: event.session?.getUserId(),
      accessTokenPayload: event.session?.getAccessTokenPayload(),
    }),
    statusCode: 200,
  };
};

export const handler = middy(verifySession(lambdaHandler))
  .use(
    cors({
      origin: getBackendConfig().appInfo.websiteDomain,
      credentials: true,
      headers: ["Content-Type", ...supertokens.getAllCORSHeaders()].join(", "),
      methods: "OPTIONS,POST,GET,PUT,DELETE",
    }),
  )
  .onError((request) => {
    throw request.error;
  });

Using Lambda Authorizers

You can use a Lambda authorizer with an API Gateway REST API to authorize requests to another integration, such as AppSync. The authorizer below requires a valid session and returns its user ID as principalId. API Gateway can map $context.authorizer.principalId to an integration header. Missing and invalid sessions are rejected; this guide does not claim support for optional sessions because AWS’s behavior for an empty principal is not established here.

1. Add configurations and dependencies

Refer to the frontend, lambda layer, and lambda setup.

2. Add code to the lambda function handler

Python

Use the code below as the handler for the lambda. Remember that whenever we want to use any functions from the supertokens-python lib, we have to call the init function at the top of that serverless function file. We can then use get_session() to get the session.

Node.js

Use the code below as the handler for the lambda. Remember that whenever we want to use any functions from the supertokens-node lib, we have to call the supertokens.init function at the top of that serverless function file. We can then use getSession() to get the session.

import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import { getBackendConfig } from "./config.mjs";

supertokens.init(getBackendConfig());

export const handler = async function (event) {
  try {
    const session = await Session.getSession(event, event);
    return generateAllow(session.getUserId(), event.methodArn);
  } catch (ex) {
    if (ex.type === "TRY_REFRESH_TOKEN" || ex.type === "UNAUTHORISED") {
      throw new Error("Unauthorized");
    }
    if (ex.type === "INVALID_CLAIMS") {
      return generateDeny("invalid-claims", event.methodArn, {
        body: JSON.stringify({
          message: "invalid claim",
          claimValidationErrors: ex.payload,
        }),
      });
    }
    throw ex;
  }
};

const generatePolicy = function (principalId, effect, resource, context = {}) {
  const policyDocument = {
    Version: "2012-10-17",
    Statement: [],
  };

const statementOne = {
    Action: "execute-api:Invoke",
    Effect: effect,
    Resource: resource,
  };

policyDocument.Statement[0] = statementOne;

const authResponse = {
    principalId: principalId,
    policyDocument: policyDocument,
    context,
  };

return authResponse;
};

const generateAllow = function (principalId, resource, context) {
  return generatePolicy(principalId, "Allow", resource, context);
};

const generateDeny = function (principalId, resource, context) {
  return generatePolicy(principalId, "Deny", resource, context);
};

3. Configure the authorizer

Create a request-based Lambda authorizer for the REST API and point it to the function above. AWS changes console labels; capture this configuration in IaC and verify that the deployed authorizer receives the headers and cookies required by your selected SuperTokens token-transfer method.

4. Configure API Gateway

Using JWT Authorizers

1. Add the aud claim in the JWT based on the authorizer configuration

Node.js

import Session from "supertokens-node/recipe/session";

export function getBackendConfig() {
  return {
    framework: "awsLambda",
    supertokens: {
      connectionURI: "<CORE_API_ENDPOINT>",
    },
    appInfo: {
      appName: "<YOUR_APP_NAME>",
      apiDomain: "<YOUR_API_DOMAIN>",
      websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
      apiBasePath: "/auth",
      websiteBasePath: "/auth",
      apiGatewayPath: "/dev",
    },
    recipeList: [
      Session.init({
        exposeAccessTokenToFrontendInCookieBasedAuth: true,
        override: {
          functions: function (originalImplementation) {
            return {
              ...originalImplementation,
              createNewSession: async function (input) {
                input.accessTokenPayload = {
                  ...input.accessTokenPayload,
                  aud: "jwtAuthorizers",
                };
                return originalImplementation.createNewSession(input);
              },
            };
          },
        },
      }),
    ],
    isInServerlessEnv: true,
  };
}

2. Configure your authorizer

3. Add the authorizer to your API

4. Send the access token as a bearer token

Exposing the access token does not automatically copy it to the JWT authorizer’s identity source in cookie-based auth. Set the header explicitly on requests to protected routes:

import Session from "supertokens-web-js/recipe/session";

async function fetchProtectedUser() {
  const accessToken = await Session.getAccessToken();

if (accessToken === undefined) {
    throw new Error("No session access token is available");
  }

return fetch("<YOUR_API_DOMAIN>/dev/user", {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  });
}

5. Check authorization claims in the JWT

Once the JWT authorizer successfully validates the JWT, the claims of the JWT will be available to your lambda functions via $event.requestContext.authorizer.jwt.claims. You should check for the right authorization access here.