In this post, I try to explain some of the differences between a single page application and a server rendered application and why the application types have different threat models.
What is an Single Page Application (SPA)?
What is a server rendered web application?
A server rendered application renders HTML on the server and sends the HTML to the client browser. The routing is done on the server. This means more of the application, compared to the SPA, is run in a trusted zone. OpenID Connect Code flow, or the OpenID Connect Hybrid flow could be used for authentication and authorization and cookies are used to persist the session.
First difference is the amount of code run and used in the public zone
More code is run in the public zone in a SPA application, which means a larger part of the application is opened for attack. The UI usually implements some type of authorization switches, and this is all done in the browser. If this is implemented without the security protections, it could be attacked, but on a server rendered app, only the result is returned to the public zone. In a server rendered app, the authorization and the authentication is done on the server.
Securing the SPA application using cookies
When the SPA application uses an API on the same domain, with LAX or Strict Same site cookies and HTTP only, then cookie-based authentication can be used. Cookies are used to persist the session, like the server rendered application. Anti-Forgery cookies would be required and also a good CSP and XSS protection. Both the Anti-Forgery cookies and the Same Site cookie help prevent cross site attacks.
The SPA application does not handle tokens, and does not need to save these to a local storage , or session storage. The SPA can only use APIs in the same domain, and all APIs would need cross site protection. The requests are sent with the cookie which can be used on the server. This is only slightly worse than the server rendered application, with the only difference being the amount of code run in the public zone, meaning a greater risk for security mistakes. The public API is also required for a SPA. The server rendered application does not need a public API.
Securing the SPA using OIDC code flow with PCKE
If the SPA uses APIs from a different domain, then it needs access tokens, and also needs to manage these in the browser. This has disadvantages compared to the server rendered web application. Nothing what is done in the browser can be trusted.
The user can authenticate and authorize using the OpenID Connect code flow with PKCE. See these two specifications for details:
This returns an access token, or a reference to an access token, and a JWT id_token. When validated and all is ok, the SPA needs to persist the tokens somewhere, for later usage. This is usually saved to local storage or session storage in the browser. The SPA application sends the access token with web socket requests, or HTTP API requests. The access token is being managed and used in the public zone, so greater risk exists, that the token could be leaked. This can be reduced by using reference tokens to the access tokens, and also by keeping the life span of the token short. Sending the token in the URL should be avoided where possible and the tokens should be handled with care. For example, if you use APIs from different hosts, the incorrect token should not be automatically sent.
So is a SPA less secure than a server rendered web application?