Because the documentation is bad. Oauth is really simple:
Lets say you want to use google as an auth provider. You do this:
"Hey google who is this guy? I'm going to send them to google.com/oauth, send them back to example.com/oauth, and in the headers of the request include the word "Authorization: bearer" followed by a bunch of text"
Google says "Oh yeah I know that guy, here I'll send them back to where you said with a token"
Then later on you can take the token and say "Hey google, somebody gave me this token, who is it?"
That's pretty much it. You have to trust that google isn't lying to you, but that's kindof the point of oauth.
But that's never what the documentation says. It's always 10 pages long and the examples are like "here's a fully functioning python web server using flask and function decorators, oh the actual auth flow, which is really like 3 lines of code, is hidden inside of a library".
To people who write documentation: PLEASE for the love of god show me how to talk to your API both using your library, but also using something like urllib2 or requests or something.
Ideally the documentation is the absolute most minimal way of making the service work, and then adds more and more usefulness on top of that. I'm not going to judge you for writing bad code in an example. The example could practically be pseudocode for all I care. I just want to see generally how your API is supposed to work.
In my experience with OAuth, one of the principle issues is that it's less a protocol and more a skeleton of a protocol. Actually go to the core RFCs for OAuth, and you realize that basically everything is implementation-dependent. You somehow register with the provider as a client (out of band and completely implementation-dependent).
Then you ask them to log you in by sending a request to an implementation-dependent webpage, with implementation-dependent parameters (which ones are required are--you guessed it--implementation-dependent), telling them to redirect you to your page when you're done. Well, actually, that assumes you're building a website. If you're a desktop application, you'll do something else. If you're on an embedded system that can't open up a webpage, there's another option. And there's yet more flows. Which ones are supported by the provider? You guessed it, it's all implementation-dependent. Oh, and maybe you need to refresh tokens to login the future. When? If? You guessed it, implementation-dependent!
It makes writing a generic OAuth client library hard because there's basically no commonality there. Really, it makes me long for Kerberos as a much saner SSO system than OAuth.
> In my experience with OAuth, one of the principle issues is that it's less a protocol and more a skeleton of a protocol
OAuth supports lots of different scenarios. Many people when they say “OAuth” they are only thinking of one or two of those scenarios, and ignoring all the others which aren’t relevant to them personally-but may be relevant to someone else
I worked on a system where we had a micro-service which had a token from OAuth server 1, and it needed to exchange it for a token from OAuth server 2, and we needed some policies to decide whether that token exchange was allowed or not. And that’s totally a use case the OAuth RFCs support (there is even an RFC specifically on token exchange), but a person wanting to add a “login with Google” button to their website isn’t interested in anything remotely like that.
> Which ones are supported by the provider? You guessed it, it's all implementation-dependent.
Security needs vary widely from system to system. So they defined a protocol which supports many different scenarios. But if your application only needs two of them, why implement the other N? OTOH, for someone who actually needs one of those other scenarios, having it standardised makes their life easier. You can’t expect the protocol to tell you which scenario you have, that’s inherently “implementation-dependent”, to the point that calling it that is getting tautologous
> > In my experience with OAuth, one of the principle issues is that it's less a protocol and more a skeleton of a protocol
> OAuth supports lots of different scenarios.
But it's literally only the skeleton of a protocol ("framework" of a protocol, as the actual spec puts it). That important "Then later on you can take the token and say 'Hey google, somebody gave me this token, who is it?'" part is totally unspecified by OAuth. How do you validate tokens? (Either on the resource-server end, or on the client-end to know if trying to talk to a resource-server is a waste of time, or to know if your post-redirect-uri is getting spoofed):
- OAuth: implementation defined
- most implementations: use our library, which validates the token using undocumented logic
- OIDC: there's a user-info endpoint you can call to, but lots of OAuth authorization servers don't fully implement OIDC, and even if they do, that's a lot of extra round trips
- RFC7523: The token is a JWT, validate the signature and claims... but everyone issues RFC6750 "bearer" tokens, not RFC7523 "urn:ietf:params:oauth:grant-type:jwt-bearer" tokens. But if you just close your eyes and pretend that your 6750 opaque token is actually a 7523 JWT and parse+validate it as such, that'll work most of the time.
So yeah, OAuth is hard because there's no great generic library, because a core part of it is implementation-defined, so you have to either do it yourself or use a specific implementation's library, and none of those libraries work quite the same.
(In the above, and for 99% of folks, "OAuth" = RFC6749+RFC6750)
In all honesty, though, "I have to look at the provider's documentation to determine like 4 URLs to add to a configuration" is not that wild of a thing? How much friction is too much friction?
> How do you validate tokens? (Either on the resource-server end, or on the client-end to know if trying to talk to a resource-server is a waste of time, or to know if your post-redirect-uri is getting spoofed):
At the beginning of the "standard" OAuth flow you can pass in client state. So you generate a signed nonce. Remember, you are starting this flow, so you mix in some user data (avoids CSRF problems), and you are pointing to some HTTPS site.
This is not 100% perfect compared to "remote server has some signing" but it does allow purely local verification of tokens when they are first received, as you can stick stuff into the "state" request parameter (as anyone should be anyways to avoid trickery).
> In all honesty, though, "I have to look at the provider's documentation to determine like 4 URLs to add to a configuration" is not that wild of a thing? How much friction is too much friction?
If it's something dumb/simple like grabbing a JWKS URL and an "issuer" URL, and then doing JWT validation, sure. Maybe you need to make a weird bespoke request to some other endpoint. Maybe you need to implement some uncommon signature verification. It could be anything.
> At the beginning of the "standard" OAuth flow you can pass in client state. So you generate a signed nonce. Remember, you are starting this flow, so you mix in some user data (avoids CSRF problems), and you are pointing to some HTTPS site.
So you redirect the user's browser to the IDP, with the nonce in the URL. The user grabs that nonce from the URL bar, then manually navigates to your redirection-endpoint, putting that nonce and their own fabricated token in the URL.
The server generates the auth code and redirects the user agent to your callback. You exchange that code with the IDP (over HTTPS which yeah that's its own nest of wormy trust) to get back a token. They can't inject a token because you don't get the token from them, just the one time code. If it's opaque you introspect it to validate or you just validate the JWT signature after pulling the keys from the JWKS endpoint. Introspection is standardized and an RFC. The state param is just a fucking session identifier.
All these URLs are defined and provided via the .well-known/openid-configuration endpoint. If your IDP publishes that endpoint correctly, most OAuth2 client libraries Just Work (TM) when pointed at the IDP domain.
Do EITHER of you even use OAuth2 outside of just cargo culting something you found off GitHub?
My apologies, it's been a while and I was forgetting about the authorization-code exchange step. So yes, for the most part "client"s can treat the bearer token as opaque. But "resource server"s absolutely cannot.
> If it's opaque you introspect it to validate or you just validate the JWT signature after pulling the keys from the JWKS endpoint.
If it's a JWT, which it doesn't have to be. OIDC allows the token to either be an RFC 6750 opaque token, or an RFC 7523 JWT, and overwhelmingly implementations use a "bearer" (6750) token. But, most of the time it's a JWT, so as I said, closing your eyes and pretending it's a 7523 token works, and so then you can just pull the keys from the JWKS endpoint.
> Introspection is standardized and an RFC.
In my experience, it is exceedingly rare for an IDP to implement RFC 7662 token introspection
> All these URLs are defined and provided via the .well-known/openid-configuration endpoint.
Yeah, OIDC-discovery is pretty sweet. And if the IDP implements OIDC-discovery, then it probably implements OIDC-core, or at least enough of it that you can use the user-info endpoint, like I mentioned. But I've seen IDPs that don't.
Oh, thank you for reminding me of the well-known endpoint, was having trouble finding it in the OAuth RFCs but I wonder if it's pulled in elsewhere.
> They can't inject a token because you don't get the token from them, just the one time code.
that's not correct in the most common flow? The most common flow involves the user agent providing the information via a GET, so they can theoretically provide a token.
The reason the state parameter is important is because without it, a malicious actor can make a link that goes directly to your system's "oauth step finalized" step, but with their credentials. (Pedantic attack: service has slack push notifications integration, through OAuth. Attacker creates link like https : //service/connect-slack-finalize?token=token-to-attackers-slack that victim clicks on. Without using state, the service will just take the token and stick it into some slack integration. now attacker's slack is getting messages for the victim's account on the service. Cookies mean that the victim's thing is accepted immediately).
.well-known/openid-configuration is specified as part of OpenID Connect (OIDC) Discovery[1]. OIDC is separate from and on-top-of OAuth2. The OIDC specs come through the OpenID Foundation, not through the IETF (so not RFCs). (Also, while what they specify is super useful, they aren't nearly as well written as RFCs tend to be :) )
No, he's right, I was misremembering (assuming "the most common" flow is the "authorization code" flow specified in RFC 6749 §4.1). The user-agent provides a one-time "authorization code" to the client via a GET, and then the client receives that "authorization code" and does its own POST to the IDP to exchange that "authorization code" for the final "access token".
This is me misusing the word "token". Access tokens are gotten via POST, but the one-time code is gotten via GET and, absent usage of things like the state parameter, can easily lead to malicious attacks.
That's something that isn't OAuth2 or your end point is accepting something insane.
Are you talking about the PKCE variant of authorization code flow which is what replaces implicit flows in native apps and SPAs? Because those use code_challenge and code_verifier fields, not the state field. If you're doing all that in the state field with signed nonces you really should move to PKCE.
It's to prevent CSRF attacks. The attacker writes their own client and does half of a login on their own end (getting an authorization-code, but not yet exchanging it for an access-token), and then tricks the end-user to navigate to //service/connect-slack-finalize?code=<attackers-code>&state=<whatever>. But with the state parameter, the client can check the state parameter against a session cookie that it set previously, and say "wait a minute, this is the conclusion of a login from a different browser". Depending on what all session-state the client is keeping track of, it may make sense to sign that state-parameter-nonce to avoid having to remember session state server-side; but the simple case would be to just check whether it == a cookie value.
Using [0] as a reference, I'm talking about Step 3. This is, in my experience, the "normal" way that people are setting up OAuth between 2 services, with a user going through the flow.
[1] includes info on this (see "flawed CSRF protection")
Aha! That makes sense! Yes that can be a problem. We exclusively use a single (our own) IdP so it's less important for us. But good to know as some future feature work will actually make this important.
One thing I could imagine here is a signature flow happening from the sender side where they sign the nonce + the token, to avoid forgery. I don't know what the signature validation looks like in that model though (if you request a public key from the provider, are you requesting that at every request flow? If you're not, now you're adding a request to every normal, non-malicious validation flow)
> Maybe you need to implement some uncommon signature verification. It could be anything.
So my feeling on this is that at the very least OAuth tends to be "well it's going to look like this, but there might be some tweaks".
I have set up OAuth verification with multiple services in the past. The social aspects and the business aspects have always been more complicated, but at least "it's OAuth" (much like "it's REST" for APIs) establishes a core understanding very quickly.
I'm open to saying that we should add more standard stuff to the flow, if it's optional then people who use oauth libraries will end up with a lot of this stuff by default. But I think the status quo is pretty nice, all things considered.
My apologies about "to know if your post-redirect-uri is getting spoofed" and then my example exploit; I was misremembering and forgetting the authorization-code step.
If you're only implementing a "client", then yeah, you can probably get away with never caring about inspecting the token. But if you're implementing a "resource server", then you'll need to, and it's all implementation-defined (but overwhelmingly that implementation is "it's a JWT").
Any friction whatsoever is too much friction. By default, any user should be able to use any provider for any service, with no prearrangement between the service and the provider.
Anything less than that predictably leads to the situation we have now where a very small number of very large providers control identity and authentication for way too fucking many things.
> And that’s totally a use case the OAuth RFCs support (there is even an RFC specifically on token exchange), but a person wanting to add a “login with Google” button to their website isn’t interested in anything remotely like that.
That's because OAuth in the industry has been changed to only talk about authentication (AuthN) and not (or very lightly) authorization (AuthZ).
And, for the better, AuthZ is so use-case specific that bundling it together with AuthN is just asking for trouble.
Consider AWS and IAM permissions. How would you implement an IAM AuthZ policy system with OAuth? Would you actually want to?
AuthN is a relatively simple process which is why that flow through OAuth (and specifically OIDC) is fairly well trodden and defined. The OAuth Authz capabilities are infrequently used which is why you see them being so spotty.
And this is why there's five "log in with X" buttons on every webapp, but no "tell us your IdP and we'll send you there"; let alone any kind of magical "we'll take your email address and check the DNS record of the domain to figure out who your IdP is, and then send you there."
Not because the "figuring out who to OAuth with from DNS" part would be hard to design; but because there's no way to "autodiscover" all those implementation-dependent details of how you're supposed to talk to an OAuth IdP.
Compare/contrast: SAML. SAML binding a webapp to an arbitrary IdPs just by punching in the IdP's SAML config endpoint URL? Works perfectly.
There's a single URL for OIDC IdPs too. It's not supported in every web app for the same reason SAML isn't: it's a two-way exchange to set up, not a one-way exchange. In OIDC (OAuth 2) you need to register a client first. In SAML you need to register a Service Provider first. Only after you've done those steps will you be able to plug the magic configuration in and have it work.
SAML is too limited to compete with OIDC/OAuth2 on the web, but it's got some advantages for enterprise authentication, so it won't be going anywhere.
The only reason an authorisation server would support dynamic client registration, however, is because it's meant to be pluggable as the back-end for an API integration suite like Kong Enterprise.
The solutions that are one big well-known IdP have no reason to want it, so Google Auth, Amazon Cognito, and similar all don't support it.
The solutions that are aimed at letting organisations be an IdP do have reason to offer it: it makes being an IdP easier, because you can stand up a client registration service without using vendor-specific interfaces.
No-one who operates an IdP wants arbitrary, uncontrolled public client registration. It'll get abused directly, and it'll enable further abuse of the systems the IdP is meant to protect.
This is my experience. It's hard because it's very complex. As a non-domain-expert it's hard to separate necessary complexity from unnecessary complexity, but it feels like there's a lot of the latter, plus enough gotchas in the former ("oh, you didn't specify an encryption algorithm that we support in your request, and our logs are terribly unhelpful for realizing that") to make it a giant pain.
Not exactly. With webauthn the complexity lies in frontend - you have to explain how and why users should use this and what it is, how to manage enrolments and so on. Touch ID, Windows Hello and Passkeys might simplify this but it is still more complex to explain to less technical customers than, say, Face ID on an iPhone app to remember your login.
If you want an easy all-purpose login flow, sign in by clicking a link in email is still an easy fallback.
OAuth from multiple providers also has complexity - Auth0 makes it so you have to maintain separate databases for passwords if you want to support login via OAuth and login from password. You have to link accounts to login from multiple providers.
Logins are simply hard work no matter how you slice it. Eventually they will be easy but… I’m not holding my breath. :)
If you were generally comparing standards: yes. Documentation with it is a breeze.
If you're talking about a "replace one standard with the next" type of comparison, then no. You can read more about why here https://oauth.net/webauthn/
+1, it really solves nearly all the authn and federation problems. You still find KDC installations in places with large *nix footprints. Sometimes it's AD, sometimes it's MIT with a cross-realm trust.
It's incredibly flexible and transparent to the user. It's easy for sysadmins, and various service owners to implement as it's basically drop a keytab in place, and set an environment variable for many daemons and libraries.
IMO, the only reason it fell out of favor with the web crowd is there wasn't a gaggle of centralized providers that let them stand up services without thinking about the infrastructure. It wasn't packaged up nicely.
As a developer that needs to use Kerberos for authentication between hosted services, I can say that Kerberos is simply awful to debug. When you have a failed login (refuse to auth), it is so hard for the _average_ developer to debug. We have pages and pages of cookbook solutions to various problems. Some issues are purely due to Kerberos complexity, and other issues are due to each programming language and their implementation of Kerberos. Most developers have no idea how SPNEGO works (Kerberos auth over HTTPS). I spend a lot of time on these issues, and I only have a surface understanding of SPNEGO. Oh yeah, and there are many subtle issues of Microsoft Active Directory (AD) vs MIT Kerberos.
Kerberos just doesn't solve the same problem: it starts by giving your username and password to a fully trusted client.
OAuth was invented to avoid the need to do that. If I use some finance package and I'd like it to download my transactions from my bank, I'd really rather not have to give it my username and password, with the full access that grants including the ability to lock me out of my bank account and transfer my money. OAuth lets you instead grant permissions for that program to do something more limited, like just read your transaction history.
Kerberos was never popular with the web crowd because it's not capable of solving the problem of authorizing web applications. Before OAuth everywhere just asked for your username and password to log in on your behalf.
And the only reason Kerberos works well today is that it's always now a single-vendor solution, homogeneous across the deployment. It's an interoperability nightmare otherwise.
> Kerberos just doesn't solve the same problem: it starts by giving your username and password to a fully trusted client.
Not really. In practice, Kerberos means just loading the gssapi or sspi library, essentially using the user's login to already have the ticket-granting-ticket generated and usable for the subsequent tickets. As the client application, I never see the user's password; it's all handled for me by the OS.
> Kerberos just doesn't solve the same problem: it starts by giving your username and password to a fully trusted client.
That's incorrect. The client doesn't need to be trusted at all. There is also there anonymous auth, certs via pkinit (which give you yubiki and piv/cac cards as well), and otp.
> Kerberos was never popular with the web crowd because it's not capable of solving the problem of authorizing web applications.
In MS kerberos, group membership comes over with a TGT in the from of a PAC.
> Before OAuth everywhere just asked for your username and password to log in on your behalf.
I'm taking about kerberos, you'd use a TGT to log into that service.
> And the only reason Kerberos works well today is that it's always now a single-vendor solution, homogeneous across the deployment. It's an interoperability nightmare otherwise.
There are a lot of cross realm trusts between MIT/AD and Heimdall/AD, and they just work. It really isn't difficult. As I said in my original comment, it's just that it wasn't packaged up nicely. All the parts are there.
OpenID Connect, specifically, where a lot of people mix those up and start looking at using OAuth for authentication. And if I'm not mistaken, logging out is implementation-dependent in OpenID Connect. There's some pseudo-standards but implementations vary in my experience. And OpenID Connect also has multiple flows for different kinds of applications. There's still a lot of confusion and complexity here.
I'm still salty about how keycloak changed their openid logout behavior, removed the old behavior in the new version when there are still a lot of oidc clients out there that still expect the old behavior. I have two inatances of keycloak using the same version and somehow both have different iodc logout behaviors. I think it's due to one instance was upgraded from older version and inherited the old behavior, but I can't get the other instance to use the old behavior (the flag mentioned in the docs didn't work) unless I downgrade the version first.
Can you elaborate on this a bit? My logout process through keycloak is through a hidden (back channel) url. As long as I hit that client url it will end the session. Applications, I find, have different behaviors. Gitea logs out the session, portainer just clears browser cookies but the session remains active
I’m using the same keycloak setup for almost 2 years now, with upgrades
There is a flag to restore the old behavior but it doesn't work in newer version. Strangely, an older instance of keycloak I run still uses the old behavior even after being upgraded to latest version, so this issue seems to only affect new instance only.
Yes, single log out is an ongoing nightmare. <Stares at Ping>
As many here have said the size and range of use cases that OAuth and OIDC support is off its head. And that's with the big boys who have millions of users, throw in ${EveryCorp} that implements its own token server and bespoke implementation of the auth, well.. good luck to the AI trying to take over our jobs.
It's fully specified. It's well implemented - you'd have to go out of your way to find an authorization server that doesn't do everything above, with the exception of dynamic client registration, because that's not intended for clients but rather for integration with developer portals and similar. Google Auth and Amazon Cognito don't support dynamic registration for third parties, eg, because if you're doing dynamic registration it'll be because you're operating your own AS - Okta, Auth0, and Keycloak all support it.
There's also plenty of good generic OAuth client libraries. Spring Security, the oauth2 crate for Rust, etc.
Just because the standards exist doesn't mean everybody follows them. Most and maybe all implementations have some crazy customisation. It makes the standards and docs almost worthless.
But that's hardly OAuth's fault. I can blame HTTP/2 cleartext because none of the websites I'm trying to access are working in my browser, but it's not reasonable to blame a generic protocol for not providing my niche when it's the implementations that are broken. Imagine complaining to the WHATWG because Microsoft decided to host an IRC server on port 80 and none of my browsers work.
In my experience, OAuth works great. Standard libraries Just Work, login Just Works, all you need is a URL (usually standardised) and maybe the configuration the service is expecting (i.e. the names of the permissions you're asking the user for). Usually, that's nothing special to set up. In some cases, particularly with huge "fuck you we're big enough to be the standard" vendors, there are some stupid hardcoded values and workarounds you need to deal with because they couldn't be bothered to use a library or document their flawed implementation.
I've set up a Keycloak server and authentication against it is super easy to set up. Copy-paste a domain, two generated tokens, and maybe a URL if .well-known discovery isn't implemented by the client, and that's it. Things can be easy, companies providing "OAuth" support just choose to make your life hard.
I think the problem with the spec is that it’s way too large to fully implement when you only need some secure token exchange, so everyone does only the stuff they need.
If the spec was smaller people would go all the way to be fully compliant since the marginal effort would be minimal.
For a desktop application could use the password flow[0], no need to involve a web browser. Yeah, sure, you're now handing keys to the kingdom to that application, but if it's native software it could also have installed a keylogger or used an embedded browser component and extracted the data from there.
One reason is that the protocol itself is more complicated than you've described.
For example, Google won't give you a long-lived access token. You need a refresh token, and then you use that to retrieve access tokens, and continue doing that as they expire.
Why? I have not a flipping idea. Please, HN enlighten me how refresh/access token dichotomy improves the API.
It's really trying to solve a problem they created by statlessly handing out tokens instead of keeping session state on the backend - they have no way to revoke a token once issued, so long lived tokens are a liability. Solution? More complexity! Hand out very short lived tokens, along with a slightly longer lived refresh token, which only allows you get a new bearer/access token.
Yes, but checking refresh tokens will occur much less frequently than checking access tokens. So you can imagine, for example, access tokens being JWT, so they are cheap to check. But every so often you have to validate the refresh token against MySQL (or BigQuery or what-have-you), which is more expensive.
That's all clear to me. But technically there exists a method to revoke an issued token. It's just that long lived tokens mean potentially lots of them == increased storage cost. It would be pretty silly not to check for revocation. How would one implement logout otherwise? Relying on just clearing session cookies? What if I obtained those cookies using something else than a browser and I can hold on to the cookie jar? Not checking for revocation == doing it wrong.
The purpose of a refresh token to allow the app to short circuit the login process. Regardless of how long the token is issued for. It's perfectly okay to ignore refresh tokens altogether, if one wants to.
The whole point of access tokens is to not do expensive checks on every request. Signature checks out and isn't expired - you are free to go. This is a core design thing of OAuth, once access tokens are out the door they are very hard to stop, so only let them last for 5 or 10 mins and use refresh tokens to get new access tokens.
Refresh tokens are your chance to do all the expensive checks - maybe you are IP restricted or want to step up with MFA etc etc. Check revocation etc
Login state for the identity provider and for the client application are different. Cookies are a reasonable way to implement it. Cookies + session storage backend will allow you delete sessions on the server side if you are worried about users keeping themselves logged in (?).
Your app is also responsible for deciding who has access. The identity provider is just handling the “is this person who they say they are” part.
Checking for revocation is checking whether the IdP still thinks that token identifies the user correctly. I don’t think it’s universally true or desirable that refresh tokens get revoked on logout. I don’t necessarily want logging out of Gmail to log out of Spotify just because I used Google auth for both.
Very simple, actually. Access tokens are short-lived and are irrevocable - google services only check validity and expiration of those tokens.
Refresh tokens are more like session tokens/cookies - those get checked every time. At Google scale, checking it probably expensive, so they are using refresh tokens.
These aren't for end-user or development experience, those are for AS performance.
Even without refresh tokens, clients need to check lifetime. If the token is opaque and the lifetime is unknown, it is no different from a session cookie.
> Does that actually improve OAuth as a whole?
Like I said, this is an improvement for whoever is validating tokens and only them. Refresh tokens are not hard to use, not sure what's the confusion here:
There's the issue of scale, but also the issue of reducing the scope of a compromise.
If a short-lived token gets leaked the damage is limited to the TTL of the short-lived token.
If you were to pass around the long-lived token you would need to do forensics on the entire life of the token to figure out how/if the credential was used.
Just think very pragmatically about the probability to keep a short-lived token secret across all the places it's being transmitted vs. keeping the single API that exchanges the refresh token for a short-lived token super secure.
That drove me up the wall in Python so much - ALL the documentation just described how to put a massive library into a cookie cutter example and never explained how it's supposed to work so I could debug the darn thing.
Not sure if they still do this, but when I first started learning, front and center of the documentation was telling you how to do it in a mobile app, then JS SPA app. The use case of a backend API+JS/mobile frontend was buried. And it was all stupidly named. It wasn't blatant like 1) MOBILE APP FRONTEND 2) JS APP FRONTEND 3) API BACKEND + FRONTEND.
So for me, mobile wasn't relevant, SPA wasn't relevant, and my use case, the third one was hard to find.
if the library is implementing oauth 2.0, that explains what the library is trying to do.
how it's implemented... well that's an implementation detail, that most often one couldn't understand without the domain knowledge (unless it's something "trivial" like an off-by-one in some string comparison or something like that).
Yes, that "domain knowledge" is kinda important when you're trying to debug a blob of code that results in random permission errors after OAuth request.
The "implementation details" are what we're tasked with implementing ;)
Yeah -- I think you just described OpenID (not OAuth).
And to be honest, this is part of the problem. We use confusing (and sometimes conflicting) terminology to describe both authentication (identifying somebody) and authorization (making sure you have the right permissions to do something).
I recently gave a talk on this and agree. While it was fun to learn, the difference between oauth and oidc isn't clear. Especially with what I've been referring to as oidc "wave 2" - machine to machine authentication without OAuth seemingly involved at all.
My personal frustration with the docs is that very similar words are used in sequence and for both generic and specific meanings.
“The possessor of the bearer token is authorised to re-request a refresh token code cookie.” is only a very slight exaggeration.
I also want to slap the Microsoft employees that use six different similar terms for three GUIDs that look identically random. E.g.: “Client ID (Principal ID)”.
OAuth document writers love abstract, lofty nomenclature for everything.
I like what you’ve done here with your explanation - I can practically envision a couple (or three!) literal sock puppets giving me this perfectly adequate overview.
Except HTTP prohibits copying headers from redirect responses to redirected requests. That's why OIDC exists, to deal with that by abusing URI q-params.
I've an Internet-Draft somewhere to fix this by making it possible to indicate on a 3xx that this is for authentication (so copy Authorization: from redirect responses to redirected requests) and to allow 401s to have Location: headers that make it more like a 3xx, and 3xxs have WWW-Authenticate:, so you can negotiate between legacy HTTP auth and redirect-based authentication.
I think the reason OAuth is so successful and prevalent is because it is so flexible. This let folks implement what they needed and not what they didn't.
Overspecifying things didn't help SAML, after all. There are still holes and unimplemented/incompatible options. Authentication and authorization, especially across systems and organizations, is hard.
Is there a simpler standard for third party delegated authorization?
I’m happy when someone explains something with words strung together into sentences, rather than code. Code is an example, an example is not an explanation.
Then your examples can be simple, rather than trying to be a complete explanation of everything.
I have seen a lot of people get caught up on things like the client_id, client_secret, redirect URL, and scopes. This is relevant when you have a lot of tenancies for your customers and want them to log in via some OAuth provider. Do you create a new client_id for every tenant? Should you distribute a client_secret? Did you properly configure the redirect? Proving identity is the easy part though. Are scopes too broad or too narrow to accomplish the app's goals? Are there unintended consequences? Those kinds of things can slow down development.
I completely agree. What I want 99% of the time is basically a Javadoc showing the methods, the inputs, and the outputs, and all of the types. And if there's a short summary and an example as supplemental material, that's great.
Unfortunately, developers seem to think their contrived examples are a stand-in for actually documenting the methods. I deal with a lot of Javascript libraries like this and it drives me insane.
I recently did an OAuth integration with an API that documented its OAuth endpoints like all its other endpoints: including a mention of OAuth in the description but giving explanations and examples with curl, just like it would if there were no standard.
It was way easier than anything else I've done with OAuth (except log in as a user).
This. Unfortunately, the problem is, the people making these tutorials don't know that OAuth is that simple. They just know how to use the library that the 3 lines of code are hidden within.
Lets say you want to use google as an auth provider. You do this:
"Hey google who is this guy? I'm going to send them to google.com/oauth, send them back to example.com/oauth, and in the headers of the request include the word "Authorization: bearer" followed by a bunch of text"
Google says "Oh yeah I know that guy, here I'll send them back to where you said with a token"
Then later on you can take the token and say "Hey google, somebody gave me this token, who is it?"
That's pretty much it. You have to trust that google isn't lying to you, but that's kindof the point of oauth.
But that's never what the documentation says. It's always 10 pages long and the examples are like "here's a fully functioning python web server using flask and function decorators, oh the actual auth flow, which is really like 3 lines of code, is hidden inside of a library".
To people who write documentation: PLEASE for the love of god show me how to talk to your API both using your library, but also using something like urllib2 or requests or something.
Ideally the documentation is the absolute most minimal way of making the service work, and then adds more and more usefulness on top of that. I'm not going to judge you for writing bad code in an example. The example could practically be pseudocode for all I care. I just want to see generally how your API is supposed to work.
edit: yes, auth0, I am looking at you.