Create a React App with Biometric Authentication
Tags: react, passwordless, tutorial, security
Published: 2022-02-16 15:35:55

Passage offers developers a frictionless process to implement passwordless user authentication into their application.  In this guide, learn how to build a simple React application that uses Passage for biometric authentication. 

Users will log in to your application using the biometrics built into their devices (e.g. FaceID, TouchID, Windows Hello, etc) or with magic links sent to their email.  The app will be built such that authenticated users can view a simple dashboard, while unauthenticated users will be blocked.  This article will walk you through how to create a React app, setup the components, and authenticate users with Passage.  

If you are already familiar with React, you can go straight to our full example application on [GitHub](https://github.com/passageidentity/example-react) or skip to "Add User Authentication with Passage" to learn how to integrate biometric authentication into an existing application.

# Setup

You will need to have `Node >= 14.0.0 and npm >= 5.6` on your machine.  To create a React app run:

```bash
npx create-react-app passage-app
cd passage-app
```

Now install the router (`react-router-dom`):

```bash
npm install react-router-dom
```

# Build Views for the Application

The router is used to navigate between the views in your application.  In this application, we will have two views `Home.js` and `Dashboard.js`.  The Home view will contain Passage’s `<passage-auth>` custom element, which enables user authentication.  The Dashboard view will be an authenticated route and will block any unauthenticated users from viewing the dashboard.

```bash
mkdir src/views
touch src/views/Home.js
touch src/views/Dashboard.js
```

Lets start with building the Home view `src/views/Home.js`:

```jsx
function Home() {
    return (
        <div className="home-div">
					This is the Home View!
        </div>
    );

}

export default Home;
```

Now we will build a simple Dashboard view `src/views/Dashboard.js`:

```jsx
function Dashboard() {
      return (
          <div className="dashboard-div">
	           Welcome!
          </div>
      );
}

export default Dashboard;
```

# Setup the Router in app.js and index.js

Let’s edit `src/app.js`, which is the entry point for the React app.  Configure the router to have routes to both the Home view and the Dashboard view:

```jsx
import React from "react";
import { Routes, Route } from 'react-router-dom';

import Home from "./views/Home";
import Dashboard from "./views/Dashboard";

function App() {
  return (
      <div>
            <div className="main-container">
                <Routes>
                    <Route path="/" element={<Home/>}></Route>
                    <Route path="/dashboard" element={<Dashboard/>}></Route>
                </Routes>
            </div>
      </div>
  );
}

export default App;
```

Also, edit `src/index.js` to add the router to the application:

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './app';
import { BrowserRouter as Router } from "react-router-dom";

ReactDOM.render(
  <Router>
    <App />
  </Router>,
  document.getElementById('root')
);
```

Now let’s create a Banner component that we can add to `src/app.js`, making it visible throughout the application.  Let’s create a `component` directory and create the `banner.js` file.  Also, let’s create a `styles` directory and create a `Banner.module.css` file to add CSS styling to the Banner component.

```bash
mkdir src/components
touch src/components/banner.js
mkdir src/styles
touch src/styles/Banner.module.css
```

 Lets build `src/components/banner.js` with this code:

```jsx
import styles from '../styles/Banner.module.css';

function Banner() {
    return ( 
        <div className={styles.mainHeader}>
            <a href="https://passage.id/" ><div className={styles.passageLogo}></div></a>
            <div className={styles.headerText}>Passage + React Example App</div>
            <div className={styles.spacer}></div>
            <a href="https://passage.id/" ><span className={styles.text}>Go to Passage</span></a>
        </div>
    );
}
export default Banner;
```

Copy this CSS into `src/styles/Banner.module.css` to add styling to the above component:

```css
.mainHeader{
    padding: 20px 30px;
    display: flex;
    align-items: center;
    background-color: #27417E;
    color: white;
}
.header-text {
    font-size: 24px;
    margin-left: 10px;
}
    
.passageLogo {
		background-image: url('https://storage.googleapis.com/passage-docs/passage-logo.svg');
    background-repeat: no-repeat;
    width: 60px;
    height: 60px;
    cursor: pointer;
}
.spacer {
    flex-grow: 1;
}
    
.link {
    margin-left: 20px;
    color: white;
    text-decoration-color: white;
}
```

Now let’s add the Banner component to `src/app.js` so that it will appear throughout the app: 

```jsx
import React from "react";
import { Routes, Route } from 'react-router-dom';

import Home from "./views/Home";
import Dashboard from "./views/Dashboard";
import Banner from "./components/banner";

function App() {
  return (
      <div>
            <Banner/>
            <div className={styles.mainContainer}>
                <Routes>
                    <Route path="/" element={<Home/>}></Route>
                    <Route path="/dashboard" element={<Dashboard/>}></Route>
                </Routes>
            </div>
      </div>
  );
}

export default App;
```

Now lets add a footer and some styling to `src/app.js`:

```jsx
import React from "react";
import { Routes, Route } from 'react-router-dom';

import Home from "./views/Home";
import Dashboard from "./views/Dashboard";
import Banner from "./components/Banner";
import styles from './styles/App.module.css';

function App() {
  return (
      <div>
            <Banner/>
            <div className={styles.mainContainer}>
                <Routes>
                    <Route path="/" element={<Home/>}></Route>
                    <Route path="/dashboard" element={<Dashboard/>}></Route>
                </Routes>
            </div>
            <div className={styles.footer}>
                Learn more with our <a href="https://docs.passage.id">Documentation</a> and <a href="https://github.com/passageidentity">Github</a>.      
            </div>
      </div>
  );
}

export default App;
```

And the styling lives in `src/styles/App.module.css`

```css
.mainContainer {
    background: white;
    box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
    border-radius: 20px;
    width: 310px;
    min-height: 310px;
    margin: 30px auto;
}
.footer {
    text-align: center;
    font-size: 18px;
}
```

# Add User Authentication with Passage

Now it is time to add passwordless authentication to your application using Passage. First, install the Passage custom element from `npm`:

```bash
npm install --save @passageidentity/passage-auth
```

Then import the package in the view where you intend to authenticate users.  In this case, `src/views/Home.js`

```jsx
**import** '@passageidentity/passage-auth'
```

Importing this script will register the Passage custom element for use in your React view. For more information about custom elements refer to the [online documentation.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)

Create an application in the Passage Console ([here](https://console.passage.id/register)) with the following settings:

- Authentication Origin: `http://localhost:3000`
- Redirect URL: `/dashboard`

Below is a screenshot from creating a Passage App in the Passage Console:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uzn0tubbv6rg90v03chb.png)

Once the application has been created, copy the Application ID from the console into a *.env* file in the root of React app’s repository.

```yaml
# .env
REACT_APP_PASSAGE_APP_ID=<Your New Passage App ID Here>
```

Now lets add the `passage-auth` custom element to `src/views/Home.js`

```jsx
import "@passageidentity/passage-auth";

function Home() {
    return (
        <passage-auth app-id={process.env.REACT_APP_PASSAGE_APP_ID}></passage-auth>
    );

}

export default Home;
```

# Verifying User Authentication

Now it is time to prevent unauthenticated users from accessing the dashboard page. Please keep in mind that this dashboard protection will not protect sensitive API endpoints. Your server should always use one of the [Passage backend libraries](https://docs.passage.id/backend-libraries/overview) to authorize users before returning sensitive data.

For the simplicity of this example, let’s focus on the frontend code.

Instead, implement this check by creating a hook to authenticate the current Passage user.  This hook contains a reusable piece of stateful logic that allows any component to check if the current user is authenticated and return basic user info that Passage stores for you.  

Create a directory for `hooks` and create a file called `useCurrentUser.js:`

```bash
mkdir src/hooks
touch src/hooks/userCurrentUser.js
```

The code for this hook is as follows:

```jsx
import { useState, useEffect } from "react";
import { PassageUser } from '@passageidentity/passage-auth/passage-user';

export function useCurrentUser() {
    const [result, setResult] = useState({
        isLoading: true,
        isAuthorized: false,
        username: '',
    });

    useEffect(() => {
        let cancelRequest = false;
        new PassageUser().userInfo().then(userInfo=> {
            if( cancelRequest ) {
                return;
            }
            if(userInfo === undefined){
                setResult({
                    isLoading: false,
                    isAuthorized: false,
                    username: "",
                });
                return;
            }
            setResult({
                isLoading: false,
                isAuthorized: true,
                username: userInfo.email ? userInfo.email : userInfo.phone,
            });
        });
        return () => {
            cancelRequest = true;
        };
    }, []);
    return result;
}
```

> Note: The React useEffect() hook happens on both mount and unmount, so canceling the API request will prevent an error that can occur when handling API requests in a useEffect hook that occurs when setting the state on a component that is unmounted.
> 

Now that this hook has been implemented, move back to `src/view/Dashboard.js` and use this hook to verify that a user has been properly authenticated.

```jsx
import {useCurrentUser} from '../hooks/useCurrentUser';
import styles from '../styles/Dashboard.module.css';

function Dashboard() {
    const {isLoading, isAuthorized, username} = useCurrentUser();

    if (isLoading) {
        return null;
    }
    const authorizedBody = 
    <>
        You successfully signed in with Passage.
        <br/><br/>
        Your email is: <b>{username}</b>
    </>

    const unauthorizedBody = 
    <>
        You have not logged in and cannot view the dashboard.
        <br/><br/>
        <a href="/" className={styles.link}>Login to continue.</a>
    </>

    return (
        <div className={styles.dashboard}>
            <div className={styles.title}>{isAuthorized ? 'Welcome!' : 'Unauthorized'}</div>
            <div className={styles.message}>
                { isAuthorized ? authorizedBody : unauthorizedBody }
            </div>
        </div>
    );

}

export default Dashboard;
```

Now unauthenticated users that visit `/dashboard` will be shown an unauthorized page, while authenticated user will be shown their Passage User ID.

> Note: Notice that styles have also been added to this version of `src/views/Dashboard.js` A copy of `src/styles/Dashboard.module.css`can be found below:
> 

```css
.dashboard{
    padding: 30px 30px 20px;
}
.title {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 30px;
}
.message {
    overflow-wrap: anywhere;
}
.link {
    color: black;
    text-decoration-color: black;
}
```

Now run the app, to see Passage biometric authentication in action:

```bash
npm run start
```

# Conclusion

Congratulations! You now have a React application that utilizes passwordless authentication.  Biometric authentication, as you hopefully can see, is both frictionless to implement with Passage and effortless for end users to authenticate themselves.

You can see the finished application on GitHub [here](https://github.com/passageidentity/example-react).

# Learn More About Passage

To learn more about Passage and biometric authentication for web applications, you can:

- Explore our [dashboard](https://console.passage.id/) to view and create users, customize your application, and add friends
- Read our [guides](https://docs.passage.id/) for other tech stacks and learn how to authorize requests in your backend server
- Join our [Discord](https://discord.com/invite/445QpyEDXh) and say hi

‍Passage is in beta and actively seeking feedback on the product. If you have feedback, bug reports, or feature requests, we would love to hear from you. You can email us at [support@passage.id](mailto:support@passage.id) or fill out [this form](https://airtable.com/shriJ6n4asgxuGAdU).

This article was originally published on the [Passage blog](https://passage.id/blog).