Hacker News new | ask | show | jobs
by jodersky 1547 days ago
There's an OAuth 2.0 extension called PKCE (https://oauth.net/2/pkce/) which mitigates this issue, and from what I understand it will be mandatory in OAuth 2.1.

Essentially, the idea behind it is that the client (the web app in this case) dynamically registers itself with a secret at the authentication server. After receiving the code, it uses a hash derived from the secret to authenticate itself. An attacker who intercepts the authorization code, would not be able to exchange it for a token, since they don't have the secret. Section 1.1 of the RFC (https://datatracker.ietf.org/doc/html/rfc7636#section-1.1) has a very concise and clear explanation.

1 comments

Thanks for sharing, looks interesting. However it is an extension to the "code flow". My main point was that lots of people do not use the code flow but rather stick with the "implicit flow" which directly hands out access tokens to the browser. If you opt for the "code flow", you need some sort of auth backend in your stack, that proxies all calls to services (since the token is only available in the BE).
Yes indeed, you are right that this is not part of the implicit flow mechanism.

AFAIU, the recommendation from OAuth 2.1 is to drop the use of implicit flows in web apps and instead rely on code flows with PKCE. The idea would be that the web app registers itself as a so-called "public client" (see https://oauth.net/2/client-types/) when it is loaded and then uses the standard code flow with PKCE.

I wrote a doc about the differences between OAuth2 and (proposed) OAuth 2.1, which might be helpful: https://fusionauth.io/learn/expert-advice/oauth/differences-...

It was based on this section with expanded examples: https://tools.ietf.org/id/draft-ietf-oauth-v2-1-00.html#name...

The implicit flow is bad for exactly the reasons mentioned: it exposes the access token (which is typically a bearer token) to the wild west environment of the browser. There are safe ways to have a token in a browser (as a secure, HTTPonly cookie, for example) but delivering the token in such a way as to allow any JS running on the page to have access to it is not one of them.

Having a server side component which holds the client secret and can safely do the exchange buys you security, but it also buys you architectural flexibility. You can decide where the token should live (on the client, sent down as a cookie or to be stored in memory or on the server, using the BFF pattern and sessions to tie the client to this token).

> but it also buys you architectural flexibility

Yes but it comes at a cost, that you always have to have a backend component for this auth/token exchange in your stack - even when relying on 3rd party SaaS auth providers. I use AWS Cognito for signup + login, and just to make the "code flow" available I had to introduce server-side services in NextJS using NextAuth. I attach the access token then to the session and send it back to the browser/SPA. Now, the access token is used for a broader range of services (different tech stack, different programming languages) that are directly called from the browser through Ajax. What bugs me with this setup is, that my FE is 100% SPA - so in theory I could just export the full NextJS project to static html and host it on a CDN. But the NextAuth service required for the code flow breaks this. I am going with it for now, but depending on hosting costs in the future (I have to run a separate server now for NextAuth vs. hosting static files on a cheap CDN), I might go back to implicit flow, and kill the need for a custom auth-service. Especially that I don't feel it is much safer now - if anyone can gain access to my nextauth server, all tokens will be compromised for all users. With the implicit flow I am no longer responsible for this security, and can rely on AWS/CDN being secure.

Sure. If your security posture is such that you can risk tokens being stolen, then perhaps the tradeoffs of the implicit flow are okay.

I don't know what you are building, but the example I always use is a recipe site vs a banking site. If you are building a recipe site where the data being protected by the token is not super valuable, then sure, the implicit flow is okay. If you're building a banking app, please take better care of your tokens and avoid the implicit flow.

> if anyone can gain access to my nextauth server, all tokens will be compromised for all users

This is where running on a PaaS like heroku and an open source library for handling the token exchance can be really helpful. You don't worry as much about the server maintenance issues due to the former, and you can rely on the "wisdom of the crowds" to help you secure the latter.

> With the implicit flow I am no longer responsible for this security

Correct. You are trading off securing the server for securing the client. In general it's harder to secure the client than a server (a bigger attack surface, less in your control), though of course it does depend on your skillset too.

> and can rely on AWS/CDN being secure.

And the browser. Don't forget you're depending on the browser being secure when you use the implicit flow :) .

> There are safe ways to have a token in a browser (as a secure, HTTPonly cookie, for example) but delivering the token in such a way as to allow any JS running on the page to have access to it is not one of them.

This always bugs me. Why is anyone creating an SPA (or any client mobile/desktop application) where they don't trust the code running in their own application? It just seems crazy to me to include dependencies/libraries that you can't trust.

Plenty of examples of NPM packages being taken over:

* https://www.whitesourcesoftware.com/resources/blog/npm-packa...

* https://medium.com/@alex.birsan/dependency-confusion-4a5d60f...

* https://blog.sonatype.com/npm-project-used-by-millions-hijac...

* https://checkmarx.com/blog/attackers-write-bugs-as-well/

When was the last time you audited all of the libraries a SPA depended on? If it was during the last build, congrats, you're doing great.

Lots of times it isn't automated and can be neglected, though.