Aaron Parecki
Yes, most authorization servers will not issue refresh tokens to JavaScript apps, because they are more risky. With public clients, the refresh token is extremely powerful, since it can be used without a secret, so many providers eliminate this risk by just not issuing refresh tokens to any kind of public client.
Jeff Lambros
Thanks for the response Aaron. With a short-lived Access Tokens and no Refresh Token, how do we keep the user from having to authenticate again to get a new Access Token?
Aaron Parecki
There are generally two ways, both rely on the user having an active session at the authorization server, which is key to the security of both. Either you can send the user back through the whole authorization code redirect flow again (which happens so fast they won’t even see it), or you can use the hidden iframe technique that’s part of OpenID Connect to do that flow invisibly.
Vladimir SD
In the video, at minute 14, you talked about a way using the Authorization flow where the token does not even get sent down to the browser, just the session cookie.
Can you point me to some examples of how this could be done in Spring Boot?
btw, great video, how you guys do more of them!
Micah
With Spring Boot, if you use the OAuth2 client and a templating engine like Thymeleaf, the token will only ever be on the server. This post uses Spring Boot in that way. It layers in JPA and MySQL, but it’s using OAuth2 & OIDC at it’s core.
Vladimir SD
If the token is only on the server how do you handle multiple web server besides sticky sessions?
Yang Wang
Is it safe to store the pkce_state
and pkce_code_verifier
in localstorage? What’s the likelihood of someone being able to access it and intercept the authorization_code before our app does?
aaronpk
That’s about the best you can do. There’s always a possibility something can steal data out of LocalStorage, but as long as you’re taking all the precautions against XSS, and have a strong content security policy to limit the number of third-party JS libraries on your site, that’s about the best security you can get in a browser. There are some good tips on browser security from OWASP here: https://cheatsheetseries.ow…
NitNeKing
Hi Aaron,
Thanks for this article and example code. It is extremely useful. Periodically a few of our users actually get to the point in the code where an ‘Invalid State’ is thrown from this line:
if(localStorage.getItem(“pkce_state”) != q.state) {
alert(“Invalid state”);
} else {
I’m trying to understand when this would happen. We store the ‘pkce_state’ that we generated locally and is used to verify the response coming back from Okta so that it matches with the same call to the authorization server (that we asked for the auth code). Instead of throwing the alert would another option be to clear local storage and start the flow over?
Faris Shomou
Hi,
Im getting this error : {“errorCode”:“E0000022”,“errorSummary”:“The endpoint does not support the provided HTTP method”,“errorLink”:“E0000022”,“errorId”:“oaez_eq3XLlTPS195AcIPysdQ”,“errorCauses”:}
Thanks,
fshomou@hotmail.com
Nathan Blair
There’s no such thing as safe storage, client-side, and very rarely, server-side. Regardless of platform. This PKCE method is, at best, security through obscurity. And due to its convoluted extra steps, it will receive little adoption. By suggesting the use of libraries, what we’re really saying is “you don’t need to ACTUALLY understand the vulnerabilities your app may be exposed to, just trust this entity!” And…we’ve seen how even the biggest firms drop the ball on this stuff. Open source or not, any library will eventually experience a leak. And that means BIG problems for your application security.
What would be most secure is not storing any tokens at all, short of living in memory (which even then, I think may have potential vulnerabilities). When a user needs to access secure resources, they’re always presented with a prompt to login. Since its 2020, websites should be using existing OAuth federated providers and remembering credentials for these providers shouldn’t be an issue. At the very least, the attack surface has been limited now to the vulnerability of the credentials being exposed from a UA/browser, instead of CRSS/XSS/CORS/BFSS/KSLDFSS/ASS vulnerabilities of cookies, web storage, or maybe even indexedDB?
Until then, implicit flow is the closest we’re going to get to not relying on insecure heavy token or client secret storage. So I’d say its far from dead.
aaronpk
The storage problem with JS apps has nothing to do with PKCE. PKCE protects the data sent in the redirect, which is a different attack surface than stored tokens.
Put yourself in the shoes of the authorization server issuing the access token. If you send an access token to the application by sending it in an HTTP redirect, you have no idea whether the application has actually received that access token, or what may have stolen it via the redirect in the process. For example, this browser extension will show you any sites that you log in to that are using the Implicit flow: https://github.com/oktadeve… It turns out that any extensions you have installed could already be siphoning off access tokens without your knowledge.
By using PKCE, the authorization server issues a one-time code in the redirect, and the application has to confirm receipt of it by making the separate POST request for an access token. If someone were able to steal the code from the redirect, they wouldn’t be able to use it to get an access token thanks to the PKCE mechanism.
Now, once the app has the access token, whether it got it from the implicit flow or using PKCE, the problem is now how it can store it in a secure way. This problem exists both with the implicit flow and with PKCE, and you’re right that it is mostly an unsolved problem with browsers. But, PKCE solves a different, more important problem, so it is useful.
I explain this more in this video: https://www.youtube.com/wat…
Nathan Blair
I think what the PKCE solution allows could be done with just enforcing a state nonce as well, and allows for one less round trip. Encode the code challenge, embed it in the state (or even just add the code verifier/challenge to the implicit flow as parameters), have the server send back the access token and the state and/or code challenge. Send the access token in addition to the decrypted state nonce and/or code verifier in all API requests. Server side, it only means checking one additional entity. Effectively, the access token is your proof authentication, the additional parameter becomes your proof of authorization.
I could probably have explained that better but it’s still early lol. I agree that there’s a vulnerability issue with the implicit flow though. But at least (if done correctly) access tokens have a short lived expiration time. There’s no way to renew them unless an extension was able to authenticate on your behalf, and at that point, I don’t think it’s an OAuth issue as a browser/HTTP(S) issue.
It may be time we re-architect-ed our approach to secure access of APIs. Insomuch that we move forward with the notion that there’s no such thing as secure storage, short of, at best, living in memory.
There’s this notion of: the user wants to experience the convenience of being logged in even after they navigate away from the page or close a tab/window. But maybe there’s a radically different approach where we end up with a more secure solution. I don’t know what that is, but I think the person that figures it out is going to make the world a much better place
Morné Niemand
@disqus_TDZbDnPyrN did you ever find a good method for storing the access token client side? cookie? session storage?
bebo
what about refresh token stored in httoOnly cookie with path and domain fixed to some “login” endpoint? then even with csrf there’s no way to use that refresh token for accessing anything other than relogging in user, especially if access token response is stored only in memory
Justin Meyer
Where can I read more about the hidden iframe technique?
Gabriel Sroka
I forked the code above and added OAuth for Okta:
https://github.com/gabriels…
D.Nikhil Teja
"If you’re building a JavaScript app that is served from a dynamic server, such as a Spring Boot backend with an Angular frontend, or an ASP.NET backend with a React front-end, then you can keep all of the OAuth exchange and token management inside the backend, never exposing it to the JavaScript front-end, and avoid all the risks inherent in managing tokens in JavaScript."
The referenced article “Spring Boot backend with an Angular frontend” still uses implicit flow with Angular. Am I missing something?
Matt Raible
The Build a Basic CRUD App with Angular 7.0 and Spring Boot 2.1 does not use implicit flow. It uses authorization code flow with PKCE because that’s the default when you create a SPA app on Okta. However, it does use the word “implicit” in its callback URL, so I can see why it might be confusing.
This article points out that it’s better to do all your authentication flows on the backend. My Angular + Docker with a Big Hug from Spring Boot post provides an up-to-date example of that. In particular, see Combine Your Angular + Spring Boot App into a Single JAR.