Skip to main content

Implement a custom invite flow

Implement an allow list for third-party sign-ups using the SuperTokens user metadata recipe.

Overview

This guide shows you how to disable public sign ups to allow only certain people to access your app. From a third-party login perspective, you need to maintain an allow list of emails and validate the users based on it.

Before you start

The tutorial assumes that you already have a working application integrated with SuperTokens. If you have not, please check the Quickstart Guide.

Prerequisites

This guide uses the UserMetadata recipe to store the allow list. You need to enable it in the SDK initialization step.

Steps

1. Implement the allow list

You can store this list in your own database, or use the metadata feature provided by SuperTokens to store this. This may seem like a strange use case of the user metadata recipe provided, but it works.

The following code samples show you how to save the allow list in the user metadata.

import UserMetadata from "supertokens-node/recipe/usermetadata"

async function addEmailToAllowlist(email: string) {
let existingData = await UserMetadata.getUserMetadata("emailAllowList");
let allowList: string[] = existingData.metadata.allowList || [];
allowList = [...allowList, email];
await UserMetadata.updateUserMetadata("emailAllowList", {
allowList
});
}

async function isEmailAllowed(email: string) {
let existingData = await UserMetadata.getUserMetadata("emailAllowList");
let allowList: string[] = existingData.metadata.allowList || [];
return allowList.includes(email);
}
Multi Tenancy

For a multi tenant setup, you can even store an allow list per tenant. This setup allows limiting sign ups for different emails for different tenants. If doing this, pass in the tenantID to the functions above, which you can obtain from the input to the API overrides shown below.

2. Check if the email is allowed

Update the backend SDK API function to only allow sign up requests from users that are on the allow list. To do this you need to use the check functions from the previous code snippet.

import ThirdParty from "supertokens-node/recipe/thirdparty";
import supertokens from "supertokens-node";

ThirdParty.init({
override: {
functions: (originalImplementation) => {
return {
...originalImplementation,
signInUp: async function (input) {
let existingUsers = await supertokens.listUsersByAccountInfo(input.tenantId, {
email: input.email
});
if (existingUsers.length === 0) {
// this means that the email is new and is a sign up
if (!(await isEmailAllowed(input.email))) {
// email is not in allow list, so we disallow
throw new Error("No sign up")
}
}
// We allow the sign in / up operation
return originalImplementation.signInUp(input);
}
}
},
apis: (originalImplementation) => {
return {
...originalImplementation,
signInUpPOST: async function (input) {
try {
return await originalImplementation.signInUpPOST!(input);
} catch (err: any) {
if (err.message === "No sign up") {
// this error was thrown from our function override above.
// so we send a useful message to the user
return {
status: "GENERAL_ERROR",
message: "Sign ups are disabled. Please contact the admin."
}
}
throw err;
}
}
}
}
}
})

See also