Skip to main content

Passwordless login via invite link

In this flow, the admin of the app will call an API to sign up a user and send them on invite link. Once the user clicks on that, they will be logged in and can access the app. If a user has not been invited before, their sign in attempt will fail.

We start by overriding the createCodePOST API to check if the input email / phone number was already invited before. If not, we send back a user friendly message to the frontend. We can check if a user was invited before by checking if they already exist in SuperTokens - cause users are created in SuperTokens when they successfully complete the invite flow.

import Passwordless from "supertokens-node/recipe/passwordless";

Passwordless.init({
override: {
apis: (originalImplementation) => {
return {
...originalImplementation,
createCodePOST: async function (input) {
if ("email" in input) {
let existingUser = await Passwordless.getUserByEmail({
email: input.email
});
if (existingUser === undefined) {
// this is sign up attempt
return {
status: "GENERAL_ERROR",
message: "Sign up disabled. Please contact the admin."
}
}
} else {
let existingUser = await Passwordless.getUserByPhoneNumber({
phoneNumber: input.phoneNumber
});
if (existingUser === undefined) {
// this is sign up attempt
return {
status: "GENERAL_ERROR",
message: "Sign up disabled. Please contact the admin."
}
}
}
return await originalImplementation.createCodePOST!(input);
}
}
}
}
})

createCodePOST is called when the user enters their email or phone number to login. We override it to check:

  • If there exists a user with the input email or phone number, it means they are signing in and so we allow the operation.
  • Otherwise it means that the user has not been invited to the app and we return an appropriate message to the frontend.

Now we will see how to make the API in which the admin of the app can create new users and invite them:

import express from "express";
import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { SessionRequest } from "supertokens-node/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";
import Passwordless from "supertokens-node/recipe/passwordless";

let app = express();

app.post("/create-user", verifySession({
overrideGlobalClaimValidators: async function (globalClaimValidators) {
return [...globalClaimValidators,
UserRoles.UserRoleClaim.validators.includes("admin")]
}
}), async (req: SessionRequest, res) => {
let email = req.body.email;

let inviteLink = await Passwordless.createMagicLink({
email
});

// TODO: send inviteLink to user's email
res.send("Success");
});
  • We guard the above API such that only signed in users with the "admin" role can call it. Feel free to change that part of the API.
  • The code above uses the default magic link path for the invite link (/auth/verify). If you are using the pre built UI, our frontend SDK will automatically log the user in. If you want to show a different UI to the user, then you can use a different path in the link (by modifying the inviteLink string) and make your own UI on that path. If you are making your own UI, you can use the consumeCode function provided by our frontend SDK to call the passwordless API that verifies the code in the URL and creates the user.
  • You can change the lifetime of the magic link, and therefore the invite link, by following this guide.
Which UI do you use?
Custom UI
Pre built UI