This is fantastic. I've implemented OAuth2 4-5 times over the past few years. It seams overly complicated at first, but over time I've come to understand more of the vulnerabilities each piece of complexity exists to mitigate. Every time I've thought I could do it simpler, I eventually discovered a vulnerability in my approach. You get basically all that knowledge here in a concise blog post. This is going to be the first thing I link anyone to if they're interested in learning OAuth.
One gripe from Attack #3[0]:
> The solution is to require Pied Piper to register all possible redirect URIs first. Then, Hooli should refuse to redirect to any other domain.
There's actually a another (better IMO) solution to this problem, though to date it's rarely used. Instead of requiring client registration, you can simply let clients provide their own client_id, with the caveat that it has to be a URI, and a strict prefix of the redirect_uri. Then you can show it to the user so they know exactly where the token is being sent, but you also cut out a huge chunk of complexity from your system. I learned about it here[1].
> Then you can show it to the user so they know exactly where the token is being sent
Unfortunately, most major websites end up hosting an endpoint that will redirect users to a separate URL provided as a query parameter. This means that users may easily be misled about where the token is, in fact, being sent.
Is this true? Do you know of any major ones offhand? That would be surprising to me.
Thanks for sharing [0]. I found its discussion of shared subdomain cookies useful. However, I believe all the vulnerabilities in the OAuth section would be mitigated by using PKCE and not using the implicit flow, even if you leave the open redirect. Am I missing anything there?
As for open redirects in general, it is an important problem. As an authorization server, if you want to protect against clients that might have an open redirect (and as you indicate eventually one will), while still using the simple scheme I mentioned above, I can think of a few options:
1. Require the client_id to exactly match the redirect_uri instead of just a prefix. This is probably the most secure, but can result in ugly client IDs shown to the user, like "example.com/oauth2/callback". Of course clients can control that and make it something prettier if they want.
2. Strip any query params from the redirect_uri, and document this behavior. That should handle most cases, but it's always possible clients implement an open redirect in the path itself somehow. You could also check for strings like "http", but at some point there's only so much you can do.
3. Require clients to implement client metadata[1], so you can get back to exact string matches for redirect_uri. This is a very new standard, and also doesn't work for localhost clients.
Does that count as an open redirect? It gives a big fat declaration where you're coming from and where you're going to, and requires the user to choose.
I agree some nonzero number of users would click to continue when they shouldn't while doing an OAuth flow. Thanks for the example.
Tho if you do this then clients need a distinct client id per redirect uri domain, mostly this is non consequential and a good thing but I think it has some ux implications like seeing multiple consent screens vs just 1 if you had multiple redirect uris registered against a single client id.
Just web complexity really, there are lots of reasons you might have multiple domains as a business.
Language-specific domains, preview domains, multiple properties (often not fully integrated) etc.
You can always paper over things with more redirects / params (so a landing that bounces on) or multiple clients but supporting multiple redirects can save the customer work in some scenarios.
You don't have to violate anything in RFC6749, so technically yes. As I said it's not widely adopted, but it's also not difficult to implement. Generally OAuth2 client libraries require you to enter the client_id which you get during registration. Instead just tell them to use the URI of their app. You can see an example of such instructions for my LastLogin project here: https://lastlogin.io/developers/. It's just a short paragraph.
One gripe from Attack #3[0]:
> The solution is to require Pied Piper to register all possible redirect URIs first. Then, Hooli should refuse to redirect to any other domain.
There's actually a another (better IMO) solution to this problem, though to date it's rarely used. Instead of requiring client registration, you can simply let clients provide their own client_id, with the caveat that it has to be a URI, and a strict prefix of the redirect_uri. Then you can show it to the user so they know exactly where the token is being sent, but you also cut out a huge chunk of complexity from your system. I learned about it here[1].
[0]: https://stack-auth.com/blog/oauth-from-first-principles#atta...
[1]: https://aaronparecki.com/2018/07/07/7/oauth-for-the-open-web