React Router Flow & Adding TOTP MFA Authentication In , we looked at how to wire up a React application with with an identity provider. In this part, we will use to add a routing and auth flow that will only allow logged in users to view the application while redirecting users who are not logged in back to the sign up / sign in page. Part 1 React Router In Part 1 we used with to set up our authentication. The techniques we will be covering here in part two will work with any authentication provider and are not limited to Amazon Cognito. Amazon Cognito AWS Amplify We will continue from Part 1 with a React project that has an authentication service already added and continue from there. Our application will have a main route which hold forms for both user sign up and sign in. Authenticator The application routes that need user authentication before being allowed to view will be (taken from the ). private route components React Router documentation The component we will end up using will look like this: PrivateRoute The private route will either allow the user to continue to the component, or redirect to auth if the user is not logged in. Here is how this will look once we implement it: The that we will be rendering will not need to know anything about what is going on with Authentication, all of this logic will be handled by the component. component PrivateRoute Let’s put this all together now into a working application. To see the final project code, check out repo this Creating additional project files First, let’s go ahead and create all of the files we will be needing in the next steps. touch Authenticator.js SignIn.js SignUp.js Router.js Home.js Header.js Now that these files are created, we will walk through them one by one implementing the authentication flow. Authenticator component We will be needing a component to handle both the Sign In & Sign up states of the app, . Authenticator.js When the user is not logged in, we will automatically redirect them to this component. This component will have a state that will be toggled to either show the or component that we will be creating shortly. showSignIn SignIn SignUp This is a pretty basic component that allows us to encapsulate both the Sign In and Sign Up forms in one route. This could also be broken up into two different routes, but I’ve found this approach to be a little nicer and more concise. Header.js Let’s create a basic reusable header with some styling that we can use as the application header. Protected Routes Next, we need to create a couple of routes that we will be using as generic protected routes. We’ll encapsulate these two routes in a single file, . Home.js These routes will only be available when the user is logged in. You will notice that we do not have any logic here that looks to see if the user is logged in, all of the logic will be handled by the router implementation. Both of the two routes we create are wrapped with the higher order component that we import from React Router. This is because we want to have access to the history prop from React Router in case we need it, which we will if we want to programmatically send the user to another route. withRouter — Home is a component with only two real pieces of functionality: a method that calls the method to get and store the user’s so we will be able to greet the user by name, and a component that links us to another route. Home componentDidMount Auth.currentUserInfo username Link — This is a basic component that only has a single piece of functionality, a method that calls the method. When the user successfully signs out, we also reroute the user to the route that we will create in just a moment. Route1 signOut Auth.signOut auth SignIn.js We looked briefly at this Sign In functionality in part 1, and here we will be implementing almost exactly the same functionality with one difference: We will hide the confirmation code screen until the user successfully signs in with username and password. We do this to provide a better user experience. This component uses two main methods of the class: and . Auth signIn signUp If is successful, we redirect them to the route using the history prop that was provided to us by from React Router. signIn Home withRouter history.push('/') If the API call is unsuccessful, we log out the error message. SignUp.js We also implemented this exact functionality in Part 1 as far as signing up and confirming the sign up of a user. The main difference here is that we are also adding some logic / UI to only show the confirmation form if fHomethe user has successfully signed up. Router.js Finally, we have made our way to the bread and butter of the functionality of this application, the router. There is quite a bit of important functionality going on in this file, so we’ll walk through it in depth. — The main import to note here is the component. Redirect will navigate the user to a new location, & will override the current location in the history stack like server-side redirects do. We’ll look at how we implement this in just a second. We also import the three main routes that our application will consist of: , , and . Imports Redirect Authenticator Home Route1 — This component will serve as our container for any routes that we want to be protected and only accessed if the user is logged in. PrivateRoute We are initializing a couple of pieces of state in this component: & which are both set to false. loaded isAuthenticated — When the component is first loaded we want to check if the user is currently logged in, and if so allow them to see the route, and if not redirect them to the sign up page. We do this by calling the method. componentDidMount authenticate — authenticate calls the method which will only return successfully if there is a logged in user. If there is a user, we update the state to true and the state to true. If there is no currently authenticated user, we redirect the user to the Authenticator component by calling . authenticate Auth.currentAuthenticatedUser loaded isAuthenticated this.props.history.push('/auth') — We also create a listener to call a function whenever a route changes by calling . Whenever the route changes, this function will fire and we will check if the user is currently logged in. If they are, we allow them to continue and don’t do anything. If they are not logged in, the promise will catch. componentDidMount this.props.history.listen — In , we return a component from React Router. With the component you have three options to render a component, one of them being the prop, which we are using. In the prop, we check to see if the user is currently authenticated, and if so renders the component passed in as the prop, and if not redirects them to the route. render render Route Route **render** **render** component auth — The component defines the routes using either a component for unprotected routes, or a component for protected routes. Routes Routes Route PrivateRoute App.js We are done writing the code to implement the Auth functionality and we can now clean up the App.js file to use the Header and Router we created That’s it, you should be able to run the application and have complete sign up / sign in functionality! About TOTP (time-based one-time passwords) is quickly becoming the MFA of choice for many companies that place a high value on security as it is more secure than MFA with email. TOTP TOTP uses apps like Authy, Google Authenticator & Duo to implement temporary access tokens that expire every 30 to 60 seconds. Cognito and now AWS Amplify have added this feature, so let’s take a quick look at how we might extend our application to implement this. The first thing to keep in mind when thinking about TOTP is that you should not make it the MFA type of choice unless the user specifies that they would like to use it. This is because the adoption of TOTP is still relatively small. The flow should be like this: User signs up, default MFA type is email User signs in, and somewhere in the application (usually in some type of settings area) we give them the option to enable TOTP. User enables TOTP, show them QR code, and enable TOTP with their app of choice Offer the option to change MFA type back to email if the user would like to do so. In our existing app, one place that we could place this functionality in order to demo it could be in the component. Once the user is signed in we will give them the option to add TOTP in this route. Home The way you initially set up TOTP is with the method. This returns a promise that we will use to create a QR Code. Auth.setupTOTP Auth.setupTOTP(user).then(code => /* create qrcode */ ) Adding TOTP to the existing app We will use the qrcode.react package to show a QR Code: yarn add qrcode.react Home.js In , import QRCode: Home.js // somewhere below existing importsimport QRCode from 'qrcode.react' Because needs access to the object, we will need to get the object by calling in componentDidMount and adding it to the state: setupTOTP user user Auth.currentAuthenticatedUser componentDidMount() { // rest of existing code omitted} Auth.currentAuthenticatedUser().then(user => this.setState({ user })) Next, we can add an method that will set the QRCode that we will be using in the QRCode component: addTTOP addTTOP = () => {Auth.setupTOTP(this.state.user).then(code => {const authCode = "otpauth://totp/AWSCognito:" + this.state.user.username + "?secret=" + code + "&issuer=AWSCognito";this.setState({ qrCode: authCode })});} To learn more about how we created the uri and the API behind provisioning a TOTP token, check out link. authCode this We also want to set the preferred MFA type to TOTP for the user. We can do this with the method. Let’s create a new class method that will capture the challenge answer from the user (we will create the input for challengeAnswer in just a moment) and change the preferred MFA type: Auth.setPreferredMFA setPreferredMFA = (authType) => {Auth.verifyTotpToken(this.state.user,this.state.challengeAnswer).then(() => {Auth.setPreferredMFA(this.state.user, authType).then(data => console.log('MFA update success: ', data)).catch(err => console.log('MFA update error: ', err))})} Now, we can add a couple of lines showing the QRCode if it is defined, adding a form to input the TOTP code, and a couple of buttons to attach to the new methods: render() {<div>// previous code omitted<buttononClick={this.addTTOP}style={{ border: '1px solid #ddd', width: 125 }}><p>Add TOTP</p></button>{(this.state.qrCode !== '') && (<div><QRCode value={this.state.qrCode} /></div>)}<br /><buttononClick={() => this.setPreferredMFA('TOTP')}style={{ border: '1px solid #ddd', width: 125 }}><p>Prefer TOTP</p></button><br /><inputplaceholder='TOTP Code'onChange={e => this.setState({challengeAnswer: e.target.value})}style={{ border: '1px solid #ddd', height: 35 }}/></div>} SignIn.js Now, in the confirmSignIn method, we need to update the method to use the type of MFA that is attached to the current user. This information is available in the user object as , which we will pass in as the third argument to : signIn user.challengename Auth.confirmSignIn confirmSignIn = () => {const { history } = this.propsAuth.confirmSignIn(this.state.user, this.state.authCode, )// rest of code omitted} this.state.user.challengeName Now, when we login we can click Add TOTP, scan the QR code and switch our authentication type to TOTP. When we log out, we should now have to use the TOTP provider we used to scan the QR code in order to get back in. To see the final project code, check out repo. this My Name is . I am a Developer Advocate at working with projects like and , and the founder of . Nader Dabit AWS Mobile AppSync AWS Amplify React Native Training Currently I’m specializing in GraphQL with as well as authentication & authorization for applications, so if this interests you follow me for more future info & tutorials! AWS AppSync JavaScript If you enjoyed this article, and share it! Thanks for your time. please clap n number of times Images courtesy of Amazon Web Services, Inc