The complete guide to protecting your APIs with OAuth2
stackoverflow.blog
stackoverflow.blog
For a spec it is quite readable and pretty short. One reason for this is that it "[..] is an in-progress effort to consolidate and simplify the most commonly used features of OAuth 2.0." [2]
It does away with most of the complicated grants/flows of previous version and basically boils down to just two[3]:
- Authorization Code Grant
- Client Credentials Grant
Authorization Code Grant (which now implies PKCE) is what you most probably want in most cases. So instead of complicated decision trees[4] for most people it will be:
"Learn one grant and be done".
[1] https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-...
[3] There is also the Refresh Token Grant, but it cannot be used alone. There is also an extension mechanism to define new grants and we will have to see if it is used.
Yes, I think that reading the OAuth 2.1 spec is a great place to start. They've taken 10ish years of real world learning and condensed it down. I first wrote about it 2 years ago[0] but have been following along on the email list and they seem to be getting closer and closer. I don't think it is at 'last comment' yet, but I know they've done a lot of work.
I am also excited about GNAP, which is a 'from scratch' attempt to solve much the same problem. They've made some great progress[1].
I briefly cover both of those in the article, in the "OAuth's future" section
0: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
1: https://justinsecurity.medium.com/the-gnapathon-57ee110508ac
It seems I have missed quite a bit of development here and will have to catch up, so thank you for the links!
If Stripe can get away with just public and secret key pair, there is a big chance it’s enough for you as well.
So I implement separate IAM flows for my website, app and third party api access?
Or... Make use of OAuth to standardize it, using code auth+pkce for web/app, and client credentials (not user/pass, this is client id + secret, scoped to permissions you want) for third party APIs.
The scoping is so useful, api keys tend to ignore that, so platforms like digital ocean only give you an all or nothing key, meaning I can't plumb an acme client in to do DNS challenges, without also letting it spool up a VM...
Even pure server-to-server applications with "no user involved" are starting to use OAuth authentication which I don't quite understand.
https://www.verygoodsecurity.com/docs/payment-optimization/a...
Sending your service credentials to every service is arguably less secure since they can have lesser security or log unwanted things.
It essentially takes a resource behind OAuth, strips OAuth from it and exposes it with Basic Access Authentication. I'm using it to let clients that don't support OAuth access services that force it.
1. Just because a large company does something does not make that thing safe or correct—especially when your risk appetite and attack surfaces are not the same.
2. OAuth has it’s place, but it alone isn’t enough to secure an API. With or without OAuth most applications need well defined identity management, authorization mechanisms (distinct from authentication mechanisms), and all the other pesky implementation details one needs to consider when there is a desire to guarantee the authenticity, integrity, and/or confidentiality of requests to and from said application.
@brap, sorry it wasn't helpful. It's aimed at folks who want to protect APIs, but maybe I missed the mark.
This article (which I co-authored) may be more helpful: https://fusionauth.io/learn/expert-advice/oauth/modern-guide...
It was also discussed here on HN, if you want some different perspectives: https://news.ycombinator.com/item?id=29752918
While I’ll dive further into how you actually use OAuth to protect an API in your system below, including code examples, [...]
I failed to find much concrete around how to use OAuth to protect an API, and no code samples. Is this a part one in a series? If so, it should be more clear about that perhaps.
edit: For example, I just had to secure my non-Azure API using access tokens issued by Azure AD. Lot of details[1] about how to verify that they're valid, actually issued by Azure AD (one does not simply download a certificate), and it's issued for the right thing.
That said, I found the why and standards section very nice, and the grant selection sections were helpful.
[1]: https://docs.microsoft.com/en-us/azure/active-directory/deve...
I think this may have been an oversight during the final editing process. I ended up having to split this up into two articles, as you surmised.
The second one is a blow by blow of the authorization code grant (and how an API should validate a token) and will be published later this week.
But I should have caught that we promised the code in the intro and removed that.
- Authorization server, roll your own, run your own (like Ory Hydra), or use third-party (like Azure AD)?
- Tokes, if you can choose, should you go with opaque tokens, JWT or something else? What about token lifetimes?
- When you get a token from a client, how do you validate that the token is valid?
There's probably more, but I feel at least these points should be included in a "complete guide".
Change the title and it's a fine OAuth blog post. edit: for example, add ", part 1 - introduction".
> Authorization server, roll your own, run your own (like Ory Hydra), or use third-party (like Azure AD)?
If by 'roll your own' you mean 'use an open source library like Python's OAuthLib', all three of these are valid options. Really hard to give general guidance because it depends on the complexity of your application environment (1 app in rails? 10 apps with mix of custom and COTS?) and operational maturity (do you want to run your own server for compliance or control reasons? do you have the skills to do so?).
> Tokens, if you can choose, should you go with opaque tokens, JWT or something else? What about token lifetimes?
"if you can choose" is an important clause here. Many times it'll be dictated by the Authorization server you choose. All other things being equal I'd lean toward JWTs because of their widespread support, but I know others have other opinions.
Re: token lifetimes, this is something you need to threat model out (what happens if a token is stolen?). Generally recommend seconds to minutes for access tokens and longer lived refresh tokens. Also whether you should use token binding or can live with bearer tokens.
> When you get a token from a client, how do you validate that the token is valid?
That will be discussed in part 2 of this series, but in general you should validate the signature (if it is a signed token, often a JWT) or use introspection to ask the Authorization server if it is valid. Then you should validate the claims.
There is definitely more, but thanks for the feedback!
For part two I would definitely want at least some discussion around choice of authorization server. I realize a full deep dive is too much, but at least list the options (use a lib, use your own server, use third-party) with some pros and cons or something like that.
If you do go into token generation and usage, and mention JWT, do mention the requirement to verify the alg field against expected value. Just given the issues it's caused in the past.
Anyway, hope I wasn't being too negative, that wasn't my intention.
No worries, I appreciated the feedback. That's one of the magical parts of HN comments.
Unfortunately part 2 is already edited and ready to go and so won't cover the choice of authorization server; it kinda assumes you are using one.
Here's an HN comment I made that covers the dimensions of what to think about: https://news.ycombinator.com/item?id=26411197
I also have collected a series of essays around what to think about w/r/t outsourcing auth (including what kind of auth server to choose): https://leanpub.com/theultimateguidetooutsourcingyourauth . Here's a coupon for 50% off that book: hn-april . Or you can email me and I'll get you a copy; my contact info is in my profile.
Thank for the feedback about JWTs. My general recommendation is to use a well vetted open source library for the signature verification, which should handle the alg check. But calling it out is a good idea.
A "complete guide" to this is still sorely lacking -maybe because it just is that complicated and can not be dumbed down?
I found these resources helpful:
* Solving Identity Management in Modern Applications" walks through the identity life cycle in detail, from initial provisioning to deprovisioning. Concepts, standards, not a lot of code. Lots of focus on the workforce use cases (rather than customer). But still great: https://link.springer.com/book/10.1007/978-1-4842-5095-2
* "OAuth2 in Action", in contrast, builds an OAuth and OIDC server in JS, from scratch, so has lots and lots of code. Great section on tokens, and covers stuff beyond the standard OAuth grants, such as dynamic client registration. https://www.manning.com/books/oauth-2-in-action
* Podcast episode about OAuth with the author of "OAuth2 in Action" https://www.se-radio.net/2019/08/episode-376-justin-richer-o...
No worries!
> I think something like a “Todos API with OAuth2 from scratch with code snippets”
Here you are: https://fusionauth.io/learn/expert-advice/oauth/modern-guide... and here's the github repo for the code in it: https://github.com/FusionAuth/fusionauth-example-modern-guid...
That assumes you aren't implementing the Authorization Server (it uses FusionAuth, but should work with other OAuth servers like Identity Server or Keycloak). If you want that experience, then I'd recommend buying this book: "OAuth2 in Action" https://www.manning.com/books/oauth-2-in-action which takes you through all of the nitty gritty. Seriously, you'll implement the whole thing. It's pretty cool.
[1]https://engineering.intility.com/article/implementing-oauth2...
The protocol could be as simple as this (using password)
app => auth-server (can user:pass login to app?)
auth-server => app (no | yes)
Or using "crypto": app => auth-server (can user login to app ?)
auth-server => app (only if user can solve challange=XXXX with answer=YYYY)
Asking for capabilities: app => auth-server (what permissions do user have on app ?)
auth-server => app (user has access to: X, Y, Z)Second scenario: could work if we're talking about a trusted third party app (e.g. Authy) generating the response. Otherwise presumably you enter your secret into the app, where the OTP is derived. This has the same problem as above.
Third scenario: no authentication required to query for capabilities? Puts on black hat Ok let me quickly check [A-Z]{1,18}@whitehouse.gov and see which accounts I'm most interested in.
Interestingly, your first scenario is the resource owner password credentials grant: https://datatracker.ietf.org/doc/html/rfc6749#section-4.3
The second scenario is, I believe, the JWT bearer grant: https://www.rfc-editor.org/rfc/rfc7523
Of course in the OAuth grants, the auth-server returns a token to be presented to other resources instead of a "yes" or "no" answer.
This is the crux of the article.
We're integrating with a vendor now that has imo chosen incorrectly to use auth code instead of client credentials.
This leads to several problems. One is that we need to store their refresh tokens, which go stale after 2 weeks. Since it's theoretically possible that the customer doesn't use the integration for 2 weeks, we need to preemptively use the refresh tokens ourselves behind the scenes every 2 weeks to prevent this. When the vendor changes tech behind the scenes, they just chuck away all existing tokens, so we then need to contact all customers asking them to log in again in order to get new refresh tokens. It sucks and it's brittle as hell.
Tldr; don't use auth code grants (the ones most people think oauth is) for server to server integrations.
We hacked together a laughable solution that uses an Event Bridge schedule task to invoke a Lambda that goes out and refreshes the tokens daily, lol.
They all model sharing trust over to untrusted boundaries using signed messages generated by one signing authority. There's always a client, different clients have different properties, they're trying to access a resource, and there is a privileged signatory.
Lots can be learned from their specs, but please don't limit yourself to them, and don't even worry about following the spec accurately (no one really does). Just know WHY the spec is designed the way they are so that you avoid the same security pitfalls. And understand WHAT you're doing: granting signed assertions based on some user interactions
Not only that, but just because it got through the IETF gauntlet doesn't mean they don't have problems. These specs slide around like crazy. My OAuth is different from your OAuth. The antidote? Just write Good Docs and provide good libraries for your integration path. A smart kid in a vacuum solving the same problem OAuth solves will come up with OAuth. Try to emulate that smart kid. And understand why certain concessions are made on the protocols you're taking inspiration from because you might actually not need to make those concessions
This stuff hasn't been fully figured out, so push the needle forward and don't be limited by the past. All protocols are incorrect given a large enough time span.
I have a different perspective. I work for an auth company now, but before I did, I didn't want to think about this at all.
I wanted to get some code shipped and build differentiating features.
I agree that the IETF and other standards bodies are not perfect (there are specs that get published that never get implemented widely and standards that are implemented that are retrofitted specs).
I do think that building on top of a spec is a great way to "stand on the shoulders of giants", get access to people's experience in use cases you haven't seen yet, and accelerate your development effort.
Also, there are a lot of folks who've written client libraries, docs, and tutorials around OAuth that people can leverage. How many devs will have experience with your "antidote"? I dunno, but my guess is the number rounds to zero.
It's embarrassing to say but auth has always confused me. And of course managers always instantly say "Just use OAuth" -_-
I'd love to hear what others have to say, but I'd suggest encrypted session cookies. This is what some frameworks support out of the box (rails has docs here: https://guides.rubyonrails.org/security.html#sessions ). Would store the session in an HttpOnly secure cookie.
If you need to scale your server side horizontally, you'd use redis, a database or something similar for your session store.
This feels like a problem usually solved at the framework level (and has been for years) so maybe that's why there are not as many "best practices" docs around?
This OWASP document may be a useful read too: https://cheatsheetseries.owasp.org/cheatsheets/Session_Manag...
Similarly, while it is good to understand the various OAuth2 RFCs and why certain decisions were made, you should also just follow them instead of rolling your own auth exchange mechanism.
Looking forward to part 2!
* Time limited (rotation built in).
* If using signed tokens, integrity of contents can be verified.
* Lots of libraries and knowledge around how to use it.
* Tokens allow for more interesting/useful payloads than an API key.
I looked at it a year ago and wrote up some of the benefits: https://fusionauth.io/blog/2021/01/07/gnap-next-gen-oauth
There's also some interesting stuff around token binding happening. DPoP is moving towards becoming a standard: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop-...