The Basic SPA Technical Workflow explained how our Code Sample uses OAuth messages. Next let’s look at the code behind the workflow.
Our Coding Goals will be to try to keep code Technically Simple, Business Focused and Reliable, with small modules that are Easy to Change later.
OAuth SPA – Page Workflow
The main UI workflow is implemented as follows in the main app.js source file. Whenever the SPA loads, it first sets OAuth Configuration and then handles Authorization Responses if needed.
When requestPageData is called, Ajax calls will be made to our API, and if there is no access token yet we will need to short circuit the page execution and start a Login Redirect.
OAuth Token processing increases the number of callbacks, so we will use ES6 promises to make our code more readable.
Token Handling adds SPA Complexity
When moving your Web UI to be token based, you will need to update your SPA page flow similar to the above, and this can be quite intrusive.
OAuth SPA – OIDC User Manager
I have created a basic adapter class called an ‘Authenticator’, to do ‘OAuth Stuff’ and reduce code in other parts of the application:
We are using the OIDC Client’s UserManager class, which does all of the Implicit Flow work for us.
OAuth SPA – Reliable API Calls
As discussed earlier, we will call APIs by implementing this behaviour:
- If an API call fails with a 401
- Then get a new access token – once only
- And try the API call again – once only
I have coded this in an HttpClient class, with a parameterized method used for all API requests:
On the first API call, getCurrentAccessToken returns null and we intentionally call the API without a token, so that we get a 401 response and can get retry handling coded early – we will optimize this later.
As can be seen, this code is tricky to write cleanly, even with promises, due to all of the async completion.
OAuth SPA – Triggering Logins
Our sample triggers the Initial Login on the SPA’s first call to the API, though this could be done on page load if you prefer.
Login requests are closely linked to API calls however, since a login needs to be triggered when an API returns a 401 response due to an expired token.
OAuth SPA – Login Handling
The login redirect and response handling are very easy to implement via the OIDC UserManager class. During a login we short circuit the SPA Page Workflow by rejecting the promise with a ‘login required’ result.
OAuth SPA – Token Storage
After login, the OIDC Client stores returned tokens for the user in HTML5 session storage. We can then easily get the access token for our API calls like this:
The full details from HTML5 storage can be seen if we look at the OIDC Client’s ‘User’ class:
OAuth SPA – Error Handling Code
All errors go via an error handler class though it is quite primitive. In the next sample we will improve UI Error Displays further.
When working with OAuth technologies there is a lot of scope for configuration / infrastructure errors even if your code is 100% perfect. So an early focus on error handling and logging is recommended.
Building ES6 and Supporting IE
Internet Explorer is a key browser for many corporate environments so we need to ensure that our ES6 code is transpiled to code that works in IE.
- The build starts at our entry point file, app.js
- The build recurses over the app.js ES6 dependencies
- Our ES6 modules are in root (.) and utils folders
- 3rd party ES6 modules are under node_modules
- Our code builds into an SPA bundle file
- Third party code builds into a Vendor bundle file
- Babel ‘polyfills’ ES6 into IE compliant code
The end result looks as follows when ‘npm start’ or ‘npm run build’ are executed. Note also that the latter command uses ‘webpack -p’ to produce minified output:
Using OIDC Client from an ES6 Project
Our sample references OIDC Client classes via the packages.json file, which the npm tool reads, so that ‘npm install’ downloads OIDC source code:
The oidc-client folder is located under the node_modules folder, and it also depends on a large ‘jsrsasign’ cryptographic library:
When an npm build command is run against our project, webpack is invoked and jsrsasign gets included in the vendor bundle file.
If you are not using ES6 features yet you can just take the bundle file from the dist folder and include it in your index.html file:
In this case you can reference OIDC classes within an Oidc namespace and translate to an older syntax if you prefer:
OAuth API – Server.js
The Server.js file primarily responds to API requests but also serves as a primitive Web Server implemented using Node JS / Express.
We use ES6 classes here as well but the import syntax is a little different, and we use a few Node JS dependencies:
We use the HTTP server to serve up web content, though you could use IIS or Apache if you prefer:
OAuth API – Claims Middleware
In our API, all incoming calls first go through some middleware that is responsible for validating the access token and reading its claims.
Only API requests that contain valid tokens get routed to the next function, which returns does the API’s ‘business logic’ and returns personalized data.
OAuth API – Claims Handling
Processing is again done by an ‘Authenticator’ class, which is an entry point for OAuth related processing. This class uses the OpenId-Client Library to make the Introspection request.
The code reads the introspection response to verify that the token has not expired, then reads token claims and returns them to the API logic:
A real corporate API would use claims for authorization, and to filter personalized data for the user in the token.
Where Are We?
We have an initial working setup with a good separation of concerns. We are using a good 3rd party library that is doing some work for us.
We should really be using id tokens as well as access tokens. Also we are missing essential features and our solution is not optimal.