I have an Angular 5 app with a Asp.Net web api that is using localstorage to store a bearer token and use this token for my backend api calls. I’ve read several places that storing the token in localstorage is not safe and have read the documentation here (https://auth0.com/docs/security/store-tokens#single-page-apps) that suggests using these approaches instead. However, I’m still uncertain which approach to use for a single page app with a backend and have not been able to find a concrete example with implementation. Are there any examples that are not using localstorage to store a token that use Angular and Web API?
When a backend is present, you would treat it as a regular webapp and use the appropriate quick start for your .NET flavor (https://auth0.com/docs/quickstart/webapp)
You’d then need to maintain a state/session between your Angular front-end and the backend the same way you would in a traditional web application.
There are a few options that you can consider for this:
If you own your backend (and everything is on the same domain), you can do as @VnceB mentioned and treat your backend as a regular web app and implement authentication there instead of in the SPA. You can use a session cookie to secure API calls that the frontend makes to the backend.
If you do not own the backend or the backend is on a different domain, you can store your tokens in app memory and use the checkSession method in your Angular application to check for the existence of a session with the authorization server. This allows you to get tokens if the SPA is refreshed, or if a user leaves and returns later, without any need to store the tokens in local storage.
You can check out Angular-specific instructions for how to do this in the token renewal section of the Angular Quick Start here.
@kim.maida This is exactly the path that I went down and it works but I have one question.
If the user refreshes the SPA a few times, each time it will fetch a new token. That means that the back-end will receive a new token every time the user refreshes the front-end application.
On our back-end API we have been caching the users information based on their JWT so that we don’t have to hit the user-info endpoint every time a user hits the API with the same JWT token. This allows us to get passed the rate limit error you recieve when you hit the user-info endpoint too many times.
If the user refreshes the SPA a few times, the back-end hits the rate limit for the user-info endpoint because the caching strategy doesn’t work. Now that the front-end is sending different JWT’s per refresh, it hits the rate limit error for that user. Do you have any recommendations for this? We cannot use the cookie method in our use case and we need to get the users info on the back-end for every API request.
If you’re using auth0.js in your SPA and requesting
id_token token, the authentication result returned from
checkSession()'s callbacks will contain:
idToken, and also
idTokenPayload. The payload is an object containing all the same information the
userinfo endpoint returns, so there’s no need to call
@kim.maida that is correct, the SPA doesn’t need to call userinfo. The back-end API does.
When I send through an access_token from the SPA to the API it contains minimal information. I can use that access_token to hit the user_info endpoint to gain more information about the user and what permissions they have.
We are caching this userinfo payload on the back-end api using the access_token as the key… So when the front-end refreshes the page a bunch of times and requests new access tokens, our back-end caching strategy doesn’t hold up and we end up having to hit the userinfo endpoint on the back-end api, many times. This runs into the too many requests limit.
Permissions should be in the access token rather than in the userinfo, so those permissions should not change if the access token changes as long as the same user is being authenticated (same permissions contained in the tokens). You can also decode the ID token in the backend in order to get the userinfo (instead of hitting the userinfo endpoint).
I’d need to know a little bit more about your stack and architecture to answer more thoroughly. Where are your permissions coming from currently (e.g., how do you set them up)?
So you would recommend including everything you need to identify and authorize a user within the access token we send along to the back-end from the SPA?
The reason we keep the access-token lightweight is because it’s sent along every request. We don’t want to bloat that token over time and potentially reach a header limit size issue.
Permissions today are a bit of a mess for us and are handled by a combination of OAuth scopes we attach to the access_token per API, an auth0 extension which attaches permissions to the user and our own database stores permission information per user which we need.
There is other information we get from the user-info endpoint that isn’t permission specific. We do this so that each API can gain access to more user meta-data without having to store it in each database per service. We use Auth0 as the main source of truth for our user information.
With the user-info rate limit it makes sense that you shouldn’t hit this endpoint to gain access to user-meta data like we are today unless we can cache that information and since we don’t have anything to securely cache that information today besides the access token we are sending from the SPA, it seems like you would recommend stuffing more information into the access token.
If your access token is a JWT, use the “sub” claim as a key for caching the userinfo data and hit the cache even when a new token is generated. Just make sure you validate the JWT signature if you’re not already doing it and do not cache the userinfo data for longer than necessary.
IMHO, all this is probably more trouble than having the backend manage token exchange and maintain a session with the SPA via cookies. Maintaining a userinfo cache is already sort of like having a session.
The ID token is intended for user identification, the access token can remain lightweight. Decoding the ID token will give you the same user information that you get from calling the (rate-limited) userinfo endpoint. As @VnceB says, you can use the
sub claim as the key to identify users and retrieve cached data (as long as you have some amount of faith that their userinfo hasn’t changed recently), rather than the token itself.
I would also concur with @VnceB’s assessment that having auth handled by the backend and using cookies on the front end may be a more straightforward solution, and is, in fact, the architecture recommended by OAuth 2.0 best practices if you have a backend and frontend on the same domain.