Consider:
- An SPA or a statically generated JAMStack website (written in Vue/Nuxt).
- A REST API (Node Express) running in the cloud.
The website is being served through a CDN.
We are required to add a Social Sign-On functionality for user convenience. The API already authenticates users with a credential based JWT (access/refresh) token flow. Social sign-on will be in addition to that.
We have selected the Authorization Code Flow as the mechanism for this implementation (instead of PKCE, because we want the SPA to use the API's access tokens instead of the authorization provider's).
Our flow is as follows:
The user clicks on a button and is directed to the Identity Provider's (in our case, Google) website to prove its identity. We provide a randomly generated state token as a request parameter to prevent CSRF attacks.
On the IdP's authentication page, the user submits their credentials and if successful, the user is redirected back to our website (the SPA) with the state token and a code as url parameters.
We then match the state value with the one that we generated on our app to defeat CSRF attacks.
We send the code value to our API in an AJAX request.
The API takes this code and our client secret and exchanges them for an identity taken with the identity provider (disregarding the access tokens as we only require proof of identity).
If the user identity is verified, the API generate a pair of access and refresh token and sends them to the SPA. (If the user does not exist in our database, it is added and similarly returned tokens for our API). NOTE: We do not store the identity token received from the IdP on the server at all.
One receiving the response, the SPA stores the access token in localStorage, (the refresh token is stored in an HTTP only cookie by the API).
All further requests to our API from the SPA are made with the access token issued by our API, once both the refresh token and access tokens expire (through logout or timeout), the user has to re-initiate the authentication flow from step 1.
I have two questions:
Assuming that all communication between the IdP and SPA, the SPA and the API, and the API and the IdP is happening over TLS, are there any security vulnerabilities present in our process?
This one perplexes me more, how do you persist the state token that we generate during the initial request to the authentication server between page redirects? (from when we leave our SPA to authenticate on IdP's webpage to when the IdP redirects us back to our SPA). Is localStorage a secure enough option? If not, would sessionStorage suffice?
CodePudding user response:
For a new project I would consider using the BFF pattern to avoid dealing with tokens at all in the SPA/browser. Because there is no secure way to deal/store tokens in the browser.
Do check out these resources for more details about why this pattern is recommended for new projects: