Auth0 authentication with react
Tags: react, auth0, authentication, javascript
Published: 2020-05-03 19:51:13

<figure>
<img src="https://images.unsplash.com/photo-1549927455-67cc16cc490c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1950&q=80" alt="cover photo" />
<figcaption>
Photo by <a href="https://unsplash.com/@chrispanas?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">chris panas</a> on <a href="https://unsplash.com/s/photos/lock?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>

</figcaption>
</figure>

Authentication is a major part of most Apps. In most cases, you will need some kind of authentication in your next project. Wouldn't it be nice if you got this out of the way once and for all? well... let's just get to it.

We are going to use [Auth0](https://auth0.com/) , an identity management platform for application builders to build an app that allows users to login/signup with their Gmail account and display a user profile with their information. You can add other social login options as [Auth0](https://auth0.com/) supports quite a number. 

<strong>Note:</strong> I developed this taking Cory house's [Securing React Apps with Auth0](https://app.pluralsight.com/library/courses/react-auth0-authentication-security/table-of-contents) course on [pluralsight](pluralsight.com)

I assume you know the basics of React.

You can find the project [here](https://github.com/kal-J/Auth0-with-react) on Github.

Step 1.
create a new react app. 

```
npx create-react-app auth0-with-react
```

Next we shall install all npm packages we shall need.
In the <i>auth0-with-react</i> folder, run 

```
npm install auth0-js dotenv react-router-dom
```

`auth0-js` will allow us integrate [Auth0](https://auth0.com/) authentication in our react app.

`react-router-dom` will be used for dynamic navigation between pages in our App.

`dotenv` is for reading the `.env` file where we shall be storing our [Auth0](https://auth0.com/) credentials.

At this point, head over to [Auth0](https://auth0.com/) and signup for an account.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4skw3fagblieuh9d39gj.png)

Create a new single page application and give it a name.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/efhhce25go2o0hhczsyr.png)

After you have created your application,take note of your Domain and client Id as we shall need them later.

 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qvcbk76kcm0o2es28mcv.png)

In your `src/` directory, create `Auth.js` file. This is the file where our authentication stuff will go.
Add the following code in the `Auth.js`
#### **`Auth.js`**
```js
import auth0 from "auth0-js";
import dotenv from "dotenv";

//read .env file
dotenv.config();

export default class Auth {
  // pass history for redirection after login
  constructor(history) {
    this.history = history;
    this.userProfile = null;

    // create a new auth object with your auth0 credentials (domain, clientID, redirectUri)
    // You will have to setup a redirectUri in your Auth0 app's settings. in this case its http://localhost:3000/callback
    this.auth0 = new auth0.WebAuth({
      domain: process.env.REACT_APP_AUTH0_DOMAIN,
      clientID: process.env.REACT_APP_AUTH0_CLIENTID,
      redirectUri: process.env.REACT_APP_AUTH0_CALLBACK_URL,
      responseType: "token id_token", // we want a token and id_token returned in the response
      scope: "openid profile email", 
      // openid is auth protocol we are using. 
      // we want access to the profile info and email from Gmail in our case
    });
  }

  // login method
  login = () => {
    this.auth0.authorize(); // this is all you need to login
  };

  // Extract the returned tokens and store in local storage
  handleAuthentication = () => {
    // Parse the url hash and extract the returned tokens depending on the transaction.
    this.auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        this.setSession(authResult);
        this.history.push("/");
      } else if (err) {
        this.history.push("/");
        alert(`Error: ${err.error}. check console`);
        console.log(err);
      }
    });
  };

  setSession = (authResult) => {
    //set the time that the access token will expire
    const expiresAt = JSON.stringify(
      authResult.expiresIn * 1000 + new Date().getTime()
    );

    localStorage.setItem("access_token", authResult.accessToken);
    localStorage.setItem("id_token", authResult.idToken);
    localStorage.setItem("expires_at", expiresAt);
  };

  //check if user is authentecated
  isAuthenticated = () => {
    const expiresAt = JSON.parse(localStorage.getItem("expires_at"));
    return new Date().getTime() < expiresAt;
  };

  logout = () => {
    // clear localstorage
    localStorage.removeItem("access_token");
    localStorage.removeItem("id_token");
    localStorage.removeItem("expires_at");
    this.userProfile = null;
    //logout from server and redirect to home page
    this.auth0.logout({
      clientID: process.env.REACT_APP_AUTH0_CLIENTID,
      returnTo: "http://localhost:3000/",
    });
  };
 
  // Get access token
  getAccessToken = () => {
    const accessToken = localStorage.getItem("access_token");
    if (!accessToken) {
      throw new Error("No access token found");
    }
    return accessToken;
  };

  // Get user's profile
  getProfile = (cb) => {
    if (this.userProfile) return cb(this.userProfile);
    this.auth0.client.userInfo(this.getAccessToken(), (err, profile) => {
      if (profile) this.userProfile = profile;
      cb(profile, err);
    });
  };
}

```
Let's now pull in `react-router-dom` to handle dynamic routing in our app.
In your `index.js`, lets add it like below.
#### **`index.js`**
```js
import React from 'react';
import ReactDOM from 'react-dom';
import{ BrowserRouter as Router, Route} from 'react-router-dom';
import './index.css';
import App from './App';

ReactDOM.render(
  <Router>
    <Route component={App} />
  </Router>,
  document.getElementById('root')
);

```
Now in our `App.js` lets pull in our Auth object and use it to protect our `/profile` route by checking if user is authenticated.

#### **`App.js`**
```js
import React from "react";
import { Route, Redirect } from "react-router-dom";
import Home from "./Home";
import Profile from "./Profile";
import Nav from "./Nav";
import Auth from "./Auth";
import Callback from "./Callback";

function App(props) {
  const auth = new Auth(props.history);
  return (
    <>
      <Nav auth={auth} />
      <div className="body">
        <Route
          path="/"
          exact
          render={(props) => <Home auth={auth} {...props} />}
        />
        <Route
          path="/callback"
          exact
          render={(props) => <Callback auth={auth} {...props} />}
        />
        <Route
          path="/profile"
          exact
          render={(props) =>
            auth.isAuthenticated() ? (
              <Profile auth={auth} {...props} />
            ) : (
              <Redirect to="/" />
            )
          }
        />
      </div>
    </>
  );
}

export default App;
```
You will notice we are importing `Home`,`Profile`,`Nav` and `Callback` in the `App` component, let's go ahead and create these.
starting with the `Callback.js`, in our `/src` directory.
#### **`Callback.js`**
```js
import React, { Component } from 'react'

export default class Callback extends Component {
    componentDidMount = () => {
      // handle authentication if expected values are in the URL.
      if(/access_token|id_token|error/.test(this.props.location.hash)){
          this.props.auth.handleAuthentication();
      } else {
          throw new Error("Invalid callback URL");
      }
    }
    

    render() {
        return (
            <h1>
                Loading...
            </h1>
        )
    }
}
```
Auth0 returns a hash string containing an access token and id token to the callback uri you provide in your Auth0 app dashboard.
In the above code, we are checking if the access token or id token are present in the location object and if so, we call the `handleAuthentication()` method of the `auth` object which we passed as a prop from the `App.js`

Next `Home` component. still  in the `src/` directory.
#### **`Home.js`**
```js
import React from "react";
import { Link } from "react-router-dom";

const Home = (props) => {
  return (
    <div>
      <h1>Home</h1>
      {props.auth.isAuthenticated() ? (
        <Link to="/profile">View Profile</Link>
      ) : null}
    </div>
  );
};

export default Home;
```
For our navbar, lets create `Nav.js` in the `src/` directory.
#### **`Nav.js`**
```js
import React from "react";
import { Link } from "react-router-dom";

const Nav = (props) => {
  const { isAuthenticated, login, logout } = props.auth;
  return (
    <nav>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/profile">Profile</Link>
        </li>
        <li>
          <button onClick={isAuthenticated() ? logout : login}>
            {isAuthenticated() ? "log out" : "log in"}
          </button>
        </li>
      </ul>
    </nav>
  );
};
export default Nav;
```
In the code above, we are linking to our home and profile routes and also displaying the login/logout buttons depending on whether the user is authenticated or not.

And lastly in the `Profile` component. We fetch the user profile using the `getProfile()` method of our `auth` object, store the returned profile object in state and use it to display the user's profile.
Lets create a `profile.js` file in your `src/` directory and add the following code. 
#### **`Profile.js`**
```js
import React, { Component } from "react";
import "./profile.css";

export default class Profile extends Component {
  state = {
    profile: null,
    error: "",
  };
  componentDidMount() {
    this.loadUserProfile();
  }

  loadUserProfile() {
    this.props.auth.getProfile((profile, error) => {
      this.setState({ profile, error });
    });
  }
  render() {
    const { profile } = this.state;
    if (!profile) return null;
    return (
      <div className="profile">
        <h1>Profile</h1>
        <img src={profile.picture} alt="profile pic" />
        <div className="list-info">
          <div className="list">
            <span className="property">Name</span>
            <span>{profile.name}</span>
          </div>
          <div className="list">
            <span className="property">Given Name</span>
            <span>{profile.given_name}</span>
          </div>
          <div className="list">
            <span className="property">Family Name</span>
            <span>{profile.family_name}</span>
          </div>
          <div className="list">
            <span className="property">Nick Name</span>
            <span>{profile.nickname}</span>
          </div>
          <div className="list">
            <span className="property">Email</span>
            <span>{profile.email}</span>
          </div>
        </div>

      </div>
    );
  }
}
```
And here is the `css` for `Profile`
#### **`profile.css`**
```css

.profile{
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}
img{
    max-width: 100px;
    max-height: 100px;
    border: 0px solid;
    border-radius: 50px;
}
.list-info{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
div.list{
    margin: 1em;
}
div>span{
    margin: 0.2em;
}
.property{
    color: #fff;
    background-color: #f0582a;
    border-radius: 8px;
    padding: 0.2em;
}
```
And that's it. if your successful, you should have something looking like the one below.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/35yoj9euqkxw54o51n6r.png)