Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Great post. I work in the space and am coming up to speed on some of the security issues.

Comments:

* this is a great read about security and OAuth: https://tools.ietf.org/html/draft-ietf-oauth-security-topics... written by experienced folks.

* don't use the implicit grant, please. Use the authorization code grant with PKCE. Here's an example (from a doc I helped write, talking about how bad the implicit grant is: https://fusionauth.io/learn/expert-advice/oauth/modern-guide...

* State can be used for CSRF protection but I've also seen it used to convey, well, state that needs to be carried over. That's legitimate, but make sure you append a random value as suggested.

* The well known endpoints will only be present if the OAuth server supports RFC 8414: https://tools.ietf.org/html/rfc8414 but you can always check the documentation. Most OAuth servers have plenty of public documentation to make their usage easier.

* Re "Flawed redirect_uri validation", the OAuth 2.1 spec has tightened this up some and wants exact URI matching. We have some customers who want wildcard matching, but the redirect_uri check is a fundamental part of the OAuth security architecture, so we've resisted that request.

* "However, by stealing an OAuth code or token, the attacker can gain access to the user's account in their own browser." is a good reminder that you should keep your token lifetimes short. We recommend minutes; use a refresh token to renew the token.

All in all, thought provoking article. There's a reason why more and more teams are outsourcing auth; there's a lot of flexibility in the specs and it's easy to make mistakes. Even when you do it right the first time, there's ongoing maintenance. You should run a bug bounty program and/or regularly pentest your system.



> * State can be used for CSRF protection but I've also seen it used to convey, well, state that needs to be carried over. That's legitimate, but make sure you append a random value as suggested.

Now that we are supposed to just use PKCE, we can stop using state for a nonce and CSRF protection and just use it for state.

> * "However, by stealing an OAuth code or token, the attacker can gain access to the user's account in their own browser." is a good reminder that you should keep your token lifetimes short. We recommend minutes; use a refresh token to renew the token.

Stealing codes does not work because PKCE, you need to steal at least the entire request and response, will be blocked if PKCE is using a cryptographic transform.

Protections from stealing tokens are confidential clients (requires a remote machine compromise) or proof of possession like MTLS or DPOP (requires private key exfiltration).

Generally the most paranoid access token lifetime recommended is ~10 minutes. There are token revocation methods that let you go longer, but revocation is an active process while token refreshes can just fail because of some change of application state.


> don't use the implicit grant, please. Use the authorization code grant with PKCE. Here's an example (from a doc I helped write, talking about how bad the implicit grant is: https://fusionauth.io/learn/expert-advice/oauth/modern-guide...

This is a bit of a strange security model. The implicit grant vulnerability mentioned is essentially a supply chain attack where an untrusted library is loaded. True, it can compromise the implicit grant token, but, in a mode where the implicit grant token is hidden, it's still talking as the website, and can do ~most~ anything, more or less that the token can do by making a request with the cookie, or whatever.


It's not just a supply-chain thing. There are a bunch of subtle interactions between the implicit flow and the rest of the web security model. For instance, because implicit flow releases working tokens (not grants, tokens) directly to content-controlled Javascript, it can be a way of transforming open redirects into account takeover.


And replacing it with auth code flow fixes that? I'll go along with auth code flow to fix Safari (now everyone) breaking cookies, but the security aspect of PKCE always smelled to me.


In the standard auth code flow, the authorizing application (I forget the OAuth terms for it, but "TweetLater" or whatever) shares a secret with Twitter, independent of the secret (your authentication credentials) you share with Twitter. The code you, a user, get --- that's fed through your browser --- is in a sense "locked" to "TweetLater". Part of the idea of the implicit flow is that it's for settings where you can't do that, or where it doesn't make sense (SPAs, for instance, where the secret the SPA would share with Twitter would be have to be embedded in the public Javascript code).

PKCE is just a weaker version of that protection.


Thanks for taking a stab at it. I'm well aware of how the auth code flow works - I have yet to see a serious discussion though on how PKCE fixes the issues inherent in the implicit flow. Everything I've seen is "implicit bad, PKCE adds security to other flows, therefore PKCE fixes implicit grant problems". Which is of course non sensical, but great for memetic transmission.


I guess I'm not clear on where you're not clear. PKCE adds a secret that the redeemer of the auth code returned from (say) Twitter needs to know to use the code. The big problem with implicit flow is the extent to which it exposes that code; PKCE makes it harder to use the code once exposed.


As you yourself said - a problem with implicit flows is abuse of open redirects. It's a rare open redirect that is vulnerable to implicit that's not also vulnerable to PKCE protected auth code flow. This pattern is generally true across the board for implicit grant vulnerabilities. I was hoping you knew of known value-add versus what's usually seen (when anything is examined at all) - contrived scenarios where the attacker can only abuse minor parts of an open redirect or has JS execution but not the ability to make off-box calls, etc. The subtle browser interactions, as you call out, are the problem, not the one versus two legged nature of running session existence into tokens. (Of course, in auth code flow you now have RTs, so now you have N+1 problems...)

It's more of a discussion for the standards groups I suppose.


Probably a silly question, but how exactly does the refresh token help here? If your app is keeping the tokens accessible from javascript, then an attacker who (through XSS for example) can steal the short-lived access token could also steal the long-lived refresh token an "trade it" for a new access token, no?


> If your app is keeping the tokens accessible from javascript...

Great question. I should have been clearer.

If you use the implicit grant, you can't use the refresh token (there are work arounds like the silent refresh, though). So it's not an applicable question.

With the application code grant, you have other options. Refresh tokens, like all tokens, should be treated with care and not exposed to javascript.

As a sibling comment noted, you can do that by storing it server side. Sure, someone could steal your session and try to access protected resources through the server side code, but that's harder to do than stealing an access token and being able to present a bearer token to any protected resource. (Or a refresh token, which can then be presented to an IdP and exchanged for an access token.)

One other option we recommend is sending down the access and refresh tokens as secure, httponly cookies. In that case, you are relying on the browser's cookie security to protect against XSS attacks. This, while not perfect, is pretty darn good, and if there's a widespread XSS cookie vulnerability:

1. Everyone with a browser client will probably have issues.

2. You could invalidate all the refresh tokens at an IdP. Pain in the but for users, but this is similar to what GitHub did earlier this month.


Refresh token rotation seems to be the standard mitigation (https://auth0.com/docs/tokens/refresh-tokens/refresh-token-r...)


Sure, that mitigates the risk when an attacker finds a refresh token later and it's no longer valid because of rotation. And it means that a stolen refresh token would probably be noticeable because the legitimate user wouldn't be able to use it anymore. But in many attack scenarios the attacker would get the refresh token immediately and the time until discovery would be enough for them to cause damage, right?


Generally there is a big overlap between attack scenarios that allow for token exfiltration and attack scenarios that allow for code injection.

Someone doesn't need to exfiltrate a token to make the client do their malicious requests right on the user's device. And certain external mitigations, like anomalous API usage detection, won't see odd time-of-travel restrictions based on the estimated geolocations of two different IP addresses or a change in the user agent behavior (different user-agent header, different networking stack behavior, etc).

The difference is that some people argue that one of them is in scope for security measures, while the other is out of scope.


Usually the refresh token would be kept server side only




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: