I have 3 website projects as follows;
- identity.example.com (asp.netcore + IdentityServer4)
- api.example.com (asp.netcore webapi)
- www.example.com (asp.netcore + aurelia)
I am able to authenticate the user using SPA user-agent using implicit grant type and I store the access_token on localStorage. Since localStorage is not safe, long lived access_token or use of refresh_token is not safe, so my users will have to login every X minutes which is not acceptable as you would agree.
My goal is to let users have "remember me" option and still keep things as secure as acceptable (I know there is no 100% security in this regard)
I think I have the following options;
1. Full Proxy Between SPA and API/IdentityServer, everything stored as http_only cookie
- spa posts credentials to its own backend (www.example.com/login)
- the spa's backend gets access_token and refresh_token from identity.example.com, stores the tokens in http_only cookie or in the session.
- the spa's backend acts as a proxy between the api and the spa andthe spa makes any api calls to it's own backend, not to the api
- the spa's backend routes all the api requests to api.example.com using the access_token stored in the cookie/session and returns the result to the user_agent.
Now this approach looks secure but it adds a proxy level which would have negative effect on performance, request response times etc. Plus, I would need to write a wrapper/proxy for every single api method in the spa's backend (the proxy) that would make calls to the actual api methods which is not that cool.
I thought of the following flow to make it secure and not painful for the user at the same time;
2. Semi Proxy Between SPA and Identity Server (for refreshing tokens only)
Summary: SPA has access to access_token, However, to refresh the token, spa should interact with its backend which holds the refresh_token in session/cookie securely.
spa posts credentials to its own backend (www.example.com/login)
- spa gets an authorization_code from the identity server (client_secret is not neccessary for this) and posts this to its own backend
- the spa's backend gets access_token and refresh_token from identity.example.com using the authorization_code, stores the tokens in http_only cookie or in the session and also includes the access_token in the response body
- the spa stores the access_token in its localStorage, and uses this for calls directly to the api (api.example.com, no proxy)
- when the access_token expires, the user-agent (spa) asks it's own backend to refresh the access_token on behalf of itself
- the spa's backend retrieves the new access_token and refresh_token from the identity server. Updates the tokens in the session/cookie and returns the access_token to the user_agent (spa)
the user_agent (spa) updates the access_token in its localStorage and uses this for future calls to the api.
Now, the questions... Is the last flow I have described secure and acceptable by the standards? Do you know an example implementation of it? Or do you have other suggestions that I should follow? Maybe security flaws I should consider?