React Simple Auth: React + Redux + OAuth 2.0

I recently started working on a React project and was looking to add authentication support to it. As a long time Ember developer I expected there to be a standard community solution similar to ember-simple-auth that I would be able to install and extend for my use cases. However, as I started searching on community areas, github, and npm and I didn’t find anything quite like what I was looking for. I ended up building my own solution based on the principles behind Ember-Simple-Auth hence the name of React-Simple-Auth. However, Ember-Simple-Auth is full fledged production ready ember-addon where as mine is just an example of what could be. It’s not exactly apples to apples here, but it’s a great name. If it turns out that it works well I imagine it could be separated out into an standalone npm package. In this article I’ll focus on explaining the details of the end-to-end solution and hope to provide a helpful resource since I saw gaps in the existing resources out there. Hopefully by walking through all the requests, methods calls and integration with react from the user clicking login to sending a request using an access token this will give you ideas about how you could use this in your own applications or build your own similar service.

Below I describe the notable resources I found and reasons I don’t think they’re sufficient. If you’d prefer to skip to the explanation of React-Simple-Auth go to the next section or go straight to the code:
https://github.com/mattmazzola/react-simple-auth-sample

Resources:

  • https://auth0.com/blog/secure-your-react-and-redux-app-with-jwt-authentication/
    This seemed fairly thorough, but shows manually entering user name and password and which is not very applicable. Most sites will use federated login from a third party as you don’t want to be in the business of handling user credentials. It was also a tutorial specifically written for the Auth0 service and used: https://www.npmjs.com/package/auth0-lock for most of the critical parts which is what I was interested in.
  • https://www.youtube.com/watch?v=DKUimZvRyqg
    Perhaps this would have been good, but most of the video was focused on the back-end service side of managing users / secrets / database logic and again my goal is to use federated login through a third-party like Facebook, Microsoft, Twitter, etc and use implicit grant flow without any server so a lot of this did not apply
  • https://www.npmjs.com/package/redux-auth-wrapper
    The library seems quite good, but it is not a complete solution. It’s only concerned with restricting route access based on Redux state. It’s good for libraries to be focused, but remember the point of this search was to get a complete working auth solution. Someone would have to take the knowledge from these docs and go search some more to find out how to make this work with their actual authentication. Given my experience with ember-simple-auth I was able to bridge the gap.

Given how large the react community is I have to say I was unimpressed. In my opinion authentication is a standard requirement for apps and also not something you want to write yourself. It would definitely ease the barrier to entry if there was more established pattern for this fundamental part of applications where a bunch of experts within the community can share ideas and ensure it’s done correctly and help increase brand quality of React. When you write a custom component it might have slow performance or not look appealing, but when you write custom user authentication you might be leaking tokens or personal information which is not something you get to recover from or fix later. I’m sure the library I’ve written has holes in it which is why I’m hesitant to convert it into a stand-alone package, but hopefully it will at least demonstrate the ideas and serve the basic requirements. Maybe some expert will take it further or maybe someone will enlighten me to an existing solution that does all of this already…

React-Simple-Auth: How does it work?

Route Mixins:

Particularly AuthenticatedRouteMixin, UnauthenticatedRouteMixin from ember-simple-auth will be re-created using react-auth-wrapper’s connectedRouterRedirect

Example:

Providers:

  • buildUrl: Build the /authorize url
  • open: Opens the window, waits for it to close, and verifies the authentication via inspecting redirect url
  • fetch: Given the existing auth data determine if it is still valid and return it or fetch new data if possible

In this case, I agree with the purpose, but I don’t like implementation in Torii. In attempt to make the programming model declarative where you only configure a few required query parameters, client id, and other such values and they compute things behind the scenes it becomes very difficult to understand what to change to get your desired results. This mostly because the default implementation is hidden in base classes and this adds more complexity than seems necessary. Due to all of this, I found it easier to write completely custom providers to handle latest OpenID connect protocol for Facebook or Microsoft instead trying to extend those included in the torii package. Hopefully I can avoid all of that and just use some basic functions that the provider will implement.
(Side note about some framework philosophy: I find React philosophy of having the minimal API surface area is very refreshing and one I hope to get better at applying in my own work. Having these design constraints of implementing a solution using a restricted set of tools / concepts generally leads to simpler designs.)

Here is the interface the providers must implement:

As you can see it’s up to the provider to define what the session is and means to the application. For most cases you simply need a user id, user name, and access token, but it’s completely arbitrary. This will make sense later when we go into code

Service (Ember-Simple-Auth + Torii):

  • Manages opening login window and polls window status
  • Abstracts the storage mechanism (localStorage in our case)
    (Side note: I think using sessionStorage might be more secure, but localStorage allows the session to persist if the user closes the browser / tab saves them from having to login again. Hopefully someone can clarify the pros/cons here.)
  • Provide known redirect page which will communicate back to parent page through the shared storage and known storage key
  • Manage session lifecycle in storage and pass session to the provider.
    (Remember the service doesn’t know what the session is but the provider does. However the service knows how to access the session from storage but the provider doesn’t. These two work together but keep responsibilities isolated)

Here is the interface for the react-simple-auth service:

Understanding the flow:

I think this is best explained with a sequence diagram. First we’ll show how you would integrate React Simple Auth into your app. This assumes you already have a provider configured and understand the fundamentals of react + redux and dispatching actions.

Image for post
Image for post
Single awaitable call to login.

Notice there is a single call: await authService.acquireTokenAsync(provider) that returns the session object! I can’t overstate how amazing this is. Once you have the session you then simply use redux as you would normally and invoke the action dispatcher to login. The reducer updates the global state and then this allows the connectedRouterRedirect to re-evaluate and now the authenticatedSelector is true and the user is redirected to the authenticated part of the application.

The whole login process happens on a single awaitable call.

This is extremely nice for SPA based applications because the alternative is a full redirect to the auth page where the user leaves your app and is then redirected back. This means your app is loaded twice, but once with special urls which mean you likely have logic within the router or app initialization to parse the URL hash. It’s very ugly process.

I hope this seems simple from the surface. Behind the scenes this is actually what’s going on:

Image for post
Image for post
Login flow with internals of service, provider, etc

Sorry for the low-resolution diagram. I didn’t pay for the premium tier :( If it’s too blurry you can always look at the source code.

Yes, it has way more lines, but hopefully it is not overwhelming. Let’s step through it together. When you call acquireTokenAsync the service asks the provider to build the authorize url, then opens a window at that url and named using a unique key. The user enters credentials, and the OAuth flow redirects back to our custom redirect.html. The redirect.html simply takes the current window.location which should have the access tokens in the hash and saves it in localStroage at a key which was set as the window.name and is known by parent window. All of these ideas about session management and polling the window are from me looking at how Torii works so please give all the credit to them. I merely re-wrote it in a more compact manner which isn’t specific to Ember apps and am explaining it here for everyone to see. Also to be fair, their libraries are much more robust and supports many more options such as using an embedded iframe and controlling the window size / position etc which are not exposed in the simpler service I wrote.

At this point the user has successfully logged in and can navigate around the restricted parts of our application; however, there are more scenarios we need to cover to be a full auth solution. Read on to find out how we solve those.

Scenario 1: Restoring Session

Image for post
Image for post
Session Restore

Again when the initial state of the User Reducer is being setup on application start it first asks the auth service to restore the session. The service attempts to load the session from storage and asks the provider to validate the session. If invalid, the storage is cleared and the call returns undefined, if successful it will return the session object.

Note: A possible area of improvement here is to allow this restoreSession call to be asynchronous. Currently this has to be synchronous because it runs within the reducer and the reducer is synchronous, but if I could find a way to make it asynchronous it would allow the ability to request a new token if the existing one is expired without throwing away all the data and requiring the user to login. Given I am using Microsoft implicit login flow which does not allow/issue refresh tokens there isn’t a way to acquire new tokens that I know of so making this request async still would not help much. However, I don’t believe all providers have this restriction and it would be nice feature to support.

Scenario 2: Make async request using access token

Image for post
Image for post

Here the service asks the provider to get an access token for a specific resource. With the token then you continue to the normal flow of using redux-thunk to make asynchronous request and once the promise is resolved, dispatch the action such as FETCH_DATA_FULLFILLED which will set the state.

Note: If you noticed the extra resourceId parameter and wondered what it was for. This is a Microsoft provider specific piece of data that has leaked into the implementation of the service. You can safely ignore if you don’t need it. Even in my sample code, my provider simply ignores this and returns the same access token since I’m not familiar how to actually use this with the v2 API. Maybe I will find a way to make this better or remove it all together, but if you read on below I explain what it was intended for. Skip the next section if you aren’t using Microsoft stuff.

ResourceId

Scenario 3: Sign out

Image for post
Image for post
Sign Out

Notice here we first dispatch the action to logout which resets the state back to unauthenticated state and destroys the current session in storage; however, we go even further and redirect the entire window to the sign out url.

I think it might be OK to avoid the page redirect in some application cases, because this will invalidate tokens for all applications not just the current one they’re using and this might not be what your users prefer; however, I did it mostly for learning.

Note: The sign out page takes a redirect url and is suppose to redirect the user back to your application, but for some reason this isn’t working for my application and it kind of leaves the user in awkward AAD landing page asking them to close their browser which is a bit of an eye sore. If any one knows the issue here, please let me know. I was wondering if maybe that sign out page only fully redirects for other types of auth flows using cookies or something.

Video Demonstration:

Conclusion:

Update 2017/10/14:

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store