See quickstart Menu

How to authenticate users in a React app

This demo uses only a frontend written in React, meaning there’s no backend. This makes the normal auth code flow redundant, which is why a simpler implicit flow was created.

Instead of having a backend server manage a client_secret and perform a complicated handshake, the frontend simply constructs an authorization url, and the redirect after authenticating contains the access_token.

It is worth pointing out the fact that this flow is less secure than the full auth code flow. The access token will be accessible to anyone with access to the browser, rather then being unreachable in the backend. However, when all you have is a frontend app, the implicit flow is required.

This demo will require setting up a OneLogin app and user, and will require cloning the code in this repo.

1. Create application

Login to your OneLogin account as an admin user and navigate the the admin page for applications (at /apps).

Click Add App, find the application called OpenID Connect, and select it.

Enter anything you want as the app title. I’ll call mine “OIDC Implicit ReactJS”. Once you save, it should take you to the edit page for this app:

AppEdit

Next, navigate to the the configuration tab. The Login URL we’ll need is http://localhost:3000/login, and the Redirect URI we’ll need is http://localhost:3000/auth:

AppEditConfig

Keep these in mind, they are important for authentication to work.

Lastly, navigate to the SSO tab. At the very top is the Client ID, wihch has a form of something like this: xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx. Copy that and store it locally.

2. Assign user to App

Since the user creation flow is already very well documented, this demo won’t cover it. Do be sure the test user has these fields filled:

  • Email
  • Username
  • Company
  • Department
  • Title

Also be sure to set the password and record it and the username locally, since we’ll need those to authenticate later. Once the user is created, assign it to the user through the applications tab of the user edit page, hit the (+) button, then select the app you created in step 1. Your user should now have access to use this app.

UserAssignedApp

3. Using the Demo

Once you’ve cloned the demo code from this repo and ran $ npm install, there is one thing we need to do before starting the app.

Navigate to the file src/api/oidcApi.js.

On line #5, change the client_id value to the one you grabbed in step 1. You should now be able to run $ npm run start. The app will now be running on localhost:3000. Navigate to it manually in an incognito tab.

We use an incognito tab for this demo is so we can also stay logged in to our admin user in the non-incognito (cognito?) tab.

We should see this simple page:

Homepage

Before we go further and use the demo, it will be a good idea to get an idea of what will happen, and how the app is designed.

This app uses react-router-dom v4 in order to handle its routes. In the App.js file you will find the router switch where all pages are routed. You might also notice a special component called ProtectedRoute. This special component implements a patter that’s used to create restricted routes. Currently, the profile route is restricted through the loggedIn prop, since it should only be accessible when authenticated.

The Login button navigates to the /login page, which does a few things. First, it checks to see if a state and nonce value exist in the redux store. If not, they’re created though a redux action, then the component rerenders. Now with the cryptographic state and nonce values created, the meat of this demo is run:

oidcApi.beginAuth({ state, nonce })

beginAuth is defined in the oidcApi.js file. Here, the important params are defined:

  • authority - The server which acts as the OIDC provider.
  • client_id - The id unique to the app you created in step 1.
  • redirect_uri - Once the authentication flow is finished on the OneLogin side, it needs to know where to redirect us. This is the same value that would have been set in the configuration tab in step 1.
  • response_type - The flow that’s being initialized. Since we’re using the implicit flow for this app, it’s been set to id_token token. This means that instead of an authorization grant, the redirect uri will contain the id_token and the access_token
  • scope - The OIDC scope for the type of information encoded in the id_token (and what the access token can be used to retrieve). Here, it’s been set to openid (which is necessary for OIDC) and profile so that we can retrieve the user’s profile information
  • state and nonce - Cryptographic strings we generate. They are used prevent replay attacks and CSRF attacks.

All beginAuth does is construct the authentication url and set it as the new window location. A console.log has been left so that we can see it in the devtools console, though you will need to enable persist logs to see it.

Now that we know what’s happening, click the Login button. The page should take a second to load, then redirect you to the login page for your OneLogin account.

Sign in using the credentials for the user you created in step 2.

Once the authentication is complete, you will be redirected to localhost:3000/auth along with hash params containing your id_token and access_token.

You will only be here for a fraction of a second as you are then redirected to the /profile page. To take a look why, dig in to the authPage.js file.

On load, the component looks for the presense of a profile prop retrieved from the redux store. If it doesn’t exist, that implies that authentication is beginning now. The tokens exist in the url hash, so the parse function from the query-string library is used to extract them. They are sent via redux action into the store, which will cause this component to update. It is worth noting here that if the oidc auth flow failed, the error will be caught here, since the error type and message would be in the url hash instead of the tokens.

One of the actions that this component fires is receiveProfileData, where it passes the data extracted from the id_token. Since the token is encoded as a JWT, we use jwtDecode to grab the payload and pass that to our store.

Once this page reloads from the new profile prop, it redirects us to the /profile page. Now that the profile data has been extracted from the id_token, this page can be populated.

4. Using the Demo pt. 2

OneLogin provides users with a portal to access all apps. This is no different for an OIDC app. If you remember from step 1, in the application’s configuration tab, we set the Login URL value to http://localhost:3000/login. This was the path we entered in step 3 in order to start the authentication flow. Well when the Login URL is set, this creates a tile in the portal that navigates there directly.

To show this off, open a new incognito browser so that no sessions remain from the last demo. Navigate directly to your OneLogin account and login directly as the test user. In the portal, the tile should be visible. Click it, and see the page redirect to http://localhost:3000/login. The flow will start again as before, and since we already logged in as the test user in OneLogin, the existing session means we won’t need to authenticate again. The profile should load within a few seconds.

Extra:

In this very simple demo, the tokens are not saved anywhere. In order to persist this session, all that’s needed is to store the access_token in the local storage, then search for it on app load. The logout functionality currently only clears the redux store of the token and profile data, but in a fully fledged app, it will also be necessary to clear the local storage of the token.

Additional Resources