Skip to main content

Enable email verification

important

For passwordless login with email, a user's email is automatically marked as verified when they login. Therefore, the only time this flow would be triggered is if a user changes their email during a session.

There are two modes of email verification:

  • REQUIRED: Requires that the user's email is verified before they can access your application's frontend or backend routes (that are protected with a session).
  • OPTIONAL: Adds information about email verification into the session, but leaves it up to you to enforce it on the backend and frontend based on your business logic.
CAUTION

This information only applies to scenarios in which you are using SuperTokens Session Access Tokens.

If you are implementing Unified Login you will have to manually check the email_verified claim on the OAuth2 Access Tokens. Please read the separate page that shows you how to verify the token.

Step 1: Backend setup#

import SuperTokens from "supertokens-node";
import EmailVerification from "supertokens-node/recipe/emailverification";
import Session from "supertokens-node/recipe/session";

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "...",
},
recipeList: [
EmailVerification.init({
mode: "REQUIRED", // or "OPTIONAL"
}),
Session.init(),
],
});

Step 2: Frontend setup#

Do you use react-router-dom?
YesNo

Step 3: Checking if the user's email is verified in your APIs#

If using REQUIRED mode

On the backend, when you initialize the email verification recipe in this mode, the verifySession middleware automatically checks if the user's email is verified based on the contents of the session's payload. If the email is not verified, the verifySession middleware will return a 403 status code to the client.

If using OPTIONAL mode

In this mode, you need to check if the email is verified yourself in the APIs in which you want this constraint. The verification status should already be in the session's payload.

import { verifySession } from "supertokens-node/recipe/session/framework/express";
import express from "express";
import { SessionRequest } from "supertokens-node/framework/express";
import { EmailVerificationClaim } from "supertokens-node/recipe/emailverification";

let app = express();

app.post(
"/update-blog",
verifySession({
overrideGlobalClaimValidators: async (globalValidators) => [...globalValidators, EmailVerificationClaim.validators.isVerified()],
}),
async (req: SessionRequest, res) => {
// All validator checks have passed and the user has a verified email address
}
);

We add the SDK's EmailVerificationClaim validator to the verifySession middleware call as shown above, and that will only allow access if the email is verified, else it will return 403 to the frontend.

Step 4: Protecting frontend routes#

If using REQUIRED mode

Wrapping your website routes using <SessionAuth /> should enforce email verification. If the user's email is not verified, SuperTokens will automatically redirect the user to the email verification screen.

If using OPTIONAL mode

import React from "react";
import { SessionAuth, useSessionContext } from 'supertokens-auth-react/recipe/session';
import { EmailVerificationClaim } from 'supertokens-auth-react/recipe/emailverification';

const VerifiedRoute = (props: React.PropsWithChildren<any>) => {
return (
<SessionAuth>
<InvalidClaimHandler>
{props.children}
</InvalidClaimHandler>
</SessionAuth>
);
}

function InvalidClaimHandler(props: React.PropsWithChildren<any>) {
let sessionContext = useSessionContext();
if (sessionContext.loading) {
return null;
}

if (sessionContext.invalidClaims.some(i => i.id === EmailVerificationClaim.id)) {
// Alternatively you could redirect the user to the email verification screen to trigger the verification email
// Note: /auth/verify-email is the default email verification path
// window.location.assign("/auth/verify-email")
return <div>You cannot access this page because your email address is not verified.</div>
}

// We show the protected route since all claims validators have
// passed implying that the user has verified their email.
return <div>{props.children}</div>;
}

Above we are creating a generic component called VerifiedRoute which enforces that its child components can only be rendered if the user has a verified email address.

In the VerifiedRoute component, we use the SessionAuth wrapper to ensure that the session exists. The SessionAuth wrapper will create a context that contains a prop called invalidClaims which will contain a list of all claim validations that have failed.

The email verification recipe on the frontend, adds the EmailVerificationClaim validator automatically, so if the user's email is not verified, the invalidClaims prop will contain information about that. Alternatively you could also redirect the user to the default email verification path to trigger the sending of the verification email.

We check the result of the validation in the InvalidClaimHandler component which displays "You cannot access this page because your email address is not verified." if the EmailVerificationClaim validator failed.

If all validations pass, we render the props.children component.

See also#

Looking for older versions of the documentation?
Which UI do you use?
Custom UI
Pre built UI