2) Showing the first and second factor UI
caution
- SuperTokens is not yet optimised for 2FA implementation, so you have to add a lot of customisations for it to work. We are working on improving the development experience for 2FA as well as adding more factors like TOTP. Stay tuned.
- A demo app that uses the pre built UI can be found on our GitHub.
#
First factor UIIf you have correctly followed the frontend setup guide in the ThirdPartyEmailPassword recipe, you should see the login UI when you visit <YOUR_WEBSITE_DOMAIN>/auth?rid=thirdpartyemailpassword
.
No further step is required for the first factor.
SecondFactor
claim validator#
Create and add the We will create a custom claim called SecondFactorClaim
which will be responsible to check if the second factor has been completed or not. Then we can use the result in various places to protect frontend routes (in the subsequent steps).
Create a file called SecondFactorClaim.tsx
in which you can add the following code:
import { BooleanClaim } from "supertokens-auth-react/recipe/session";
const SecondFactorClaim = new BooleanClaim({
id: "2fa-completed",
refresh: async () => {
// This is something we have no way of refreshing, so this is a no-op
},
});
export default SecondFactorClaim
Then in the main session.init
in the supertokens.init
block, add this claim's validator so that it runs the check on each route.
import React from 'react';
import SuperTokens from "supertokens-auth-react";
import Session from "supertokens-auth-react/recipe/session";
import SecondFactorClaim from "./SecondFactorClaim";
SuperTokens.init({
appInfo: {
appName: "<YOUR_APP_NAME>",
apiDomain: "<YOUR_API_DOMAIN>",
websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
apiBasePath: "/auth",
websiteBasePath: "/auth"
},
recipeList: [
// other recipes..
Session.init({
override: {
functions: (oI) => ({
...oI,
getGlobalClaimValidators: ({ claimValidatorsAddedByOtherRecipes }) => {
return [...claimValidatorsAddedByOtherRecipes, SecondFactorClaim.validators.isTrue()];
},
}),
},
})
]
});
#
Second factor UIFor this guide, we will be showing the second factor UI on /second-factor
.
#
1) Disable the default UIimport SuperTokens from "supertokens-auth-react";
import Passwordless from "supertokens-auth-react/recipe/passwordless";
SuperTokens.init({
appInfo: {
appName: "<YOUR_APP_NAME>",
apiDomain: "<YOUR_API_DOMAIN>",
websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
apiBasePath: "/auth",
websiteBasePath: "/auth"
},
recipeList: [
Passwordless.init({
contactMethod: "PHONE",
signInUpFeature: {
// this will prevent the passwordless UI from being rendered when you
// visit <YOUR_WEBSITE_DOMAIN>/auth?rid=passwordless
disableDefaultUI: true,
},
})
],
})
/second-factor
#
2) Create the second factor UI on Copy & paste the code for the second-factor UI from our demo app right here.
This component customises the Passwordless.SignInUpTheme
component to:
- Add a button to "login with another account" which allows users to redo the first factor.
- Redirects the user to the
/
route in case the second factor has already been completed. - Auto sends the OTP to the user in case they are signing in and we already know their phone number.
#
3) Override the passwordless UI components to change the header text and disable the change phone number buttonCopy / Paste the following override customisations in the Passwordless.init
function call:
import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import Passwordless, { PasswordlessComponentsOverrideProvider } from "supertokens-auth-react/recipe/passwordless";
import { useSessionContext } from "supertokens-auth-react/recipe/session";
SuperTokens.init({
appInfo: {
appName: "<YOUR_APP_NAME>",
apiDomain: "<YOUR_API_DOMAIN>",
websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
apiBasePath: "/auth",
websiteBasePath: "/auth"
},
recipeList: [
Passwordless.init({
contactMethod: "PHONE",
signInUpFeature: {
// this will prevent the passwordless UI from being rendered when you
// visit <YOUR_WEBSITE_DOMAIN>/auth?rid=passwordless
disableDefaultUI: true,
},
})
],
})
function App() {
return (
<SuperTokensWrapper>
<PasswordlessComponentsOverrideProvider
components={{
PasswordlessSignInUpHeader_Override: () => {
return (
<div
style={{
fontSize: "30px",
marginBottom: "10px",
}}>
Second factor auth
</div>
);
},
// we override the component which shows the change phone number button
PasswordlessUserInputCodeFormFooter_Override: ({ DefaultComponent, ...props }) => {
const session = useSessionContext();
if (session.loading !== true && session.accessTokenPayload.phoneNumber === undefined) {
// this will show the change phone number button
return <DefaultComponent {...props} />;
}
// this will hide the change phone number button
return null;
},
}}>
{/* Rest of the JSX */}
</PasswordlessComponentsOverrideProvider>
</SuperTokensWrapper>
);
}
export default App;
#
4) Disaplay the second factor component on your app's routerFinally, we add the custom component we copy / pasted before to our router:
import {
Routes,
Route,
} from "react-router-dom";
import * as reactRouterDom from "react-router-dom";
import { SuperTokensWrapper, getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react";
import { SessionAuth } from "supertokens-auth-react/recipe/session";
import SecondFactor from "./SecondFactor";
function App() {
return (
<SuperTokensWrapper>
<div className="App">
<div className="fill">
<Routes>
{getSuperTokensRoutesForReactRouterDom(reactRouterDom)}
<Route
path="/second-factor"
element={
<SessionAuth>
<SecondFactor />
</SessionAuth>
}
/>
</Routes>
</div>
</div>
</SuperTokensWrapper>
);
}