I'm a security newb trying to find out how to secure my SPAs, and am totally lost in the forest of RFCs, BCPs, drafts and blog posts. If possible, I'd like to serve my SPAs statically from a cdn. At first I was uplifted by this article from Okta explaining why and how to replace the implicit flow with the code+PKCE flow. This BCP draft also seemed promising, however (emphasis mine)...
The code in the browser then initiates the authorization code flow with the PKCE extension (described in Section 7) (B) above, and obtains an access token via a POST request (C). The JavaScript app is then responsible for storing the access token securely using appropriate browser APIs.
Storing the access token securely using appropriate browser APIs – is this even possible?
If I understand it correctly, the code+PKCE flow for public clients is only better than the implicit flow because it doesn't put the access token in the address bar and history, but it still has the drawback that the tokens need to be stored in localstorage/sessionstorage/non-httponly-cookies.
Right?
So I'd still be vulnerable to XSS and evil browser extensions.
I'm considering two alternatives:
- Use a confidential client backend that serves my public SPA clients. Confidential client receives tokens in back-channel, and sets them as HttpOnly-cookies. Public clients can now talk to protected resources without using confidential client as proxy on every request.
- Storing tokens on confindential client backend so that they never reach the public clients. Public clients instead are given a session cookie, referencing the tokens. Public clients will need to use confidential client as proxy on every request to protected resources.
Storing tokens in HttpOnly-cookies seems a lot better than storing them in localstorage/sessionstorage, but comes at cost of serving SPA from confidential client instead of directly from cdn.
Any advice on these approaches? Any literature that explains these approaches (or another canoncial way) in more depth?