Improved SPA – Coding Key Points


In our last post we provided an Improved SPA Overview. We will look at the key technical points below.

Unobtrusive OAuth Code

Our code layout has been updated to look as follows, since the important UI code is our application logic, which we want to keep clean and evolve often based on business requirements.

Plumbing code related to OAuth or error handling will be written once, and then will not change. So we’ll move it to a subfolder and can forget about it once it is finished.

UI Location and Navigation

In a Single Page Application the behavior should be roughly as follows, and we will see that OAuth code can integrate nicely with these concerns:

  • Download the web page when the user first accesses the app
  • Use REST based URLs for locations within the Web UI
  • Updating a URL raises a router event which then selects the view
  • A view gets data via fast Ajax requests and updates the DOM

SPA Routing

You can use either path or hash based routing in your SPA, and I chose to use hash based routing because it was simplest to implement:

  • I want the Server Side of my Code Sample to have zero logic
  • SEO is not a factor I care about, since the UI is behind a ‘Login Wall’

A Real World SPA would typically use a framework such as Angular or React, in which case you will have more powerful options for areas like Routing.

UI Data / Navigation Changes

Our sample shows how the implicit flow deals elegantly with SPA locations across login redirects. Consider the following change to the hash location, in listView.js, when a startup is clicked in the list view.

The hash change event is handled in the main app.js file, and executeView acts as a router that works out which view to load:

We use the OIDC Client library in authenticator.js to store application state during an OAuth redirect and then maintain the user’s UI location upon return from login:

API Claims Caching

Our Claims Middleware has been extended to cache the results of Introspection. The ClaimsHandler class represents an outline of processing:

Lower level details are deferred to the Authenticator class, which uses the OpenId-Client library to take care of these plumbing details:

  • Metadata Lookup to get Endpoints
  • Token Introspection to read Token Claims
  • An API call to read Central User Data
  • A placeholder that could read Product Specific User Data

The ClaimsCache class is a simple wrapper around the NodeJs MemoryCache, which supports caching an item until the access token’s expiry time:

API Claims Caching and Reliability

There are 2 important reliability concerns that you will need to cover in this type of solution:

Thread Safe Memory Cache UTC Based Timestamps
NodeJS has a single threaded execution model, where an Event Loop executes while asynchronous I/O is completing Both the Javascript Date object and the ‘exp’ field in Access Tokens are UTC based

If you are working on a .Net based API then I would recommend using the Identity Server Implementation or basing your solution on it.

UI User Info Lookup

Our UI gets its User Info by calling its API, and this data could contain whatever is needed by our SPA:

This is a little contrived in our case, since our Code Sample only needs to display who is logged in:

Error Handling Classes

In code we have added Error Entities in both the API and UI – and constructors ensure that all fields have good defaults:

A higher level ErrorHandler class is used by the rest of the application. It is responsible for creating error instances and populating them with details such as those from OAuth error responses:

The ErrorHandler class deals with error logic. The API error handler logs technical details server side, then returns a less detailed error to the UI:

Where Are We?

Our Code Sample has been updated to handle Claims in both the UI and API. Business logic is cleanly separated from plumbing code and it is easy to extend either.

Next Steps

  • We will discuss Token Renewal, which is an essential feature in order to avoid redirecting the user every 30 minutes
  • Return to the Index Page to see all topics in a logical sequence