oidc-client-js
oidc-client-js copied to clipboard
UserStore should not default to sessionStorage
Neither the localStorage not the sessionStorage is considered safe for handling authentication tokens. If there is for instance any third party script on the website, that script would be able to extract tokens from localStorage and sessionStorage.
Thus the InMemoryWebStorage should be made the default. Many people using this library won't know better and currently default to an unsafe storage. For people who are educated about this and can take the risk would still be able to switch.
The loss of functionality should be quite minimal in this case. The only difference will occur when the user reloads the page, in which case the OIDC provider will have to be asked again directly for a new token, instead when the browser is reopened.
Neither the localStorage not the sessionStorage is considered safe for handling authentication tokens. If there is for instance any third party script on the website, that script would be able to extract tokens from localStorage and sessionStorage.
Sure, but that's the case with any credential stored anywhere. Don't include third party JS in your app if you don't trust it.
Thus the InMemoryWebStorage should be made the default.
This would make more JS apps not work terribly well. Imagine if a user just hit refresh in the browser -- those tokens would be lost.
This would make more JS apps not work terribly well. Imagine if a user just hit refresh in the browser -- those tokens would be lost.
How's that a problem? Silent refresh is exactly there for solving that problem.
Sure, but that's the case with any credential stored anywhere. Don't include third party JS in your app if you don't trust it.
A namespaced object cannot leak them though.
A namespaced object cannot leak them though.
You mean trusted types? https://developers.google.com/web/updates/2019/02/trusted-types
Or something else?
I'm going to close. I think the default is a reasonable balance of security. If you want something stronger, there are ways to achieve.
May I recommend though that you make a note in the README, so people know the recommendation at least? Especially if they embed third party scripts (Google Analytics, some javascript library from a CDN or such), that those scripts will have access to the tokens if they don't switch to memory storage.
Yep, I'm totally up for that and it's a good idea to let people know/understand as much about what they're doing/getting. Would you be willing to start a PR with it? I'll reopen this w/ enhancement so we don't forget.
@DASPRiD I agree with you about the preference of using InMemoryWebStorage instead of localStorage or sessionStorage, but i'm having trouble finding a way to implement the code to manage the reload of the browser.
The loss of functionality should be quite minimal in this case. The only difference will occur when the user reloads the page, in which case the OIDC provider will have to be asked again directly for a new token, instead when the browser is reopened.
If I may ask you, how, where and when can i request a new token after the page reload?
That's quite easy, simply do the following:
userManager.signInSilent() on page load :)
The only real downside to this is, that after the page loads, it takes a second or so for the user to show up as signed in.
That's quite easy, simply do the following:
userManager.signInSilent()on page load :)
Thank you but i've already tried that on the load of my Vue app, the problem is that i receive the error Error: Frame window timed out
One big downside of InMemoryWebStorage is that you are hitting the OP much more often and credentials are not shared between tabs on the same origin. So I would argue against making it default.
Documenting the security implications of session & local storage is important though.
One thing I missed in this issue, to safely use SessionStorage or LocalStorage users should not use any 3rd party/untrusted Javascript in their app or on any of the pages on the same origin, or those other pages could still leak credentials. This is easy to forget, but quite important.
That is exactly the point, yeah. As long as you don't have any foreign JavaScript on your your domain, you should be fine. At least from your side. Whether the user has some Greasemonkey script or such running is a different story.
I am so much confused about token security. Some are convenced that LocalStorage is not for sensitive information others are saying it is ok to do so.
in Auth0 site here they think it is not secure using LocalStorage and think the best way securing your token is by using cookies. But I think the cookies in this approach should be set from sever not javaScript.
Again I am some how lost here and not expert in this area so if any one here can explain this issues and how for example microsoft and google are protecting there token?
@baselbj
- It is a consideration you need to make for your specific use case
- Storing any credential in a browser has risks associated with it, however you do it
- There is currently no perfect solution that has both optimal usability and security
Cookies set from the server with HttpOnly flag are the most secure method, but only applicable if you can do this (in some situations you cannot). It also introduces its own set of risks and considerations if you need to somehow make the token available in frontend javascript.
In OAuth0's reasoning, they point towards server set cookies if a backend is present. In case of an SPA they suggest logging in and storing the tokens in memory. These are both the most secure methods based on the use case, but they are not necessarily the easiest to use correctly.
You can use Session or Local storage somewhat securely too however, but it requires much more due diligence to eliminate and prevent foreign code from running on your domain/origin. This is much easier said than done in the modern landscape of SPA applications however.
Useful discussion! I've been studying the different approaches and concerns regarding token storage and I'd like to ask what do you guys think about implementing an approach similar to the one described in this article (according to the author, it was originally purposed here).
As @brockallen pointed out:
I think the default is a reasonable balance of security. If you want something stronger, there are ways to achieve.
From what I've learned, when it comes to SPA, Okta sets local storage as default, auth0 recommends memory storage, Keycloak gives session and cookie storage options. And, as @sg3s said, at the end it's all about your use case and the security/usability relation you plan to achieve. So what about oidc-client-js giving developers the option for a higher security level (assuming that the two cookies approach leads to that)?
I'd love to hear specially from @brockallen , @DASPRiD and @sg3s . Thank you guys!
Both articles lean on the concept of splitting up the token and keeping part as a HttpOnly cookie and part in the FE application for authentication. The second article doesn't even mention OAuth2 / OIDC so it is a completely custom workflow. Most notable 3rd party cookies are unreliable these days so it wouldn't really be dependable for situations where frontend and backend run on different domains.
It's not a terrible strategy idea and it may address some security concerns but it does not address broader use cases. It also violates some of the separation of concerns principles imbued in OAuth2/OIDC and customizes workflows beyond what is in the specifications. I would not suggest adopting these strategies to anyone for interoperability reasons, unless it fits their specific use case and they're prepared to solve these issues themselves.
The functionality suggested also requires (mostly) server side components to be implemented, it has little overlap with the client side authentication this library is used for. If so desired there are plenty of other (server side) libraries that would allow retrofitting OAuth2 procedures to fit these specific workflows.
The oidc-client-js library should keep mostly to the spec, and not adopt novelty functionalities beyond that scope, in my opinion. But if the OIDC or OAuth2 workgroups adopt and document specific procedures around the handling and storage of credentials, it should be implemented.
I added
userManager.signinSilent()
as suggested above and that helped somewhat but that started breaking once I logged out of one of multiple tabs, see https://github.com/Edgeryders-Participio/realities/issues/191#issuecomment-756828918
Sure, because your session at the STS is gone, silent renew via the authorization endpoint (in the iframe) won't work.
Yeah it's reasonable that the silent renew doesn't work in that case, but when it doesn't work (since I'm not logged in) then shouldn't it clear the user's local userData (i.e. make the user see that they're logged out clientside)?
then shouldn't it clear the user's local userData
that's up to you. you can handle the userSignedOut event then call the user mgr's removeUser to achieve that.