289 comments

[ 0.25 ms ] story [ 271 ms ] thread
Because it's too flexible, and allows the OAuth provider to choose which features they support. That puts all the complexity on the client to support every possible implementation.
Thank you for "asking" and have the luck of appearing in the first page in HN. I am sure many here wonder why. There are more complex pieces of software that find a way to abstract the problem in a nice interface.
It's a turd, if you want poor security you should make things so complicated that half the implementers have no idea what they are doing. We should have a kind of LD50 for specs: How much feature cruft do you need for 50% of the test subjects to die.
Exactly right answer. It's a turd.
Because "OAuth" is not a thing, it's a label you can glue on any wild-and-wacky authentication mechanism someone hacks together. It is structurally impossible for OAuth to ever become "easy".
Yes, I’ve noticed that a lot of companies implement “OAuth” differently. I’ve found the only way to reliably implement it is to read each company’s docs and also test their API. Once you implement their flow and signing (or not signing) method, it’s really not that complicated.
Yep. OAuth is more of a concept than a standard. There are common patterns, and you might get lucky and find 2 integrations that handle it the same way... but I wouldn't bet on it. I worked on a system that OAuth'd with about 5 different 3rd party systems and each one was a little bit different.
I agree and disagree. The OAuth 2 spec is pretty explicit. People do not follow it.

The industry has played fast and loose with the spec because too many people implementing OAuth support in their app do not understand OAuth and/or insist on bleeding application or architecture-specific behavior into their authentication flow.

The end result is what you describe: a proliferation of approaches that roughly follow the spec. But this should not be mistaken for the spec being loose IMO. Rather that the state of auth is abysmally non-standard and homegrown.

I owned the authentication stack for a large enterprise platform company, and worked directly with the biggest players on ensuring compatibility between our platform and their auth flows, and the typical deviations from spec are almost always unnecessary and duplicate something that was already possible.

To be fair, Auth Code flow gets pretty funky with browser redirects + backend calls and can be hard to grok at first.

The other major issue is that once some custom auth thing exists in production, it's never going away if there are enterprise customers relying on the behavior. I suspect that a lot of customized implementations were never meant to be long term solutions.

We have a joke about this at Zapier -- don't be an oauth butt!

"We support standard oauth butttt..."

OAuth is easy in 2023. The problem is that OAuth does essentially nothing of interest by itself. It's a way to pass scoped access tokens.

That's it. That's the end of the spec.

If you want to do anything with those tokens, you need to implement against other APIs the provider makes available. Unless all of those APIs are also standardized, it means implementing a bunch of stuff for each integration. And now things are no longer easy.

OAuth is a way to obtain scoped access tokens.

And if you read the article, you would have something of substance to contribute on that topic, whether it is easy or hard or otherwise.

Example: What is the purpose of OAuth having specifications for redirect URI allowlist AND PKCE?

None. They are entirely duplicative features.

But ... reason, reason, reason ... they both exist, and are going to exist, forever.

EDIT: I should say, PKCE is a functional superset of redirect URI allowlist.

My understanding is that PKCE allows you to more safely operate a client that does not have a client secret.
The React Native docs have a good writeup on how PKCE reduces vulnerabilities with deep linking & session hijacking - https://reactnative.dev/docs/security#authentication-and-dee...

PKCE also gives you nice assurances that the device finishing the flow is the same as the device that started it. Without PKCE, the classic client credentials flow risks login CSRF - https://support.detectify.com/support/solutions/articles/480... - which may or may not be an attack vector you care about.

(comment deleted)
> well yeah, but there was already state, but nobody used it for that

state was meant to be application state. The problem is that there _wasn't_ anything like PKCE in vanilla OAuth 2, so the client was told to overload state with protocol state with particular processing rules, rather than just its own application state.

With PKCE, the AS can see whether it is the same client instance and reject the request. The state workarounds meant the client had to implement the rejection, which many did not do.

If one uses a redirect_uri with PKCE parameters to send a response a non-oauth endpoint, the AS is still acting as a redirector.

It's typically not a list. You typically will have one redirect uri (at least, one per AS), because thats the communication endpoint defined by your client. There are other parameters like state to remember what you wanted to do after you had an access token.

PKCE exists to bind the front-channel authorization request and back-channel token request together as being by the same client. There is otherwise no evidence that the two parts are by the same software instance, which causes other security issues.

The redirect_uri parameter is not a list.

I am referring to the redirect allowlist registered out-of-band by the client: https://datatracker.ietf.org/doc/html/rfc6749#section-2

right. I was speaking to registration of redirect URIs - most clients should only register one, because they have only one endpoint (for that AS at least) for handling authorization responses. You should not use redirect URI for deep links, for example - you should capture pending actions to take after authorization separately.

You could hypothetically register redirect URI for the DNS names of your individual client cluster nodes, but there's no guarantee an AS will let you register as many URI as you have cluster nodes - and you will have a failure case to handle if the cluster node goes down mid-authorization, and coordination if you need to grow said cluster.

On iOS, two applications can register as handling the same callback URI scheme. Which one gets the callback is non-deterministic. Thus, it is possible for some other app to get the token. I don’t see how an allowlist would mitigate this.

With PKCE, the other app can still intercept the token, but the token is incomplete and useless.

Correct me if I'm wrong, but what you're implying here is that PKCE and URI allowlist are the same feature because if you specify URI allowlist while initializing the flow - it somehow helps public clients to prove they're the apps that initialized the flow and are the ones who are finalizing it?

Could you please expand on that thought, I'm genuinely curious if you actually might be right or whether this assumption of yours is how we get security holes.

That's not correct. There are a number of attacks that can be mitigated by both, but PKCE serves as a very effective defense in case an authorization code leaks to an attacker. Such a leak can be caused by a malicious script on the redirect URI, referer headers, system or firewall logs, mix-up attacks and other problems even when the redirect URIs are restricted.

There is a good reason why we mandate both redirect URI allowlisting AND PKCE in the OAuth Security BCP RFC draft. One learning from our discovery of mix-up attacks with "code injection" was that client authentication is not sufficient to prevent the misuse of authorization codes.

Your comment doesn't explain what attack can be mitigated by redirect allowlist but not PKCE.
For starters, without restrictions on the redirect URI, I (as the attacker) can just redirect a user to the authorization endpoint with a client ID of a trustworthy client, a redirect URI pointing to my server, and a PKCE challenge that I selected so that I know the PKCE verifier. The auth code will end up at my server and I can redeem it, giving me (instead of the trustworthy client) access to the user's resources. If the client is a confidential client, I can use a authorization code injection attack to redeem the code and work with the user's resource.
It was designed by committee, and the internet adopts any shitty technology that a big player pushes.
It'd be interesting to hear about people who have had a good time implementing OAuth, as my experience is similar to that in the article. I've played with adding it to a few side projects and the process usually goes:

1. Read loads of docs, end up pretty confused

2. Find a library that seems to do what I want

3. Install this huge library full of opaque code doing...things

4. Have an impossible time troubleshooting issues

5. Get scared ("I'm almost certainly doing something wrong here") and give up

I find it hard to have much faith in security standards like this. I want them small, well defined and understandable and OAuth does not meet any of these criteria in my experience.

3b. realize that library is deprecated or hasn't been updated in many years and is either totally broken, or incompatible with something else you're using.
3c. Write your own "library" which encompasses the the 50 lines of code.

Adding the oauth calls into your app that already has a web server, job processing, and a db is way easier than integrating what ever weird stuff some random library does.

I have read many docs.

The one that I find easiest to understanding is still the one that I wrote about a decade ago when I first had to work with OAuth 2. All others I understanding by mapping what they said to concepts in mine, and that seems to work pretty well.

My document is available at https://metacpan.org/dist/LWP-Authen-OAuth2/view/lib/LWP/Aut.... Even though you're unlikely to ever use that library or language, you may find it worthwhile.

I'm pretty astounded, because this is by far the best OAuth 2 doc I've ever read, and that's after reading and watching many hours of content from leaders and companies in the space.
Thank you.

It summarized my understanding after a month of reading documentation, doing experiments, and generally banging my head against the wall.

Looks great, taking a look and favoriting.

(Only feedback I can think of is that it would be great to have a few diagrams... like from excalidraw or something. Am largely a visual learner.)

As a fan of Perl, this situation of having an obscure, meticulously-documented CPAN module to not only explain but implement some common-but-surprisingly-hard pattern is just so ... Perl-y (:

Perhaps it's a semi-consequence of Perl being perceived as old and crusty, and popular with old crusty people who are merely trying to get things done without fluff (at least speaking for myself).

Reminds me of Data::Manip, another favorite of mine.

Thanks for taking the time to write this (and implement all that nonsense).

Thanks.

The crustiness in this case was enhanced by my grumpiness over some frustrating debugging sessions, and having to go through a lot of very confusing documentation to get there. That grumpiness shows through in a couple of places.

For example I understand why Google decided that I was asking for the same permission too often, and began handing me codes that would not work. And also wouldn't explain what was wrong with the code that I just got.

Please publish this on its own somewhere! This is fantastic.
I'm bookmarking this to go through the next time I need to bang my head against this wall, thanks
Your documentation is amongst the best I've seen on OAuth, but it suffers from the same naming confusion I always run into when I'm reading OAuth docs.

> OAuth 2 makes it easy for large service providers to write many APIs that users can securely authorize third party consumers

If I'm trying to write a Mastodon client, I'm reading this line piece by piece:

> OAuth 2 makes it easy for large service providers

"service providers" OK, that must be the Mastodon service.

> ... to write many APIs that users

"users" That must be me

> ... can securely authorize third party consumers

"consumers" OK... that's also me?

Users are your users shared with the users of that API service (which may include you if you use your own service), consumer in this case is you (the application you're running).
I think it's helpful to not stray too far away from the standard terminology. I found that "third party" was an excellent indicator that the consumer is probably not me, especially since I've already qualified as a user.
Read the introduction again, and then follow the link to the Terminology section.
This is the first Oauth document I've seen with a no-nonsense "Terminology" section. Thank you!

The only thing I'd suggest is putting the terminology first, so readers can first correct their misconceptions from all the terrible literature around this.

I thought about it, and settled on the compromise of referring to it up front, and then putting it at the end.

That's because there are many ways you could use a document like that. And the Terminology section is very overwhelming unless your goal is to understand how OAuth 2 REALLY works, and why it works that way.

OAuth specification was the best source for me. All provider docs are simplified and usually specific to the provider
It’s been a while since I’ve read the RFCs but I recall them being very accessible and clearly written.
We really deserve a less over-engineered actual standard that has a very restricted feature set.

In practice, isn’t OAuth predominantly used to verify proof of email ownership? If so, why not just use magic links as sign up & sign in?

1. Sign in/up: Enter email (can be pre-filled by browser/app)

2. Click the email verification link or enter code if on different device.

3. Profit. No manual typing necessary, only clicks.

This is trivial to implement, and can be extended in the future with a simple standard for browsers/apps to automatically verify in the background (to avoid the tab-switching inconvenience in step 2). On iOS they auto-populate SMS codes in a similar fashion.

2FA can be out of scope, (many times not needed because email providers already have it). But if needed, it can be added as a second step after the email proof.

Please tell me what I’m missing. This seems, to me, like an excellent trade off between implementation simplicity, extensibility, user convenience and security.

It would simply not handle pretty much any case that I have used OAuth2 to implement so far.

For example - login system that merged LDAP/Kerberos/client cert/long-lived application token authentication into single system, that also linked said authentication system into all applications in the network, including making it possible to login to AWS Console using Kerberos (that one was twisty to get running, not because of OAuth2 but because of how it is handled by AWS IAM).

Also, I have used it to link in MFA systems of different kinds (it was definitely easier side than industry standard of using Radius)

In addition, this proposed system requires that every app has ability to send emails, which honestly is less simple than it sounds, especially today when sending to arbitrary public emails.

For service accounts, email is clearly not the right choice. I don’t have experience with enterprise auth, are Kerberos etc not using company email for human identity?

> this proposed system requires that every app has ability to send emails, which honestly is less simple than it sounds

For humans and especially end-users of consumer services, my observation is that the elaborate auth dances are using email ownership as last resort anyway, ie for account recovery and/or a trusted 3p that has verified the email. So the thought is simply to make that flow more convenient. Perhaps this is misguided.

In case of OAuth2/OIDC, if I do not use external providers (like Google etc.), I can still deploy one of the many OAuth2/OIDC providers myself and centralise handling of user database this way.

This also means I have one place to support sending last resort emails

As for enterprise auth, a lot of places in fact do not use emails for identity. Sometimes there's more than one login id mapping to one identity (noticeable case - Kerberos/LDAP as done by Active Directory, where your login can come in email-style form and pre-AD form, and the email-style one doesn't have to correspond to an email)

Email and SMS are inherently insecure, and it would be a lot harder for whoever's on the other end to apply security practices (e.g. look at where logins are coming from, rate-limit authorization attempts). They can't pass extra metadata back (user's name/address/avatar/etc.) and they can't do fine-grained permissions (grant me access to this github repo but not that one, grant me read but not write, ...). Plus I don't want to have to switch apps a bunch, and may not have my email with me; doing it all in the browser is much nicer.

(On a side note, I'm constantly annoyed/frustrated that after about 20 years of development, authentication codes/apps and smartcode verification are starting to be almost as secure and usable as the HTTPS client certificate support that was built into every browser as far back as the '90s)

> Email and SMS are inherently insecure

But nevertheless widely used as de-facto identity, I assume because account recovery in case of lost credentials is paramount. At least 90% of my accounts would be stolen or lost, today, should I lose access to my email.

I don’t particularly like email, for many reasons. Especially that most people’s email can be blocked by a faceless corporation that suddenly bans you. That said, I think there are far worse options, such as endless iterations on proprietary and ad-hoc auth “standards”. At least to me, the technical challenges of hardening email seem far less intimidating than educating the public on a new system.

> apps and smartcode verification are starting to be almost as secure and usable as the HTTPS client certificate support

I agree that client certs are greatly under-utilized and poorly supported by eg reverse proxies, but how would they help here? What’s the user flow for non-technical Joe to acquire a client cert to pay his bills?

> But nevertheless widely used as de-facto identity, I assume because account recovery in case of lost credentials is paramount. At least 90% of my accounts would be stolen or lost, today, should I lose access to my email.

Sure, but that's your choice. If you want to make your Google account require 3 factor authentication and a 10 minute timeout, you can, and from the perspective of any site logging you in via OAuth from Google, nothing changes. Even if 90% of users are going to use email only, it's nice to not force everyone down to that lowest common denominator.

> I agree that client certs are greatly under-utilized and poorly supported by eg reverse proxies, but how would they help here? What’s the user flow for non-technical Joe to acquire a client cert to pay his bills?

I just feel that if we'd put half the effort people put into SMS 2FA, authenticator apps, password managers and all that into making a better UX for client certificates, we'd be further along. Ah well.

My experience is similar but tbh I feel it's mainly due to the fact that oauth is implementing an important security layer (authentication/authorization) and that's hard by definition. There are lots of steps and things you can't afford to overlook or you'll be vulnerable to multiple different attacks
I found doing a custom (without a library) OAuth integration for only one service relatively straightforward, i.e. when it’s not treated as a standard. For example for a plug-in exclusively for Shopify (random example, didn’t try it) you would just treat it as the rest of their API and write custom code.

In short it works when you can see it as a guideline instead of a standard.

When I stopped using libraries and implemented my own things got a lot less frustrating. Still a pain in the ass, but I didn’t feel like the errors came out of nowhere any more anyway.
Yeah I've just implemented it myself. Google, Microsoft and some other OIDC provider.

I abstracted it away behind a "token provider" interface, so I can just instantiate the right one for the job.

There's some incantations to get the request right, but I found most documentation decent so just follow that.

One service required I implemented RFC 8693 token exchange, which turned out to be trivial since I could represent it as one "token provider" instance wrapping another.

Failures can still be opaque though, especially when using the tokens.

I've reimplemented oauth for the same provider (bungie.net) countless times. I think it might seem daunting, but when you break it down into the steps it's pretty simple:

- Send user to example.com

- Eventually they're sent back to yoursite.com with a ?code=abc

- Call example.com/OAuth to exchange ?code=abc for access token and refresh token

- You're done (for now)

- When access token expires, call example.com/OAuth to exchange refresh token for a new access token and refresh token

The tricky part is that a bunch is implementation specific, so memorising the above only gets you half way to implementing with another provider.

Yeah I don’t see what the big deal is. You don’t need to know every oauth flow by heart and most of the time you’re going to be using the authorization code flow anyway. There are tons of articles and comments on HN about much more complex things than oauth every day.
I have an application with this flow already implemented to authenticate a user.

But the application currently has it's own authorization policy in a flat file which organizes users into groups and gives groups permission to take certain actions, a simple RBAC. I'd like to refactor this so that the authorization is delegated to the OAuth server.

Ideally I want to be able to ask a keycloak/okta server "now that user X is authenticated, are they part of the group Y?".

Since this isn't a common use case and everything is so abstract it's been very difficult to find an obvious path forward.

I had bit similar need when implementing our own OAuth2 authorization server to be used by our own (three) web applications and several APIs related to them, all of which use the same backend to store users and API keys with differing permissions to logical entities our datamodel consists of. These permissions are somewhat naturally mapped to scopes, but the main issue is that single user or API key could have differing permission across multiple logical entities, so it's M-to-N representation (M logical entities, with N different scopes). So simple returned scopes would not suffice, as resource server needs to know which entity or entities the caller also has access to, according to scopes.

As I could not find any pre-existing good guidance on solving this, I ended up implementing it by having the resource servers (i.e. APIs) check the token using token introspection endpoint at the auth server (not the RFC version of introspection, just private one for now but I may add the RFC compliant version as well so it could be called by 3rd parties as we have not yet rolled out this OAuth2 based setup for our customers). Part of that return information from the introspection endpoint is this permission mapping between logical entities and scopes, so the resource server can know to which entities caller has which scopes. Of course, as our access tokens are not JWT and just opaque (encrypted) data, the introspection endpoint then need to decrypt it - just so it can quickly validate expiration etc. - and if valid, fetch a matching copy from the database that has more contextual information than what's encoded inside the client-supplied access token. Otherwise, the length would be prohibitive if token would be fully self-contained with these extra information.

Maybe this same kind of method could be used for your use case as well, encoding authorization policy adjacent to the access token in your backend, and looked up by app using it after it has received the token?

I've both had to acquire and validate[1] tokens from Azure AD, both Client Credentials[2] and Authorization Code[3] flows, and found Microsofts documentation quite good. I've also had to acquire tokens using certificate-based Client Credentials flow from another party.

Overall I found it fairly OK, it's a bit of a learning curve when you're just used to basic user/pass, but it's fine.

However the main issue is that when it's not working it's very opaque. This is even worse when trying to _use_ the tokens.

I spent many hours trying to figure out why my Client Credentials tokens wouldn't work for logging on to Office365's IMAP server, only to spend half an hour searching and finding some Microsoft community forum post saying it wasn't implemented yet... they only supported Authorization Code flow. This has been fixed they say, I'll know later today cause that just came up as a priority ticket...

All you get back is some "nope", with no way to figure out why.

[1]: https://learn.microsoft.com/en-us/azure/active-directory/dev...

[2]: https://learn.microsoft.com/en-us/azure/active-directory/dev...

[3]: https://learn.microsoft.com/en-us/azure/active-directory/dev...

I had a good experience when I went in to a project with the attitude that "Ok, I'm going to wire up auth0 in perfect accordance with how they want me to, absolutely from scratch with the most perfect clean official documentation/library (in my case the react auth0 sdk) conforming approach possible.

I spent a lot of time making sure I did everything as by the book as possible and , in the end, I had a good experience and felt confident in my implementation, and like I understood all the moving parts. I even got help in a SO thread from an auth0 employee.

Auth0 could have been anything, here... On the same project, I kind of did the same thing with vite/react/tailwind client and fast api backend where I touched every piece of it, made every decision, and made sure I was really confident. This was in my own little vacuum where a big part of the app was just creating my perfect little sandbox. Stuff gets way more complicated when you have to hustle to get stuff over the line or you're trying to cram so new auth implementation in to an 8 yr old app that's been walked around on by dozens of engineers slamming out slop to close a ticket.

(Note: I know auth0 is a little more on-rails than implementing auth0 without it. They tend to have pretty good guides if you're using current hotness like react or fastapi)

My preferred development style is

1) skimming for limited context

2) copying and pasting random code

3) trying it with print statements

4) getting stuck

5) repeating steps 1-3 until it works

It's interesting to hear that because IHMO one of the reasons OAuth and JWT took over the world is that you can base64 decode the tokens and see whats inside them, compared to Kerb or NTLM which you eventually learn to spot based on their binary headers or whatever (eg NTLM tokens in HTTP Headers always start with "TRIM" for some reason)

I get the problem though, many of the libraries are not great or simply difficult to use

Well, my experience is like install keycloak, configure some framework plugin (like Spring Security) and that's about it...

I have rough understanding of oauth but never had to dive in.

Yeah, pretty much. We have a hidden iframe silently refreshing tokens.

Don't ask me why, I thought the whole point of OAuth was that example.com was allowed to access identity.com. Instead, example.com opens a hidden identity.com iframe and does what do I know?

At my old job, users could specify their own IdentityProvider for their instance, which added a whole layer of complexity.

Now we have found out that a different team has implemented their side of OAuth completely wrong: Their device flow ("Open in app and enter 56474") doesn't poll automatically. Instead, user have to click "try now" manually. The whole auth is lost when the device restarts.

I'm not surprised Microsoft lost control of Bing because they implemented OAuth wrong for one of the services displayed there.

And each provider you intend to support exponentially clutters up code in the frontend and your /authenticate endpoint in the backend. Imports for each one, IF/ELSE statements to handle each one.
OAuth sucks. It's one of the worst abstractions of our time. It's incredibly easy to implement some half assed spaghetti and call it "standard OAuth".
I suspect it’s either by design that it is this terrible and hard to deal with or larger auth companies have a vested interest in keeping it this terrible.
There is no grand conspiracy here, and the problems OAuth is trying to solve can be pretty messy regardless of the approach.

When the environment involves delegating authentication across service providers, dealing with browsers and native clients, mobile apps, etc, there will not be a simple solution.

This is made even more challenging by constantly evolving application and edge deployment architectures, each bringing with it a new element of complexity.

OAuth sucks, and I’m sure it could be replaced with something better, but that replacement will also suck and bear a passing resemblance to OAuth, because the underlying problem is a messy one that isn’t going away soon.

It doesn't suck, it's literally bare-minimum what you need in order to securely retrieve the token.

It's by far not THE worst, you're spouting total nonsense. What's THE worst is lack of attention, and one needs quite literally 10 minutes to read the RFC and understand it's fairly simple protocol with minimal number of parameters.

I'm sorry you had a hard time with OAuth, but have you ever thought the problem is in you and not the protocol? Reason I'm asking is because you declare it sucks but you are not providing any kind of alternative of how it would not suck.

> and one needs quite literally 10 minutes to read the RFC

rfc 6749 is 4259 words according to wc -l. You mean that it takes 10 minutes to carelessly skim it.

Never mind that there are several other OAuth RFCs.

You need 10 minutes of focused reading to understand what the purpose of the protocol is and you don't need all of the OAuth RFCs to implement the bare-minimum nor do you have to support all the grants available.

Most people I worked with don't understand the purpose of OAuth and that's what the 10 minutes should be invested into.

Or, you can, you know - nitpick and live in the world of gloom and doom where everything sucks.

For what it’s worth, I taught a series of classes/labs intended to help developers within my former employer’s ecosystem understand OAuth.

What I learned after delivering the content to hundreds of devs over the years: OAuth confuses people, and for completely understandable reasons. Most people think about auth in terms of usernames/passwords or API keys. The mental models associated with these do not transfer to OAuth, and there are enough moving parts with client and server-side interactions that someone not already steeped in the authn/authz space will have some catching up to do.

Forming an understanding of the basic concepts rarely happens in 10 minutes, and if that’s your experience, good on you. Most of us bang our head against the wall for a bit and some of us go on to teach classes about it.

Bottom line: OAuth is hard. That is not the same thing as the general concept being simple.

> Most people think about auth in terms of usernames/passwords or API keys.

OAuth is hard to explain here because it is not an authentication protocol, and without extensions has severe security flaws in being one. OpenID Connect is the most popular effort extending OAuth to also handle authentication.

But that doesn't change that the thing OAuth is most associated with is not what it actually sets out to accomplish.

Yeah, this misconception is at the heart of so much confusion, and far too many OAuth implementations were clearly borne of "well, the customer was demanding we support OAuth so we implemented it", and there was clearly no understanding of the distinction between authn and authz, or what use cases are actually getting solved.

I once worked with a vendor who provided an API and required all API clients to use OAuth with Auth Code Grant Flow. The API was designed to support backend automation use cases within the product, and could execute functionality behind the scenes headlessly. Auth Code Grant Flow requires interactive login by a user sitting at a keyboard, and they never stopped to consider that backend automation use cases are not going to work well if someone has to periodically execute an interactive auth flow. This wasn't a once-in-a-year occurrence since they enforced relatively short refresh token timeouts.

The degree of confusion and the prevalence of that confusion on this subject is quite high. Someone gets a task to "Implement OAuth", and they see that Auth Code Grant Flow is the most common, so that must be what we should implement.

> Forming an understanding of the basic concepts rarely happens in 10 minutes

It happens when you're working and have sufficient experience, knowledge and practice when it comes to web, programming and HTTP.

It's impractical, irresponsible and insane to have anyone other than a person with sufficient knowledge to work on application / API that supports OAuth and for person with proper expertise - the protocol is simple because it's literally the bare minimum needed to establish secure request/response pattern.

I work with SAML 2.0 and OAuth2/OIDC, I deal with programmers and other people within areas of API, authentication, authorization and it is true that majority of people I interacted with are having troubles understanding these protocols. However, what I can attest to is that they're also extremely underwhelming as people working in IT, with bad work habits and misplaced focus. It's impossible to explain or clear up the protocol to such audience because they lack much more when it comes to knowledge and discipline. But, I did interact with several brilliant people with far less experience compared to me who did get the purpose of this protocol and who implemented it successfully and have done so quite fast.

What the wall of text means is that where there's will - there's a way. You can't force knowledge into audience who can't or refuse to understand it, so there's no point in even trying to do it let alone draw conclusions how the protocol is hard, confusing or difficult when there's whole knowledge void in other pre-requisite areas.

> it is true that majority of people I interacted with are having troubles understanding these protocols. However, what I can attest to is that they're also extremely underwhelming as people working in IT, with bad work habits and misplaced focus. It's impossible to explain or clear up the protocol to such audience because they lack much more when it comes to knowledge and discipline.

In my experience, IT generalists are dealing with a thousand problems, and that has little to do with misplaced focus and more to do with the fact that management just spent $$$ on this new platform and needs it integrated with internal systems, and they're encountering this OAuth thing out of necessity.

Part of the reason my training materials were successful within that community was because they distilled all of the usual questions about how and why someone would use OAuth relative to the platform and presented essentially a menu of recommended options to be explored further depending on their use case.

And people found success with this approach because it helped them solve their very immediate and concrete problem while also explaining why the approach solves that problem.

> You can't force knowledge into audience who can't or refuse to understand it, so there's no point in even trying to do it let alone draw conclusions how the protocol is hard, confusing or difficult when there's whole knowledge void in other pre-requisite areas.

Different people have vastly different learning styles, and some people just don't do well if you hand them a dense specification. Working from concrete use cases can be far more productive since it meets users where they are and gives them a pathway to understanding how their use cases connects to the OAuth concepts.

In a perfect world, everyone who works on auth would be experts. In the world we have, quite a few people have no choice but to do their best to learn the subject matter to solve the problem at hand.

Unfortunately this also leads to quite a bit of confusion and some really problematic OAuth implementations.

Can you share any of your training materials?
Unfortunately I cannot as they all belong to that employer and there are some privacy implications to linking the content as-is, but I’ve been thinking about reframing the ideas behind the content as a more general intro to OAuth and publishing a series of vendor-agnostic blog posts and/or videos.
An abstraction that allows a landscape of experimentation and learning on an interrupt based general purpose machine? What a bunch of thoughtless assholes!
What I don't understand is why all of these complicated SSO technologies like OAuth, OpenID Connect, SAML, LDAP, etc are integrated at the application layer at all. All these web applications should be using something like "Trusted Header SSO" [1], thus completely offloading authentication to the upstream reverse proxy (which does or should exist anyway) and then the whole problem vanishes at the level of the application. It's like the MxN problem that Language Server Protocol addresses, but here "M" is applications and "N" is authentication protocols. The fact that I have to hand over my administrator password to the grubby memory of random shoddy applications as they pass it over LDAP to authenticate me in my corporate "holy grail" SSO-everything environment is insane to me.

[1]: https://www.authelia.com/integration/trusted-header-sso/intr...

OAuth isn’t an authentication protocol, it’s authorisation protocol (which attempts to solve nebulous authorisation delegation scenarios in an often overly generic and overly complicated way).
For your consideration, centralizing auth like that imposes the requirement of (a) running a second piece of security tech (b) ensuring that all upstream applications are in the same authn/authz domain as the front proxy

That's not even getting into the fact that the "SSO technologies" that you listed have vastly different mental models of what inputs and "claims" they support. Maybe you're hinting at https://xkcd.com/927/ but for better or worse I don't believe there is a magic wand authn/authz standard like you want

I don't understand.

For example: a CI service wants to write to a Slack channel.

The CI service produces a request and directs me, the channel owner, to Slack where I confirm the request, and then the CI service obtains the scoped access token.

How is this replaced by anything to do with an "upstream reverse proxy"?

EDIT: I think you are thinking of OAuth as SSO. It can be used for that, but that's not it's "true" purpose like it is for SAML.

---

> The fact that I have to hand over my administrator password as they pass it over LDAP to authenticate me

Yes, that is insane and why LDAP is generally no longer used for web applications.

If you only want to put a dumb web app behind a proxy then use oauth-proxy. But OAuth caters for a lot more than that use case.
Related gripe: Your API doesn't always need OAuth. If I'm using your product as your customer and I directly want to leverage my own product data via your API then I shouldn't be forced to have to implement [your almost invariably non-standard and difficult] OAuth implementation. An API key should be an option or HMAC if the extra security is felt to be warranted, but not the absurdity of needing to go through a client flow to check the boxes to say yes I consent to share my own data with my own self for this API use that will never be seen or used by a 3rd-party.
> If I'm using your product as your customer and I directly want to leverage my own product data

Correct. Fortunately, in my personal experience many services offer exactly this (JIRA, Hubspot, Slack).

OAuth is only relevant where the resource owner and client are different parties. I.e. it's for third party clients, not second party clients.

Not to mention needing to create awkward shared service accounts to impersonate.
OAuth client credential flow is this easy mode you ask for. It’s one call with a secret in it and back comes a token.
The problem is "client credential" flow is not always available... And even if it were it might not as easy as you claim. In particular if you want to have an access token working on several instances. Most implementation allows only one access token at once so you need to have a shared storage and synchronization just for that...

If you are developing an API please have mercy: provide something other than OAuth. For me, avoid OAuth unless there is 3 distinct parties.

If you want to have password reset, OTP, 2FA, webauthn and so on, you probably want to use an external authentication provider. Integrating it with oidc/oauth is much easier, than doing all that stuff by yourself.
I ran into that recently trying to connect a web shop we've built to an accounting platform. The web shop handles the entire checkout flow, but the owners also want a copy of the invoice stored in their accounting platform. The platform has an API. So our web shop can ping the accounting platform after an order is placed, and sent them a record of what was purchased? No. Because the API requires authorization. So the admin has to occasionally log into our website and hit a button to send invoices to the accounting platform. (At least we can send multiple invoices at once, so it doesn't require them to hit a button on each individual one.)
this. im baffled how most platform that has user accounts does not allow provisioning of service accounts so users can easily access their data.
Love this post by Nango, it's exactly right!

Agreed that the biggest reason is that OAuth is a framework and not a protocol. That means different companies can do it ever so slightly differently, and as a dev, you need to understand the differences and normalize it for your use case.

At Stytch we've run into the same issues, normalization problems, security implications, flaky IdPs; a hard problem, but one you _have_ to get right!

> OAuth is a framework and not a protocol

Indeed, a lot of people do not realize the difference. Frameworks do not provide interoperability, and at most we can push to try to get people to solve problems the same way.

Profiles (such as OpenID Connect or FAPI) constrain frameworks allowing implementations to be interoperable.

OAuth is somewhat like the multitude of different dialects in some places like the United Kingdom.

The original article was talking about the problems due to trying to support all those different OAuth dialects. I'd argue most developers will never know that degree of pain, because they are only trying to solve problems within their particular application space.

OAuth is such a pain in the ass for small projects. I miss api keys.
I integrated my product using OAuth 2.0 with 3 other systems from 3 different companies. Each one required different code on my side. Not fun at all.
It's easy with https://btn.social/ :D

I made this because I was tired of asking the same question.

There are a bunch of services out there trying to address the same thing, but unfortunately they get wildly expensive and they own/control your user data.

Professionally, I've been a consultant and freelance developer for 10+ years. Time and time again I'd see teams start with Auth0/etc, only to eventually:

1. duplicate user data into their own DB/Cache to avoid the latency of asking Auth0 for user data

2. remove Auth0 entirely because having active users !== having paying users... so Auth0's monthly bill of $3000 to $10000+ couldn't be justified

3. #1 then eventually #2

btn.social is just as simple to set up, but has predictable & transparent pricing with volume discounts the more you use it. At its most expensive, it's 100 logins per penny ($0.01), which is to say that "MAUs" are a thing of the past.

As quick pricing comparison:

- Auth0's (public) pricing calculator [1] shows 10k MAUs for $228/mo. That just means 10k users logged in 1+ time(s) that month. In btn.social terms, that's 10k, 20k, or 30k logins depending on if each of those users logged in 1x, 2x, or 3x (etc). For $5, you get 50k logins with btn.social.... and for $169 you get 2.5M logins... [2]

- Supabase charges $25/mo, which includes a lot more than just OAuth(!) but if you look at Auth alone, that $25 includes 100k MAUs and is an additional $0.00325 per MAU thereafter [3]. Assuming 1x login per MAU, that's $2950/mo for 1M users. With btn's "Business" tier, you get 1M logins for $79/mo, but of course there's still the $169 for 2.5M if you don't like the "1x login per MAU" assumption.

Another key difference is that btn.social saves nothing about your users.

It's your app so you get to keep your data. You'll never need to have to ask btn.social for Alex's profile because we won't have it :D This means we're privacy-first & we don't add/pose a risk for GDPR concerns.

btn.social was just launched last week [4], so some additional guides & examples are still underway, but there are already 10+ OAuth providers [5] to choose from & a free/"Hobby" tier so anyone can start playing around in just a few minutes.

[1]: https://auth0.com/pricing

[2]: https://btn.social/#pricing

[3]: https://supabase.com/pricing#compare-plans

[4]: https://twitter.com/lukeed05/status/1648751062340501505

[5]: https://docs.btn.social/providers/

First off, congratulations on launching! I'm not sure why you're getting downvoted, because it seems quite relevant to the discussion. I'll definitely look at btn.social for my next hobby project. The pricing is super reasonable and the documentation very clear.

One question I have around OAuth in general is whether it only applies for SPAs or if I could also use it for more traditional multi-page apps? I guess I could do something similar to what you're doing in your guide [0] and do a POST from the onlogin callback, but how does my backend know that the payload is valid?

[0] https://docs.btn.social/guides/#link-a-payload-with-a-db-rec...

Thank you! Not sure, just part of the orange site mystery~

Your `onlogin` callback will only ever be invoked by the SDK on successful login. So the data coming thru there is always the result of the OAuth flow itself. For MPAs, you can create your own document cookie and/or modify local/sessionStorage, and then every new page load will pull/load from those storage systems. SPAs can & should do the same – they just have the added benefit of keeping in-memory state alive.

As for POSTing to your backend, the same XSRF/CSRF/CORS rules & mechanisms apply as with any other client->server transaction. You can rely on CORS for limiting who can talk to your API, you can pass along a unique token for CSRF prevention, and/or come up with your own Authorization header format.

Soon I'll be adding a `redirect_uri` config option for all btn.social apps. Much like the normal `redirect_uri` in OAuth, this will be a target URL that btn.social redirects users to post-login. This will enable server-side redirects, which means that your API just has to verify the "login.btn.social" referer.

Ah great, thanks! It would be great to see a larger example on your site to see how this all ties together. For example, I don't really understand what's stopping anyone from opening dev console and just calling my 'onlogin' callback and passing it fake data to impersonate another user. It seems to me that somewhere I'd still need to verify the user data with the provider.

I fully realize that that's probably due to my own ignorance and no knock on you. However, maybe there's others like me who'd benefit from some more examples.

For sure! There are some larger examples coming so that people can see it plugged into a more real-world application.

RE: onlogin, that method accepts a callback handler that is added to an internal/private array of callbacks to run once the `popup` or `redirect` sequence has finalized. It's the only externally-facing "doorway" into the OAuth flow. Everything else is inciting action (`popup()` and `redirect()`) or a read-out of the last-known payload (`last()`). It's actually very similar to a state manager I wrote a while back[1]. Callbacks are added via `$.on()` which assembles an internal `tree` map to be `dispatch`d once a change happens.

You've probably already seen the API docs for the btn.social SDK, but linking just in case[2]

[1]: https://github.com/lukeed/vegemite/blob/master/src/index.js

[2]: https://docs.btn.social/usage/#methods

Probably because the part most of the world is actually interested in was later renamed 'Open ID Connect' and any attempt to research using the term OAuth will bring you nothing but incorrect, old, ad-hoc implementations and presentations by snake oil selling 'personalities' claiming they understand it.
Naw, the same applies to oidc. Especially in the browser.

The issue itself also sounds easy enough to implement that you might get suckered into rolling your own (you only need to do a redirect and a request every n-minutes) ... But it's surprisingly hard as there are a lot of edge cases with long running timers and potentially multiple browser tabs/windows etc.

And once you realize you actually want OpenID Connect and none of the OAuth 2.0 features, you stumble about the fact that OpenID is not what you want because they just share a name... Where was I again ???
because it was a dumb standard with terrible implementations solving a problem that nobody had
Right. Do all of those dumb dialogs that allow you to sign in using your Google account, import data from one cloud to another, add integrations from third party providers to other apps… they are all solving problems you don’t have?

OAuth is one of that pieces of infrastructure that power lots of things without people even noticing them, but don’t bode well with armchair engineers.

I'm an application develop who uses oauth, but it's only one in many authentication systems I've used, and certainly one of the worst.
Which others have you used which solves similar problems, but better?

Genuinely curious.

Well, I use oauth only for authentication (IE, use it to get a trusted email address for the web user of my app), not authorization. Of all the systems I've used, kerberos has been the best authn solution, then basic auth to log in as a user where authorization is done in another ACL system (FS acls, for example).

My complaints are almost entirely about the ergonomics of writing oauth code in a server, provisioning the resources to make it work (like DNS), and then debugging 900 redirect_uri errors.

Which highlights the very problem I was talking about, considering OAuth is an authorization protocol, not one for authentication…
My favourite are the appendices that amend the protocol to fix security issues that almost no API seems to implement properly. These can get quite long and are frequently amended with new drafts every year or two.

For example, most APIs don't implement PKCE in order to prevent injection attacks against the authorization code grant type.

Just keeping up with security practices is a full time job for teams.

Nice read from nango team. FWIW we are using nango/nango cloud in prod to support ~15 integrations.

We signed up before they had an admin dashboard and did everything over cli. It was easy then and even easier now. We have also contributed a couple of providers when they didn't have apis we wanted to use setup yet (Stripe, Zapier NLA). They have an open slack channel and we were able to get them shipped immediately.

I am in the middle of implementing an OAuth 2 server for my internet forum [0]. The last 10 or so commits are OAuth related. I read docs for about a month and now I feel like I can code it. One thing that confuses me is how it can work without a client secret (it's recommended to not use a client secret for SPAs and native smart phone apps).

[0] https://github.com/ferg1e/comment-castles

So make sure you are doing PKCE for public clients.

With that, and the redirect URL (and therefore trusting DNS), and the other browser security model stuff…. You’re in fairly good shape.

There’s newer standards coming like DPoP - but it’s probably not worth it yet.