More Single Page Application (SPA) and OAuth2 Thoughts

Robert Broeckelmann
4 min readSep 1, 2019
dmytrok / more

This post continues where “SECURELY USING THE OIDC AUTHORIZATION CODE FLOW AND A PUBLIC CLIENT WITH SINGLE PAGE APPLICATIONS” left off on the topic of securing Single Page Applications (SPAs). That post describes an architecture where the SPA running in the browser (User Agent)is acting as the OAuth2 Client. That is my preferred approach to using OAuth2 with SPAs, but it isn’t the only way of doing it. Let’s assume that the OAuth2 Authorization Code Grant or OIDC Authentication Flow is still used. One could alternatively make a server-side component (web server, application server, API Gateway) be the OAuth2 Client. In the original scenario, the SPA is an OAuth2 Public Client; in this alternate architecture, the server-side component can be an OAuth2 Confidential Client.

An OAuth2 Confidential Client is capable of keeping its client secret secure. Generally, an application running on a device (desktop, laptop, tablet, smart phone, etc) is not capable of securely storing its client secret. Well, not without a lot of work that is far outside the level-of-effort most development shops are prepared to go through.

If we change the architecture slightly to have the OAuth2 Client be a server-side component that implements the Redirect URI (registered with the OAuth2 Provider) for the application, then we can use a Confidential Client. This Redirect URI does not require any type of authentication to access; you just need a valid authorization code that was obtained via a successful authentication against the OAuth2 Provider(Authorization Endpoint). The server-side component will make the second-call in the OAuth2/OIDC protocol to the Token Endpoint. So, the OAuth2 Authorization Code is exchanged for an OIDC ID Token, OAuth2 Access Token, and optionally an OAuth2 Refresh Token.

It should be noted that this is effectively what the original three-legged OAuth pattern was and is what the original OAuth2 Authorization Code Grant (OIDC Authorization Code Flow) was envisioned to be.

If my preferred architecture is used, then an API Gateway is handling the application’s authentication and authorization of API calls that are made to the server. I’ve had the API Gateway act as the OAuth2 Confidential Client for SPAs before. This can work so long as you trust the API Gateway and have full administrative control over it (or, at least, your organizaton does). We’ll assume that the API Gateway has a mechanism to securely store the client credential and that it is actually being used.

Even without an API Gateway, you can still have an application server or other dedicated server-side component act as the OAuth2 Confidential Client that makes the call to the OAuth2 Provider Token Endpoint. Obviously, that application server needs to be properly secured; that is beyond the scope of this blog post.

The response from the Redirect URI could either be:

  • a session-tracking cookie (or other mechanism) that allows the SPA to access other APIs as part of a known security session.
  • the tokens the OAuth2Token Endpoint returned.
  • A JSON Web Token (JWT) or similar mechanism could be generated that describes all the relevant details of the authenticated user,application, and what permissions have been granted.

If you use the first option, then the API layer (API Gateway or otherwise) must track security sessions. This violates the RESTful principal of statelessness and is a lot more complicated than other approaches I’ve discussed extensively.

If you use the second option, the API security layer is simpler (and stateless). However, one must stop and ask what has actually been accomplished in terms of security by adding the confidential client with a client secret as described? Since the Redirect URI cannot require authentication and the end result is that you are going to obtain either a session-tracking cookie or an access token that lets you access the APIs, the end result is the same as with the public client scenario described in the diagram above. If you use this approach, make sure that you enable Cross-Origin Resource Sharing (CORS) on the Redirect URI to further ensure that only your SPA code can access the application — this isn’t fool proof.

If you use the third option, then:

  • the IdP-issued tokens remain securely on the server-side.
  • the server-side doesn’t have to track security sessions for each user.

So, it doesn’t have the issues of the first two options, but it involves issuing a token that is used by the SPA in much the same way that it would use an OAuth2 Access Token. In fact, if we put a little more structure around this pattern, the server-side component is effectively acting as an IdP for the SPA with a federation relationship to the original IdP. In its simplest form, you’ve constructed a proxy between the SPA and its IdP.

I’ve seen all three of these patterns used. All three options can be implemented securely, if done properly. Each one can add security benefits to your application at the expense of architectural complexity.

You can see an example of this architecture in “Three-Legged OAuth2 from Single-Page Applications: A Use Case for a Function-as-a-Service” by Chris Concannon. I used to work with him and provided feedback on a couple of the early versions of that article.

Or, you can just use the SPA with an OAuth2 Public Client pattern that I described in the last post.

Image: dmytrok / more

--

--

Robert Broeckelmann

My focus within Information Technology is API Management, Integration, and Identity–especially where these three intersect.