Using Lambda Authorizers
CAUTION
This guide only applies to scenarios which involve SuperTokens Session Access Tokens.
If you are implementing either, Unified Login or Microservice Authentication, features that make use of OAuth2 Access Tokens, please check the separate page that shows you how to verify those types of tokens.
You can use a lambda as an Authorizer in API Gateways. This will enable you to use SuperTokens in a lambda to authorize requests to other integrations (e.g., AppSync). An Authorizer pointed to this lambda will add context.authorizer.principalId
that you can map to a header. For example, you can map this to an "x-user-id" header which will be set to the id of the logged-in user. If there is no valid session for the request, this header won't exist.
1. Add configurations and dependencies
Refer to the frontend, lambda layer, and [lambda setup](Refer to the frontend, lambda layer, and lambda setup guides for setting up the frontend, lambda layer, and lambda function.
2. Add code to the lambda function handler
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 { SessionEvent } from "supertokens-node/framework/awsLambda";
import Session from "supertokens-node/recipe/session";
import { getBackendConfig } from "./config.mjs";
supertokens.init(getBackendConfig());
type AuthorizerEvent = SessionEvent & APIGatewayAuthorizerEvent;
export const handler = async function (event: AuthorizerEvent) {
try {
const session = await Session.getSession(event, event, { sessionRequired: false });
if (session) {
return generateAllow(session.getUserId(), event.methodArn, {
setCookie: event.supertokens.response.cookies.join(', '),
});
} else {
return generateAllow("", event.methodArn, {
setCookie: event.supertokens.response.cookies.join(', '),
});
}
} catch (ex: any) {
if (ex.type === "TRY_REFRESH_TOKEN" || ex.type === "UNAUTHORISED") {
throw new Error("Unauthorized");
}
if (ex.type === "INVALID_CLAIMS") {
return generateDeny("", event.methodArn, {
body: JSON.stringify({
message: "invalid claim",
claimValidationErrors: ex.payload,
}),
setCookie: event.supertokens.response.cookies.join(", "),
});
}
throw ex;
}
}
const generatePolicy = function (principalId: string, effect: StatementEffect, resource: string, context?: any) {
const policyDocument: PolicyDocument = {
Version: '2012-10-17',
Statement: [],
};
const statementOne: Statement = {
Action: 'execute-api:Invoke',
Effect: effect,
Resource: resource,
};
policyDocument.Statement[0] = statementOne;
const authResponse: AuthResponse = {
principalId: principalId,
policyDocument: policyDocument,
context,
};
return authResponse;
}
const generateAllow = function (principalId: string, resource: string, context?: any) {
return generatePolicy(principalId, 'Allow', resource, context);
};
const generateDeny = function (principalId: string, resource: string, context?: any) {
return generatePolicy(principalId, 'Deny', resource, context);
};
3. Configure the Authorizer
- Go to the "Authorizers" tab in the API Gateway configuration
- Click "Create new Authorizer" and add the new Authorizer
- Fill the name field
- Set "Lambda function" to the one created above
- Set "Lambda Event Payload" to Request
- Delete the empty "Identity Source"
- Click "Create"
4. Configure API Gateway
- In your API Gateway, create the resources and methods you require, enabling CORS if necessary (see setup api gateway for details)
- Select each Method you want to enable the Authorizer on and configure it to use the new Authorizer
-
Click on "Method Request"
-
Edit the "Authorization" field in Settings and set it to the one we just created.
-
Go back to the method configuration and click on "Integration Request"
- Set up the integration you require (see AppSync for an example)
- Add a header mapping to make use of the context set in the lambda.
- Open "HTTP Headers"
- Add all headers required (e.g., "x-user-id" mapped to "context.authorizer.principalId")
- Repeat for any values from the context you want to add as a Header
-
Go back to the method configuration and click on "Method Response"
- Open the dropdown next to the 200 status code
- Add the "Set-Cookie" header
- Add any other headers that should be present on the response.
-
Go back to the method configuration and click on "Integration Response"
- Open the dropdown
- Open "Header Mappings"
- Add "Set-Cookie" mapped to "context.authorizer.setCookie"
-
- In the API Gateway left menu, select "Gateway Responses"
- Select "Access Denied"
- Click "Edit"
- Add response headers:
- Add
Access-Control-Allow-Origin
with value'<YOUR_WEBSITE_DOMAIN>'
- Add
Access-Control-Allow-Credentials
with value'true'
. Don't miss out on those quotes else it won't get configured correctly. - Add "Set-Cookie" with value
context.authorizer.setCookie
no quotes
- Add
- Under response templates:
- Select
application/json
: - Set "Response template body" to
$context.authorizer.body
- Select
- Click "Save"
- Select "Unauthorized"
- Add response headers:
- Add
Access-Control-Allow-Origin
with value'<YOUR_WEBSITE_DOMAIN>'
- Add
Access-Control-Allow-Credentials
with value'true'
. Don't miss out on those quotes else it won't get configured correctly.
- Add
- Click "Save"
- Add response headers:
- Select "Access Denied"
- Deploy your API and test it