OAuth 2.0 Security Best Current Practice
tools.ietf.org
tools.ietf.org
I do not know why so many engineers end up reading a clear specification document like RFC-6749 [1] and then ignore 80% of the instructions. I had to deal with so many weird bugs and bad OAuth server implementations, I lost count of how many emails went back and forth trying to make sense of unexpected behavior.
Even Apple engineers got it wrong. They decided to create their own thing based on OpenID Connect for “Sign In With Apple” [2]. OpenID had to write an open letter [3] explaining the repercussions of their changes and fortunately were able to convince Apple to fix the implementation [4].
OpenID was lucky though, they had some leverage, but a no-one like me couldn’t possibly convince a gigantic conglomerate like Tencent to fix their web API. Talking with Tencent engineers has been one of the worst experiences in my career. The company apparently has a culture of constant job rotations, engineers are assigned projects for short periods of time with bonuses for early completion which encourage them to deploy half-finished code before moving to something else.
[1] https://tools.ietf.org/html/rfc6749
[2] https://developer.apple.com/sign-in-with-apple/
[3] https://openid.net/2019/06/27/open-letter-from-the-openid-fo...
[4] https://openid.net/2019/09/30/apple-successfully-implements-...
I thought the point of OAuth was to make it easy to build clients and push the complexity to the authorization servers :) ?
Indeed, there are some people when it comes to best practices, that I respect more than industry standards as they are usual best practices that will be standard tomorrow.
can you tell me if there is a good drop in component that I can add to my product and integrate easily to get oauth2/open id connect? I heard good things about identity server but would appreciate if you could share your opinion on it.
I've been tasked with implementing oauth/openid at work and in about one month in spare time i was able to read rfc 6749, install keycloak, configure it, create a client, create a very basic app (~80 lines of python and flask) and log in via oauth/openid with user information and groups pulled via LDAP.
Keycloak is really a game changer.
This presentation is very interesting: https://www.youtube.com/watch?v=FyVHNJNriUQ
It doesn't include any domain-specific operations like your examples.
[1] https://openid.net/specs/openid-connect-core-1_0.html#Standa...
Want to add Microsoft logins with an oidc library? One url.
Want to add Twitter / Facebook signin? Go get an OAuth library and write several hundred LOCs to detail the scopes and crap you want form them because they are non-standard.
If you want a drop in OAuth/OIDC server that runs anywhere, I'd recommend looking at FusionAuth. You can set it up in 5 minutes and it runs everywhere: https://fusionauth.io/docs/v1/tech/5-minute-setup-guide
Worth noting: it is free as in beer--if you need an open source solution, it's not a fit.
Edit: typo
I wish I could upvote this 10 times. There are open source solutions. There are closed source solutions. There are SaaS solutions.
Pick a solution that meets your needs. Build the differentiating features of your app, not an OAuth server.
I'm employed by a company which provides an OAuth implementation which I think is worth evaluating; you can look at other comments on this post or in my profile for more details. Happy to chat more over email, which is also in my profile, if that'd be helpful.
Since I don't know your use cases or needs, I would like to take a step back and think about what a developer might think about when evaluating this type of decision. Here are some things I'd consider:
* what does your organization currently use? If your org has experience with an identity solution, I'd evaluate that very seriously. It might not fit all your needs, especially if it is focused on IAM (identity and access management) as opposed to CIAM (customer identity and access management) but since your org uses it and presumably knows how to operate it, start there.
* cost of a solution. What does it cost to run the solution every month? What costs to set up the solution? Don't forget to estimate the time of staff to operate--even if I handed you a great solution for free, it would take an engineer's time to get it up and running, and that costs money.
* features and functionality. These could be what you might think of as features (does it support passwordless, what languages have client libraries, what standards like SAML, WS-Fed, OIDC, LDAP are supported) as well as non functional requirements like tenancy, performance and data center locality (sometimes governments have requirements about where user data can live).
I've built a number of software applications and I can tell you that I wish I'd started out with a separate identity provider more often. It makes it easier to create a single sign on experience, add more applications, and enable new functionality. Add that to the fact that identity is a necessary but not sufficient requirement that doesn't often differentiate or add much value to your application. If you're building a todo app, people expect to be able to login but will rarely rave about how smooth the login experience is :) . If you find a solution that works, you can often drop it in and accelerate delivery of the real value of your application, while giving you flexibility for the future.
Finally, here's a video from RailsConf about the dangers of rolling your own user identity management: https://railsconf.com/2020/video/seyed-m-nasehi-why-you-shou... . I don't know the speaker, but certainly some of the issues his hedgehogs (no, really) encountered seemed familiar.
I have been writing a hobby OpenID server as a way to learn Go and to understand auth. Reading the OpenID connect spec is SO much more educational & clearer than the OAuth RFC on its own.
To be an OIDC Relying Party [1]?
To be an OIDC Provider [1]?
To be an OAuth2 client [2]? Keep in mind, being an OAuth2 client is probably not useful by itself -- you'll have to program your app to deal with the resource server's specific APIs to accomplish anything, and request the appropriate scopes for the situation, based on what you're doing.
[1] https://openid.net/specs/openid-connect-core-1_0.html#Termin... [2] https://tools.ietf.org/html/rfc6749#section-1.1
So I guess the upshot is I find resources like this and the upcoming 2.1 to be valuable.
Since OAuth is pretty coarse-grained, you tend to have: - A client has a policy configured for the file sharing service or file collection, and does not use e.g. the scope parameter to request particular permissions - A file collection lets scopes be assigned particular permissions, and a client requests access by requesting one or more scopes
Although I am not a fan of the level of complexity it adds, UMA (User Managed Access) makes a pretty strong attempt at solving these sorts of problems as well.
scope="/dir1:read /dir2:write /dir3/file.txt:read"
Then when the authorization screen is presented, the user could even modify the permissions granted on the fly.
Usually applications only need a single directory to store data, and it shouldn't matter to the app where that directory is in relation to the rest of the user's data. In that case you could do something like this:
scope="dir?:write"
Which tells the authorization server to present the user with a directory picker, so the user has control over where the data is stored. It doesn't make any sense to give write permissions to all your data for every single application.
After 5 minutes of poking around, I still don't understand how exactly UMA works in relation to Oauth2. I agree it seems to be pretty complex.
What's also very annoying is the need to register an "application" at the provider. So i.e. for Facebook you have to login into their developer portal, create an app, fill out all the stuff they want to know, and then rely on them to approve your app. You have to do this for each and every provider, and for each application you want to support, and of course they all have different developer portals and want to know varying amounts of information... I've given up on Instagram OAuth because it seemed to much hassle to get your application approved.
Don't know why this is necessary, there should be a simple straightforward spec solely for Auth, that doesn't require all these steps.
[0]: https://aaronparecki.com/2018/07/07/7/oauth-for-the-open-web
Also, service providers who are custodians of the resource owners' data want you to be accountable to them and their users, so they want to have some information about you.
There's a proposed standard for dynamic (and/or programmatic) client registration [2]. While there are some interesting use-cases, it offers little benefit to a service provider who wants to vet clients (or appear as if it did).
The spec contains some design recipes if you want a loose association between which clients are allowed to receive data the user approved (i.e. implicit grant), if you care little about whether the user trusts your app (i.e. resource owner password credentials grant), or if you care little about user approval (i.e. client credentials grant). The catch is that your unilateral opinions are insufficient: the client and service provider need to be in agreement about whether these are okay to use.
And, as is often the case with "kitchen sink"-type specs, these other usage modes muddy the waters around the safer and saner usage modes, so much of the current advice on OAuth2 focuses on dissuading their use.
[1] https://tools.ietf.org/html/rfc6749#section-2 [2] https://tools.ietf.org/html/rfc7591
Ok so the other replies here clarify why — but really it'd have been nice if there was at least some consistent interface between all the OAuth / OIDC providers, so one wouldn't need to learn new interfaces all the time.
... And, worse, now I need to take screenshots and document all these different interfaces, for an open source project I'm building, whose users might want to register OIDC client apps too. :- /
"The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client."
This sounds reasonable on the surface, and technically it might be correct (depending on your interpretation of the words). But if someone naively follows this instruction to implement the server - issue a new refresh token and revoke the old one in the same refresh request at the server, what happens is when the client doesn't receive the new refresh token due to any transient failure, the client effectively loses its authorization, since the old refresh token is revoked and they didn't get the new one. Thus the protocol becomes "leaky" - you lose users because of transient errors.
i.e. "after issuing a new refresh token to the client" needs to be "after the new refresh token is being used at least once", or otherwise invent some other way to ensure the new refresh token is correctly received at the client before revoking the old.
These sorts of details will typically wind up in an implementation report document. It's rare that an IETF protocol specification document will attempt to pre-determine the solution to all such implementation details up front, and IMO, not good when they do.
In some contexts, it may be much better to require the application to re-obtain resource owner authorisation than to allow a replay of a refresh token. In others, it may be much better to avoid requiring resource owner authorisation. What you see as a bug, others would see as a critical feature.
I believe the intention here is not to verify whether it was used once as you state. And transient matters (how the client will detect if not received would be a different scope), for example, if client didn't receive (or lost), then likely here you may need restart auth cycle.
Of course that is if the OAuth provider allows it.
That's the catch. Last time I checked, at least Google didn't. So how can you write a (F)OSS email application for accessing GMail via OAuth2, without putting the secrets into the code (which Google also forbids)?
[0]: https://developers.google.com/identity/protocols/oauth2
[1] https://developers.google.com/identity/protocols/oauth2/nati... [2] https://web.archive.org/web/20150520223809/https://developer...
"Developer credentials (such as passwords, keys, and client IDs) are intended to be used by you and identify your API Client. You will keep your credentials confidential and make reasonable efforts to prevent and discourage other API Clients from using your credentials. Developer credentials may not be embedded in open source projects."
That's why the best option is to set up a secure middleware layer to hold your secrets. Here's an example using react: https://fusionauth.io/blog/2020/03/10/securely-implement-oau...
(Full disclosure, this is a post on the blog of the companyfor which I work.)
It's really the implicit grant (where access tokens are available to the client) that should be avoided.
From https://tools.ietf.org/html/rfc7636
"OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack. This specification describes the attack as well as a technique to mitigate against the threat through the use of Proof Key for Code Exchange (PKCE, pronounced "pixy")."
At the end of the day, no matter what the grant, if you store the access tokens (or refresh tokens) in the browser running an SPA, it is vulnerable to an XSS attack, right?
My understanding is that that is suboptimal because the browser has such a large surface area to secure.
Edit: This seems pretty new, any timeline for a release?
If the user doesn't want to use your service, they configure their own key.
OAuth with client secrets is fundamentally unsuitable for desktop and mobile applications, but that hasn't stopped virtually every major implementor from using it that way. The people who wrote the spec were too busy congratulating each other and downplaying concerns to seriously engage with this issue until it was much too late to do anything about it.
It remains a deeply broken standard, and the people who were responsible for it at an early stage are entirely to blame. Several of them have removed their names from the spec, but I'll not forget the role they played in shouting down critics who raised concerns about this very issue.
The spec has been clear from the start that there are clients that can protect their secrets, and ones that can't [1]. Here's a choice quote: "A native application is a public client installed and executed on the device used by the resource owner. Protocol data and credentials are accessible to the resource owner. It is assumed that any client authentication credentials included in the application can be extracted." Unfortunately, much of the rest of the spec is a bit like a playing puzzle, where reading it in one sitting doesn't actually build your understanding, and you have to constantly cross-reference various sections to construct a complete picture of the process the spec is trying to set.
It look a long time for people's familiarity with the spec to grow, and for providers and clients to improve in quality and adherence to the ideas and text of the spec.
To be honest, I was wishing for OAuth 2.5 if not OAuth 3.0 to consolidate already fragmented OAuth 2.0 spec and landscape [2]. At this stage, there are too many draft proposals and a majority of them led by vendors with some interest in standardizing their implementations.
For example, this RFC suggests restricting issued access token to one resource at a time (using audience parameter). Well with Microservices landscape this gets really challenging. Your client application may be interacting with multiple resources. Neither OpenID Connect nor OAuth 2.0 offer solutions to issue multiple access tokens (not yet). An API Gateway may be a solution but still so much ambiguity.
I think OpenID Community has done a better job to organize their specifications and working groups [3]. If you go on their specification page it tabulates really well what spec is final, currently under implementation, draft, or obsolete. Still, big vendors influence agenda and direction.
(Disclaimer: I am the founder of https://axioms.io/ which OAuth 2/OpenID Connect compliant identity management platform)
[1]: https://tools.ietf.org/html/rfc6819
[2]: RFC 6749, RFC 6750, RFC 6819, RFC 7662, RFC 7009, RFC 7519, RFC 8414, RFC 7591, RFC 7592, and 20 more.
[1] https://news.ycombinator.com/item?id=23083245 [2] https://tools.ietf.org/html/draft-parecki-oauth-v2-1-02 [3] https://oauth.net/2.1/
I've been wondering about this lately. This makes sense from a security perspective, but the primary alternative is setting a header like Authorization: Bearer, which makes your request subject to browser cross-origin restrictions, and all the fun that comes with CORS. In my case, the extra round-trip required for the CORS preflight in this situation is simply a non-starter for my application.
It would be nice if there was an escape hatch for developers who understand the risks. More background in this SO question I asked a couple days ago:
Request body works, but it forces you to split your API if you want to be cache friendly. What I mean is ideally you want public data to be GETable and thus cacheable. In the system I'm building, any given path can change back and forth from being public vs requiring authorization, so if I use requests bodies (ie POSTs), I would need to detect whether the data is public or not before making the request and choose between GET or POST at request time. That might not actually be that bad; I'll have to think about it more.
> This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.
> ...
> It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."
I think publishing an IETF RFC on security best practices is hard, but I hope these authors manage to pull through
The problem is, there's a bunch of decisions with auth, which have to be made, but aren't really important. What should the name of the auth token header be? What should be the format of the responses? These decisions are like deciding what side of the road to drive on: it doesn't matter which side of the road you drive on, as long as everyone drives on the same side of the road. The function of a standard is to decide what side of the road everyone drives on.
OAuth isn't a standard. It's just a description of all the various homegrown third-party auth systems that anyone has implemented over HTTP, with only the absolute worst patterns weeded out. None of the shareholders wanted to re-implement their third-party auth, so they just made sure their flavor of auth made it into the standard. It's like if the Europeans and the Americans got together to standardize the side of the road that everyone drives on, and the standard they came up with is "You have two options for which side of the road to drive on, the right or the left."
Until a group of visionaries with enough clout to make the world fall in line creates an actual standard, OAuth is going to continue to be crap. And there's strong disincentive to do that: if you agree to conform to a standard that isn't exactly what you've already implemented, then that means you have to reimplement. A few years' pain conforming everyone to a standard would significantly drive humanity forward--an incomprehensible amount of developer hours have been spent writing custom OAuth integrations, and an actual standard would allow us to write libraries around it that everyone could use. But corporations don't care about pushing humanity forward when it's contrary to their bottom line.
(ideally, just like rfc 6749...)