TL;DR: In this post, we are going to build a React application with Auth0 authentication using the popup feature. We will cover some of React’s newest features, starting our app with create-react-app. We will be using React Hooks to write components without using any classes. We will also be using Auth0’s Popup authentication flow and we will be covering how the popup mode might be a good option for your project. You can find the GitHub repo here.

React’s New Feature

React Hooks are new with the current version being, React 16.8. This allows for function components to have a state. No longer do you need a class component to have a state. Let’s learn more about Hooks.

"React Hooks are new with the current version being, React 16.8. This allows for function components to have a state."

React Hooks

Hooks are one of the latest additions to React, giving you the power to avoid having to write a class component to use state. Function components are now more powerful and that calls for less code. Yay! Use hooks to "hook" state into a function component to make it much more versatile.

We will be writing our frontend components using Hooks. As we go over the code in the application, we will discuss how to implement this new React feature.

Auth0’s Popup Authentication

Auth0 offers a variety of different methods for users to log in. In today’s example, we are going to be using the popup method. The user will be able to click on a button to enable the popup to activate, the user will log in, and then the popup will disappear after they have been authenticated.

A lot of users will have popups disabled on their browser, so when it’s not necessary to use, we recommend not resorting to the popup method. But it is always good to know how it works!

What We Will Be Building Today

Today we are going to be building a single-page application with a back-end API. The initial view will be a list of TV shows with a Log In button. The user will be able to hit the Log In button and the Auth0 log-in popup will appear.

After successfully logging in, the popup will disappear and the view will change to a list of movies and TV shows. The view of movies and TV shows will be protected so it will not show up unless the user is authenticated properly. At the bottom of those lists, there will be a Log Out button which will take them to the unprotected view of just TV shows.

Getting Started with React

We are going to be using the command create-react-app today to scaffold our React project. Navigate to the location where you would like to have your new project and type the command:

npx create-react-app react-auth0-popup

This command will not only get create-react-app going but with npx, it also avoids the need to install the package locally and will bring in the latest version. In order to use npx, you must have NPM 5.2 or greater installed. It’s great; one command to handle both tasks. Once that is done building, jump into the project:

cd react-auth0-popup

Open the project in whichever IDE you use and let’s see if our project is running correctly. Run the command:

npm run start

It should then navigate automatically to localhost:3000 in the browser. We should see the React logo!

"Using create-react-app and npx together to scaffold React apps is awesome!"

Setting Up Auth0

Today we are going to be using Auth0 for authentication in our application. To create a new account, visit Auth0's sign up page. To log-in to your exsisting account, visit Auth0's home page. Once we have logged in, click on the big button in the upper right-hand corner that says + New Application.

It will then bring up a modal and we will give our new application a name, "react-auth0-popup". Click on Single Page Web App and then click on Create. We will then want to click on the React option for the "technology" we will be using for our application. We will be going over how to integrate Auth0 into our project.

There is a quick-start method, but we will be going over the Auth0 implementation without using the quick-start.

Our Auth0 API

We are next going to set up our API for our project. It will be the "audience" aspect of our app.

Learn more about the audience parameter here.

We are going to head on over to the Auth0 API dashboard. From there, click on the "Create" button:

Create API button on Auth0

Give it a name and an identifier and then click on "Create".

An example for a name could be "tvshows-movie-api" and an example for an identifier could be "https://tvmoviesapi". Remember, the identifier cannot be modified once created.

From there, we will click on "Settings" tab and we will see the identifier and name spots filled in with that information we inputted. We will be using the identifier in a little bit so keep that handy for now.

API Data for our SPA

Jump back to the code and let’s start setting up our API. Create an api folder at the root of the project. Within that folder, create a data folder. In here we are going to have two separate files. One will have the data for our "TV Shows" list and another for our "Movies" list. Make a file within the data folder titled tvShows.json. We are going to make an array of objects. You can use whichever shows or data you want, I am going to have a list of six objects about six different TV shows. It will look like this:

// api/data/tvShows.json
// be sure to delete the commented out file path name for our json files if copying and pasting.

[
  {
    "id": 1,
    "name": "The Office",
    "airDate": "March 24, 2005",
    "lastShowDate": "May 16, 2013",
    "aCastMember": "Steve Carell",
    "totalSeasons": "9",
    "totalEpisodes": "201",
    "interestingFact": "John Krazinski shot the footage of Scranton that appears in the show’s opening credits from his jeep when he and a few friends cased the city before he began shooting."
  },
  {
    "id": 2,
    "name": "Mad Men",
    "airDate": "July 19, 2007",
    "lastShowDate": "May 17, 2015",
    "aCastMember": "Jon Hamm",
    "totalSeasons": "7",
    "totalEpisodes": "92",
    "interestingFact": "The first and second episodes were shot one year apart."
  },
  {
    "id": 3,
    "name": "Friends",
    "airDate": "September 22, 1994",
    "lastShowDate": "May 6, 2004",
    "aCastMember": "Jennifer Aniston",
    "totalSeasons": "10",
    "totalEpisodes": "236",
    "interestingFact": "The theme song performed by The Rembrandts became a #1 single on the American pop charts."
  },
  {
    "id": 4,
    "name": "Stranger Things",
    "airDate": "July 15, 2016",
    "lastShowDate": "October 27, 2017",
    "aCastMember": "Millie Bobby Brown",
    "totalSeasons": "2",
    "totalEpisodes": "17",
    "interestingFact": "They auditioned 906 boys and 307 girls for the main roles."
  },
  {
    "id": 5,
    "name": "Seinfeld",
    "airDate": "July 5, 1989",
    "lastShowDate": "May 14, 1998",
    "aCastMember": "Julia Louis-Dreyfus",
    "totalSeasons": "9",
    "totalEpisodes": "180",
    "interestingFact": "Jerry says 'Hello, Newman' only 15 times in the entire series."
  },
  {
    "id": 6,
    "name": "Breaking Bad",
    "airDate": "January 20, 2008",
    "lastShowDate": "September 29, 2013",
    "aCastMember": "Aaron Paul",
    "totalSeasons": "5",
    "totalEpisodes": "62",
    "interestingFact": "Only two actors appear in every single episode, Aaron Paul and Bryan Cranston."
  }
]

In that same data folder, we are going to make a file titled movies.json that will have an array of objects holding movie data. We will do a list of six objects again. The data will hold information like this:

// api/data/movies.json

[
  {
    "id": 1,
    "name": "Forrest Gump",
    "airDate": "July 6, 1994",
    "aCastMember": "Tom Hanks",
    "interestingFact": "During the ping-pong matches, there was no ball; it was entirely CGI, animated to meet the actors’ paddles."
  },
  {
    "id": 2,
    "name": "The Incredibles",
    "airDate": "November 5, 2004",
    "aCastMember": "Samuel L. Jackson",
    "interestingFact": "The original title of the film was 'The Invincibles.'"
  },
  {
    "id": 3,
    "name": "Black Panther",
    "airDate": "January 29, 2018",
    "aCastMember": "Chadwick Boseman",
    "interestingFact": "Chadwick Boseman (Black Panther) has a background in martial arts and did his own stunts."
  },
  {
    "id": 4,
    "name": "Harry Potter and the Sorcerer's Stone",
    "airDate": "November 14, 2001",
    "aCastMember": "Daniel Radcliffe",
    "interestingFact": "The book and movie are called 'Harry Potter and the Philosopher's Stone' everywhere except the United States."
  },
  {
    "id": 5,
    "name": "Finding Nemo",
    "airDate": "May 30, 2003",
    "aCastMember": "Ellen DeGeneres",
    "interestingFact": "The tiki heads in the tank are caricatures of Pixar employees."
  },
  {
    "id": 6,
    "name": "Jurassic Park",
    "airDate": "June 11, 1993",
    "aCastMember": "Jeff Goldblum",
    "interestingFact": "Jurassic Park was shot on location in 1992 on Hawaii's Kauai Island."
  }
]

Config File for the API

In the api folder, we are going to want to make a config file that will hold some of the data we need for our API routes which we will make in a moment. Go ahead and create a new file config.js in the api folder. That will have two items from the Auth0 API that we set up earlier. The file should look something like this:

// api/config.js

module.exports = {
  AUTH0_DOMAIN: "yourauth0domain.auth0.com",
  AUDIENCE: "https://yourapifromauth0"
};

Note: These credentials come from our application within our Auth0 dashboard, not the API application settings.

Once we have this set up, be sure to head over to our .gitignore file and put this file path in there. We will want this file ignored so when it is pushed to GitHub, others cannot get our Auth0 information.

API Routes

Each of our files filled with TV Show data and Movie data will need to be assigned a route. In the api folder, we will want to make another file, we will name that one routes.js.

We are going to need to add in a couple of dependencies that will be used within this file.

Run the following command to install those project dependencies using NPM:

npm install --save express express-jwt jwks-rsa

Once those are installed, bring them into the top of the routes.js file along with path:

// api/routes.js

const express = require("express");
const jwt = require("express-jwt");
const jwks = require("jwks-rsa");
const path = require("path");

Also, at the top of the file, import the TV show and movie data, the authentication configuration settings, and set our express router up:

// api/routes.js

// Other imports

const allShows = require("./data/tvShows");
const allMovies = require("./data/movies");
const config = require("./config");

const router = express.Router(); 

From here, let’s build a function that will check for our authentication:

// api/routes.js

// imports here

const authCheck = jwt({
  secret: jwks.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: `https://${config.AUTH0_DOMAIN}/.well-known/jwks.json`
  }),
  audience: [config.AUDIENCE],
  issuer: `https://${config.AUTH0_DOMAIN}/`,
  algorithm: "RS256"
});

Routes

By adding in our route paths, we are going to be able to test our API data. Using a product like Postman, we will be able to test whether or not http://locahost:3000/api/ sends back "API is working" or if http://localhost:3000/api/data/tvshows returns a list of TV Shows from our API data.

Note: /api/data/movies will only show if the middleware, authCheck, is ran without any problems.

// api/routes.js

// Imports here

router.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, "../build/index.html"));
  // When the application is built, this file will be created.
  // It currently is not created.
});

router.get("/api/", (req, res) => {
  res.send("API is working");
});

router.get("/api/data/tvshows", (req, res) => {
  res.json(allShows);
});

router.get("/api/data/movies", authCheck, (req, res) => {
  res.json(allMovies);
});

At the bottom of the routes.js file, be sure to export our router information at the bottom of the file:

// api/routes.js

// other code here

module.exports = router;

The SPA’s Server

Now that we have our API ready, we will need to make sure our server is up and running. In the root of the project, create a new folder titled, server. Within that folder, create a file called server.js. In here we are going to need a couple dependencies so dive into that file and let’s get working on it.

We are going to need the following dependencies installed into our project for our server file:

By running the command:

npm install --save body-parser cors

We are going to see those added into our project. Once those are done installing, let’s add them into our server file so that we are able to use them.

At the top of the server.js file, we will want to add the following dependencies and the path for our routes file. Also, declare our port:

// server/server.js

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const routes = require("../api/routes");

const PORT = 3005;

To set up the app:

// server/server.js

// Other imports

const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

By using app.use, we can have our server use our previously defined routes:

// server/server.js

app.use("/", routes);

Now that we have our server set up, let’s get it running on a port. At the bottom of our server app, add the following lines:

// server/server.js

// This is the last line of the file
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));

Once this file is saved, in our terminal within the project, run the command:

node server/server.js

or

nodemon

We will see that it is "Listening on port 3005". We now have our server running!

Building Our Application

Now that we have our server set up and our API data ready for us, let’s build the frontend so that we are able to view this information on a web browser.

Auth0 Credentials

Throughout our frontend app, we are going to be using our Auth0 credentials. For convenience, we'll hold those credentials in a file so that we can easily import them in components that need them. For security, however, we'll include that file in our .gitignore file so that it doesn't get committed to GitHub.

In the src folder, create an auth folder and within that create a file titled config.js. In here, we will want it to look something like this:

// src/auth/config.js

export const config = {
  clientId: "your auth0 clientId",
  domain: "yourauth0domain.auth0.com",
  redirect: "http://localhost:3000/close-popup",
    // we will go over this redirect soon.
  logoutUrl: "http://localhost:3000",
  audience: "https://yourauth0api"
};

With our Auth0 credentials, fill that information in and then stick that file path in the .gitignore file.

Open up the .gitignore file, and insert this line config.js. That will do the trick! To learn more about ignoring files, visit this website.

Let's go to our settings tabs under the application we made earlier, we will see fields for our "Allowed Callbacks URL" and "Allowed Logout URL". There we will put in the URL’s that we put in our auth/config.js file.

This is not from the API settings, but the application settings.

We should be inputting the following into the application settings section:

"Allowed Callback URLs" -> http://localhost:3000/close-popup "Allowed Logout URLs" -> http://localhost:3000

Be sure to save your changes!

Closing the Popup

The popup can be tricky, a lot of people block popups from showing up in their browser. At Auth0, we do not recommend using the popup method because of them being blocked, but it is always good to know how to do it.

When using popup, we will need a separate file that handles the closing of the popup. Within our public folder, create another folder and we’ll name it, close-popup. In there, create a file named, index.html.

This is where we are going to "redirect" after the authentication is verified. We want the close-popup file to run and for that popup to disappear and take the user back to what they were doing.

The code within this file will look like this:

// public/close-popup/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta
      charset="utf-8"
      content="font-src: 'self' data: img-src 'self' data: default-src 'self'"
    />
    <title></title>
    <script src="https://cdn.auth0.com/js/auth0/9.8.1/auth0.min.js"></script>
  </head>
  <body>
    <script type="text/javascript">
      const webAuth = new auth0.WebAuth({
        domain: "your auth0 domain",
        clientID: "your client id"
      });
      webAuth.popup.callback();
    </script>
  </body>
</html>

We will want to put in our Auth0 application credentials into the two lines indicating domain and clientID. Remember, our domain should be something like yourname.auth0.com and the clientID will be a long string of characters.

After putting this file in the .gitignore file, we will be able to have a popup that will close after the log-in process is complete. We are putting it in the .gitignore because you will see that our Auth0 credentials (domain, clientID) are stated within this file.

Auth0 Service File

This file is where the magic will happen for our authentication. Within the auth folder, create a service.js file. We will need the auth0-js dependency. Within the command line, type:

npm install --save auth0-js

Once that is installed, we are going to import our config.js file and our new dependency to the top of our auth/service.js file like so:

// src/auth/service.js

import { config } from "./config";
import * as Auth0 from "auth0-js";

We will start with our Auth component and bring in all of our config items. Underneath the imports, insert the following code:

// src/auth/service.js

// Imports here

class Auth {
  auth0 = new Auth0.WebAuth({
    domain: config.domain,
    clientID: config.clientId,
    redirectUri: config.redirect,
    audience: config.audience,
    responseType: "id_token token",
    scope: "openid profile email"
  });

}

Notice in the responseType we have id_token token. We are going to be using both the id token and the access token for this project. And in the scope, we will have the openid, profile and email coming through.

Setting up our service.js

We are going to be using different variables throughout our functions within our service.js file. Let’s get those set and declared. By setting things like our idToken or idTokenPayload to null, we are going to allow ourselves to give those values when necessary.

// src/auth/service.js

// Imports...

class Auth {
  // auth0 definition...

  loginCallback = () => {};
  logoutCallback = () => {};

  userProfile = null;
  authFlag = "isLoggedIn";
  authStatus = this.isAuthenticated // we will create isAuthenticated soon
    ? "init_with_auth_flag"
    : "init_no_auth_flag";
  idToken = null;
  idTokenPayload = null;
  accessToken;

}

localLogin and localLogout

When a user logs in, they will be activating a couple different functions. The login function (which we will go over soon) will be very important but that function will be calling localLogin. In this function, we are going to be saving a bunch of values to the variables we set just above this. userProfile will now have the authenticated results of idTokenPayload which has information like:

  • Email
  • First and last name
  • Picture (if provided)

We will also want to save that accessToken so that later on we can save it to a header.

At Auth0, we do not recommend saving user information to localStorage but we will save the authFlag to localStorage. By setting that to true, we are able to save this particular sessions status.

We are also going to build our localLogout function here. This will be taking the information we want to be cleared and clearing it for us, setting things back to null.

// src/auth/service.js

class Auth {
  // auth0 definition...  
  // other methods...

  localLogin(authResult) {
    localStorage.setItem(this.authFlag, true);
    this.idToken = authResult.idToken;
    this.userProfile = authResult.idTokenPayload;
    this.accessToken = authResult.accessToken;
    this.loginCallback({ loggedIn: true });
  }

  localLogout() {
    localStorage.removeItem(this.authFlag);
    this.userProfile = null;
    this.logoutCallback({ loggedIn: false });
  }

}

Accessing the accessToken

Next, we are going to create a function that will hold on to the value of our accessToken so we can use it later on.

// src/auth/service.js
class Auth {
  // auth0 definition...
  // other methods...

  getAccessToken() {
    return this.accessToken;
  }

}

Logging In

When our user clicks on our Log In button or whichever method we choose, we want our login function to fire off and give them a smooth experience. Our login function will be using the auth0 variable we set earlier in the file and use the popup capability.

By running this line in the login function:

this.auth0.popup.authorize()

We are going to be able to activate the cool feature of popup.

We are going to check for two things: Authenticated or Not.

If their user information is passed through, then this function will grant us an accessToken that we will be using. If not, an error will happen and localLogout() will take care of things from there.

We will also be adding in isAuthenticated() to set the localStorage authFlag to "true".

// src/auth/service.js

class Auth {
  // auth0 definition...  
  // other methods...

  login() {
    this.auth0.popup.authorize({}, (err, authResult) => {
      console.log(err, authResult);
      if (err) this.localLogout();
      else {
        this.localLogin(authResult);
        this.accessToken = authResult.accessToken;
      }
    });
  }

  isAuthenticated() {
    return localStorage.getItem(this.authFlag) === "true";
  }

}

Logging Out

When our user clicks on Log Out, let’s clear out the information we just saved and used for authentication. We will also be calling localLogout and get that running.

We will also take them to the logoutUrl that we have set in our config file.

// src/auth/service.js

class Auth {
  // auth0 definition...  
  // other methods...

  logout() {
    this.localLogout();
    this.auth0.logout({
      returnTo: config.logoutUrl,
      clientID: config.clientId
    });
  }

}

Exporting our service.js

We are going to want to use this information freely throughout our application, so we will set auth to new Auth(). By doing this, we are going to be able to use these functions in parent and child components.

We are creating an instance of Auth() that will be hosted in this module and can be used throughout the application by anything that imports it.

Once that is declared, we can export this file. We are good to go!

// src/auth/service.js

// These are the last two lines of the file

const auth = new Auth();

export default auth;

Our Frontend Components

We are going to be using three components for our application. One parent and two children components.

  • App.js
  • TvShows.js
  • Movies.js

The App.js component is already there and created for us. When we ran create-react-app, it built the initial view for us there. We can go into that file and clear it out, we’ll be changing a bunch of it.

App.js

At the top of our file, let’s import the things we will be needing:

// src/App.js

import React, { Component } from "react";
import TvShows from "./components/TvShows.js";
import Movies from "./components/Movies.js";
import auth from "./auth/service";

We have not created two of those imports yet (those are going to be our child components) and we also have our auth/service.js file coming in as well.

Our App component will take care of the Log In and Log Out buttons and which component we want being viewed.

We will want to set our state to:

this.state = { loggedIn: false };

// Full code below

Then, bind some of our functions:

auth.loginCallback = this.loggedIn.bind(this);
auth.logoutCallback = this.loggedOut.bind(this);

// Full code below

Those two functions will toggle the state for us. When loggedIn() is being called, we want the loggedIn state to be changed to true. With the opposite happening for loggedOut(). We will see that code soon.

By using the ES6 Ternary feature, we are going to toggle our Log In and Log Out buttons and also which component to show on the page.

To learn more about ES6 Ternary’s, visit this page.

Once we have that logic in, our component should look like this:

// src/App.js

class App extends Component {
  constructor(props) {
    super(props);

    auth.loginCallback = this.loggedIn.bind(this);
    auth.logoutCallback = this.loggedOut.bind(this);

    this.state = { loggedIn: false };
  }

  loggedIn() {
    this.setState({ loggedIn: true });
  }

  loggedOut() {
    this.setState({ loggedIn: false });
  }

  render() {
    return (
      <div>
        {this.state.loggedIn ? <Movies /> : <TvShows />}
        {this.state.loggedIn ? (
          <button onClick={() => auth.logout()} className="log-in">
            Log Out
          </button>
        ) : (
          <button onClick={() => auth.login()} className="log-in">
            Log In
          </button>
        )}
      </div>
    );
  }
}

export default App;

TV Shows and Movies Components

We are going to have two child components, TvShows.js and Movies.js. In both of these, we are going to be using React Hooks. The component itself will look familiar but some of the functionality will be executed differently.

We are going to make our TvShows.js component first. In the src folder we are going to create another folder named components. Within that folder we are going to make three files:

  • TvShows.js
  • Movies.js
  • styles.css

Using React Hooks

We are using React Hooks in these files. We are going to be importing React features like, useState and useEffect.

Imagine useState is like setState but now it can be used in a functional component; no need to write a class! And useEffect can be used in place of lifecycle methods like componentWillMount.

TV Shows Component

At the top of the file, components/TvShows.js, we are going to import the things we will need:

// src/components/TvShows.js

import React, { useState, useEffect } from "react";
import "./styles.css";

And then start the component:

// src/components/TvShows.js

// Imports here

function TvShows() {

}

To get state set up in our component, we are going to do it a little different:

// src/component/TvShows.js

// Imports here

function TvShows() {
    const [shows, setShows] = useState([]);
}
  1. The variable shows is similar to this.state.
  2. The second, setShows is similar to this.setState.
  3. That third part, useState([]) is going to be our initial state value.

Those are familiar!

Next, we are going to use useEffect instead of componentDidMount. By using fetch, let’s call our API's data and receive the response.

// src/component/TvShows.js

// Imports here

function TvShows() {
    const [shows, setShows] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3005/api/data/tvShows")
      .then(response => response.json())
      .then(data => {
        setShows(data);
      });
  }, []);
}

We are going to view shows.name and shows.airDate but remember, we have a whole list we can choose from:

  1. id
  2. name
  3. airDate
  4. lastShowDate
  5. aCastMember
  6. totalSeasons
  7. totalEpisodes
  8. interestingFact
// src/components/TvShows.js

import React, { useState, useEffect } from "react";
import "./styles.css";

function TvShows() {
  const [shows, setShows] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3005/api/data/tvShows")
      .then(response => response.json())
      .then(data => {
        setShows(data);
      });
  }, []);

  return (
    <div>
      <h1>TV SHOWS</h1>
      {shows.map(shows => (
        <div className="card" key={shows.id}>
          <h3>{shows.name}</h3>
          <h5>{shows.airDate}</h5>
        </div>
      ))}
    </div>
  );
}

export default TvShows;

This information is allowable whether or not the user is authenticated, this information is not "protected".

Movies Component

This component is going to be very similar to the TvShows.js component. The main differences are:

  • Storing an accessToken
  • Importing the /auth/service file.

By using getAccessToken() from /auth/service.js file, we are going to store the accessToken into the header and call the API movies route.

For now, we will view the movies.name and movies.airDate, but we do have a large list to choose from.

After the user logs in, they are going to be taken to this view, which has the TvShows component in the view as well. That will allow for the user to see both components at the same time once authenticated.

// src/component/Movies.js

import React, { useState, useEffect } from "react";
import "./styles.css";
import TvShows from "./TvShows";
import auth from "../auth/service";

function Movies() {
  const [movies, setMovies] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3005/api/data/movies", {
      headers: {
        Authorization: `Bearer ${auth.getAccessToken()}`
      }
    })
      .then(response => response.json())
      .then(data => {
        setMovies(data);
      });
  }, []);

  return (
    <div>
      <h1>MOVIES</h1>
      {movies.map(movies => (
        <div key={movies.id} className="card">
          <h3>{movies.name}</h3>
          <h5>{movies.airDate}</h5>
        </div>
      ))}
      <TvShows />
    </div>
  );
}

export default Movies;

Styles

Let’s make our user interface a little prettier!

/* src/components/styles.css */

.App {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

h3 {
  color: rgb(33, 124, 94);
}

.card {
  border: 2px solid black;
  width: 20%;
  background-color: peachpuff;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  margin: 20px;
  font-size: 12px;
}

.log-in {
  width: 15%;
  color: peachpuff;
  background-color: rgb(33, 124, 94);
  font-size: 25px;
  margin: 20px;
}

h1 {
  color: rgb(33, 124, 94);
  margin: 20px;
}

Our Finished Application

When localhost:3000 is ran in the browser by using npm start, we should see this:

React Popup App unprotected view of Tv Shows

When the user clicks on "Log In" it’ll bring up a popup window without navigating away from the current view.

Auth0 Popup login modal window view

The user will log in with their credentials and the popup will disappear and our Movies component will show. The user will then see a list of movies and tv shows.

Authenticated view of Tv Shows and Movies

The Log In button will change to the Log Out button and if we "inspect" the page, go to the "Network" tab, click on "movies" and in the "Headers" portion, we can scroll down and see that the accessToken is begin stored.

Logout button

Authorization: Bearer "token"

If the user clicked on "Log Out", that would take them to the unauthorized view of just the "TV Shows" and the session will be terminated.

About Auth0

Auth0, a global leader in Identity-as-a-Service (IDaaS), provides thousands of customers in every market sector with the only identity solution they need for their web, mobile, IoT, and internal applications. Its extensible platform seamlessly authenticates and secures more than 2.5 billion logins per month, making it loved by developers and trusted by global enterprises. The company's U.S. headquarters in Bellevue, WA, and additional offices in Buenos Aires, London, Tokyo, and Sydney, support its global customers that are located in 70+ countries.

For more information, visit https://auth0.com or follow @auth0 on Twitter.

Sign up for a free Auth0 account here

Conclusion

The popup method for logging in is handy because it does not redirect the user away from their current screen. The downside to it is a lot of users block popup's, so it may not be the best choice for authentication.

In our project, we were able to implement the popup method and use the new React Hooks. Depending on your project, popup could be the way you go.