How to Implement Authentication For Your React App
A good authentication system is a crucial ingredient for building modern apps, and also one of the most common challenges that app developers face.
There are several contributing factors that make designing good authentication flows a challenge. For instance, OAuth flows work ever slightly so differently across providers. And handling a client-side only flow is quite different than a middleware based flow. Further, there are different kinds of authentication flows: sometimes, you may want a passwordless authentication and other times, you may want an old-school username and password based flow. And once you have implemented an auth flow, handling user authorizations is an altogether different challenge.
In this tutorial, I will walk through building an Authentication flow for a client-only React app with a very simple authorization rule:
- Display a database connected UI view that is only visible to logged in users. I will be using ReactiveSearch for building this quickly, but the walk through should apply the same for any React view.
- I will be using Auth0 to implement the authentication flow that will support logging in either via an e-mail / password mechanism or a Google OAuth login.
Here is how the final app will look like:
When is it a good Idea to Implement Authentication
Before we dive deeper, it is always a good exercise to think if your app really needs an authentication flow.
If your site or app data is public, and you don’t care about resource abuse and DoS attacks in particular, it is fine to do away with an authentication flow.
In all other scenarios, you would at least want a part of your UI view to implement authentication. May be, it is the admin dashboard or may be it is the ability to post data or perhaps it is a mechanism to associate a public profile for encouraging sane user conversations.
Basics: An Authentication Token
I want to start by introducing the idea of an authentication token. An authentication (or access) token is a piece of data sent by a server to a client when the user authenticates herself or himself with the correct credentials.
A token improves the future accessibility of the app where the user doesn’t have to go through the authentication flow every single time s/he is trying to do something with the app. A good practice is to apply a time limit (like 14 days or 30 days) on a token so as to get an optimal trade-off between ease of use and security. Depending on what kind of app you are building, this can be as low as a few minutes to sometimes as long as several months.
OAuth tokens are used by a lot of apps for authentication. When we signup or login via Auth0 we’ll receive an id_token (containing user’s profile information)and an access_token (a credential that can be used by a client to access an API). We’ll store these tokens on our client for future use.
The Set Up
We will start by signing up for the Auth0 service, and create a new client for our tutorial app. We’ll need the Client ID and Domain in order to configure auth0 in our app. You can get these from your Auth0 dashboard.
We’ll also need to add a callback URL in the Allowed Callback URLs section since after authentication the client is redirected back to the callback URL with the token info which we’ll later parse for our use. You can add the following to the Allowed Callback URLs:
http://localhost:3000/callback // our app will run on 3000 port
http://yourhosteddomain:PORT // add if you are hosting it live
Building the React App
This is how our final app’s file structure will look.
├── public
│ ├── 404.html // used for handling routes on gh-pages
│ └── index.html // root index html
└── src
├── App.js // reactivesearch example app
├── auth.js // main authentication service for our app
├── Callback.js // displayed when auth0 redirects to callback URL
├── history.js // used by auth service and react-router
├── Home.js // the root component for final app
├── index.css // styles
├── index.js // renders the final app using ReactDOM
└── Routes.js // uses react-router to handle all the app routes
1. Initializing the React project
I’m using create-react-app to set up the project easily without manually adding configurations:
npm install -g create-react-app // if you don't already have itcreate-react-app reactivesearch-auth0-example
After the dependencies are installed you can cd
into the reactivesearch-auth0-example
directory and run:
npm start
which should start the project at http://localhost:3000/
.
2. Adding ReactiveSearch: Our UI View
Now that we have the basic react app initialized, lets create our UI view. We will use ReactiveSearch for building a quick UI.
npm install @appbaseio/reactivesearch
After adding the reactivesearch dependency we have to include reactivesearch styles into the build system. A simple way to do is to import
the reactivesearch styles in our root project file src/index.js
. This will inform webpack to bundle the CSS for reactivesearch along with the project.
import '@appbaseio/reactivesearch/dist/css/style.min.css';
We will use materialize as the style framework and add the CDN link directly in the /public/index.html
file.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css">
We can import
some basic components in /src/App.js
as:
import {
ReactiveBase,
CategorySearch,
RatingsFilter,
ResultCard
} from '@appbaseio/reactivesearch';
3. Getting started with ReactiveSearch
To start with, we will create a simple application with a few components. Alternatively, you can also follow these links to create your own app with your data:
(i) Creating an app and importing data
(ii) Understanding the base component for reactivesearch
The sample app we’re building is taken from the reactivesearch quick start guide. Lets modify the src/App.js
file in our project directory. The final app should look like this:
If you have followed along thus far, the project should now look like this:
Now that our React app is up and running with a live data view, lets proceed with adding authentication. You can also get the final code from the project repository.
Adding Authentication
When built, our app’s authentication flow will look like this:
Your App → Auth0 login → Auth0 authenticates user → Auth0 redirects to callback URL → Your App with the token
You can also check out the following screen-cast to see it in action:
We’ll be following the Auth0 quick start guide for adding authentication to our React app, with some modifications to suit our app’s purpose.
1. Adding Auth0 and React Router
You can install auth0-js dependency by running:
npm install auth0-js
We’ll also need react-router-dom to handle the routes:
npm install react-router-dom
2. Creating An Authentication Service
We’ll be creating a history service to easily manipulate browser history. We’ll use history package which will be used by the authentication service and react-router.
createHistory
can also accept an object containing basename
property. This can be helpful if you want to set a base URL for all the routes. We’ll use the same history
with react-router to correctly handle routes on our app deployed at github pages. In development
we don’t need any such base URL.
We’ll be adding an authentication service to use auth0-js. This will look like:
Here’s how the authentication service works in a nutshell:
- auth0 is a new instance of the Auth0 client. The
redirectUri
is set tolocalhost
indevelopment
and to the project domain otherwise (which is github pages for this project) - login method calls the authorize method on the auth0 client which will open up the Auth0 login screen.
- handleAuthentication method calls the parseHash method on the auth0 client. We’ll call this after successful authentication to read the token information from the page URL Auth0 redirects to after authentication.
- setSession stores the information about access_token, id_token and expires_at in local storage so we can use it later without going through the authentication process again.
- logout clears all the session information about the tokens and expiry time from the local storage. After logout the user will need to authenticate (login) again to get the tokens.
- isAuthenticated checks if the token is past expiry time (set at the time of login). If the token is expired, the user will have to authenticate again and get a fresh set of tokens.
3. Adding a Callback component
We’ll need a Callback component to handle redirects from auth0. This component will show a loading message till the session is set up. After this the user will be redirected to the UI view by the authentication service.
4. Adding a Home component
App component (the one we created previously) only if the user is logged in using our authentication.
- We will show an example view to a non-authenticated user with an option to login or signup.
The Home component calls the isAuthenticated method in authentication service /src/auth.js
and displays the App component only if the user is authenticated.
5. Setting up app routes and handling authentication
We’ll be using react-router-dom
from react-router to handle all our routes. The handleAuthentication
method is called after Callback component is rendered and parses the URL hash from the page auth0 redirects to after authentication. After we extract the token information we will direct the user to the Home component.
The Router component takes the same history object that we used earlier in /src/auth.js
. We’ve set simple routes:
- / route is the main route which renders the Home component
- /home route also renders the Home component. This is the same route the user is redirected to after authentication.
- /callback route renders the Callback component and runs the handleAuthentication function to parse the token information from Auth0’s redirect URL.
All the components rendered by react-router are also passed the auth object as a prop and all the other props by Route as {…props}
. We don’t need the additional props from react-router in this tutorial but they’re very helpful when you want to interact with the history.
We have to also update the root component in /src/index.js
to render the Routes component instead of the App component.
That’s it! Our app is now ready with an authentication flow. You can now have fun with tweaking the UI view and adding more complex authorization views.
Further Reading
Check out the followup post on adding authentication to your APIs:
Summary
We walked through building a client-side only authentication flow for a React app. In the process, we learnt about the idea of access tokens and how they form the centerpiece of any authentication flow, and built a flow where we support username / password and Google based logins.
If you liked this post, you will love our deep dive posts for building data-driven Reactive Apps: