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
- Require the authorizer on each protected method.
- In the integration request, overwrite
x-user-idfromcontext.authorizer.principalId. Never forward a client-supplied identity header. - If the browser must read gateway-generated
401or403responses, configure them with the exact trustedAccess-Control-Allow-OriginandAccess-Control-Allow-Credentials: true. Do not combine credentials with a wildcard origin. - Deploy and test the API. The IaC fixture must prove that a spoofed identity header cannot reach the integration.
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
- Go to the “Authorizers” tab in the API Gateway configuration and select the “Manage authorizers” tab
- Click “Create”, in the creation screen select “JWT” as the “Authorizer type”
- Enter a name for your authorizer (You can enter any name for this field)
- Use
$request.header.Authorizationfor the “Identity source”. This means that API requests will contain the JWT as a Bearer token under the request header “Authorization”. - Use the exact normalized issuer emitted by SuperTokens for this configuration:
<YOUR_API_DOMAIN>/dev/auth. This isapiDomain + apiGatewayPath + apiBasePath, with one slash at each boundary. - Set a value for the “Audience” field, this will be the value you expect the JWT to have under the
audclaim. In the backend config above the value is set to"jwtAuthorizers"
3. Add the authorizer to your API
- In the “Authorization” section select the “Attach authorizers to routes” tab
- Click on the route you want to add the authorizer to and select the authorizer you created from the dropdown
- Click “Attach authorizer”
- Deploy your changes and test 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.