This was a good article. The first section, explaining the reason why OAuth2 is a fit for certain data flow needs, was really strong. I liked the diagram of the flow as that made it clear what all the pieces were.
I think that if you need that separation between your resource servers and authorization server, the OAuth dance can be a bit complicated, you can just use a simple api key. But as soon as you start to allow outside access to your systems, I'd suggest using an OAuth server (disclosure, I work for FusionAuth, a free as in beer competitor to Keycloak, Gluu, etc).
Additional things that I wish had been in the article:
* Don't use implicit flow, use the authorization code grant with PKCE.
* Don't use resource owner password flow; it was designed to allow existing systems to bridge into OAuth and shouldn't be used in new systems today.
* Also, storing access tokens should be done carefully in the browser (httponly, secure cookies is the best way). If you can't do that, then use a server side proxy to store the access tokens. Otherwise your access tokens might get stolen by code executing in the browser.
* OIDC is built on top of OAuth and has a standard set of claims. If you can get by on what OIDC defines, you can switch between identity provider implementations fairly easily.
Storing tokens in cookies would be against the spec wouldn’t it? I’m not putting rfc6749 on some sort of pedestal, but it clearly states that the tokens are in the response body and not set in cookies.
Do you have any examples of Authorization servers in the wild doing this or front end SDKs that work with that?
I’m very curious, I’m doing an SPA security research project at work and I’m very interested in these stories and learning more.
I’ve seen some folks do refresh in an httponly cookie, and Access in the js space. I’ve seen another example (auth0) put the refresh token in a web worker and access token accessible in js.
And I’ve seen things like msal.js just say F it and make them all accessible to js.
Sorry, I was wrong. According to the spec you definitely have to send the access token back in the response body[0].
However, a client can store them as cookies (or, as you mention, other places, such as a service worker[1]). This is useful if the client is a single page application (SPA), which may need to present the access token to other resource servers.
RFC 6750 has something to say about how to store the bearer token [2]:
> Don't store bearer tokens in cookies: Implementations MUST NOT store bearer tokens within cookies that can be sent in the clear (which is the default transmission mode for cookies). Implementations that do store bearer tokens in cookies MUST take precautions against cross-site request forgery.
So, I apologize. The authorization server wouldn't send the access token as a cookie. Instead, there'd be a server side proxy which would request the token and then send it down as a secure cookie. Again, best practice would be to keep the access token on the server side proxy and just send a session id down to the client, but that sometimes doesn't work. Here is a diagram illustrating that path (with the 'store' entity acting as the proxy mentioned above)[3].
Thank you for your replies and insight, it's really appreciated.
The way I see it with SPA and tokens in JS accessible space is that you're exposing your users to the possibility of token theft and user impersonation if someone is able pull off an XSS. This is not so different than using a session cookie that is not marked as 'httponly'. What's old is new again :)
I have also heard the argument that we shouldn't worry about token theft, because it's already too late if you're XSS'd, but I don't buy into that rhetoric(not saying XSS isn't incredibly bad).
Unfortunately I work in financial services, so we can't just skate by and assume that we're so uninteresting that an adversary with advanced capabilities wouldn't look to exploit our external or internal users somehow.
One another thing we're looking into is access tokens, like having the user re-authenticate or use a stronger factor(or multiple) to get the AS to grant them a very short lived, non-refreshable token to do their sensitive operation.
I'm going to check out the fusionauth blog for a bit more inspiration, if you're interested in continuing this discussion I would be interested in carrying it on.
> This is not so different than using a session cookie that is not marked as 'httponly'.
The difference is a session cookie is tied to one server, but an access token could be used with many different APIs or other services. That said, an access token may expire more quickly, so the devil is definitely in the details.
> One another thing we're looking into is access tokens, like having the user re-authenticate or use a stronger factor(or multiple) to get the AS to grant them a very short lived, non-refreshable token to do their sensitive operation.
That makes sense, for sure. You could definitely require MFA to get an access token and have it be short lived. At that point it gets to a question of UX and how much impact you want on your users, but I'm not familiar with all the requirements you have.
> I'm going to check out the fusionauth blog for a bit more inspiration, if you're interested in continuing this discussion I would be interested in carrying it on.
Please do! Happy to respond here or if you want to check out the FusionAuth forum (which I monitor), you can find it on the the website under the resources tab.
Thank you very much for the valuable feedback ... very much appreciate and pointers ... this why love to share so can get feedback and pointers for other information may have missed.
Even through Keycloak is more mature than ORY ecosystem I went with the latter as I have more experience with Go than Java. I'm only writing integration, dashboard. When those thing are ready I'm going to open source and offer a free option until I add billing, at that point you can either go on-premise or just continue with us.
Ory will probably offer a cloud service in the near future but I'm just scratching an itch for now.
If you know your way around it, Keycloak is good. But it seems quite easy to get into some paint points. I was using it recently and accidentally sent something that wasn't valid JSON. Instead of a 400 return code with some kind of message you get a 500 and the server coughs up a giant stack trace.
We too are building a SaaS product with billing over OAuth2. Here is our product page - https://formfly.intuit.com. We are not using OIDC. What are you building @taosx? and how are you using OIDC?
I'm building a suite of services around automotive. Due to legislation and types of consumers they're separate (different domain/branding), so SSO.
Just dropped my job in order to catch up with everything I wanted to learn and what better way than to build a business.
Currently I'm a bit (1 month) behind the schedule cause I complicated everything in order to learn k8s, rust, web assembly, svelte/sapper...so much fun, on the other side I burned through my finances a little too fast so I might need a job soon.
Nice, good luck! My advice would be to offer something very opinionated to limit the chance that something is rolled out incorrectly. That and preventing lock-in are two big requirements IMO.
I’m doing something somewhat similar, happy to exchange notes.
1) Most of the work around authentication is integrations (get the app to integrate with whatever authentication protocol/database). Integration is not a product, it's consulting services.
2) There are very established products for authentication servers. See Microsoft ADFS, PingIdentity and ForgeRock on premise. See Okta and auth0 on SaaS.
3) If you're going to roll some authentication as a company, you stick to Microsoft ADFS for internal employees or to Google/Facebook auth for external accounts. You need them anyway so there is absolutely no point in getting something else. (Yes, your company is gonna use microsoft windows internally and your customer will request google auth support).
4) There is absolutely no point for yet another product. What is it gonna do? It's gonna sit on top of google auth so you can integrate with it rather than with google? Pointless, might as well integrate to google/microsoft directly.
5) Where there is money is in consulting services, libraries and plugins. For examples make a plugin for apache/nginx/haproxy to use google auth, so developers can just put that in front of their service (legacy application) and it's mostly plug and play. Or easy library for python/java/whatever to integrate (developer can just configure a google id and URL and can retrieve user info). It's hard though because of customization hell, every use case wants to do things slightly differently.
That's my 2 cents working in the industry. For reference I've worked on authentication in startups for customers, in government projects for citizens and in companies for 100k+ employees.
I know this has nothing to do with the product, but I hung out with their CEO Brian Pontarelli and one of their lead devs (I think his name was Daniel) for an afternoon many years ago. This was before FusionAuth, when they were running under Inversoft. They came to do a lunch and learn + product feedback at the place I was working at then. We kept in touch for a while after.
I’ve never heard of FusionAuth but after a quick glance it seems interesting. Generally, I think these standards are worth knowing even if you do decide to use a managed offering.
Open core..wouldn't that mean the core product being open source..from what I'm seeing on github only some components are open source. By that example I would also call auth0 open core.
I work for FusionAuth. It's not open core. It's the other way around (open shell?), as you see, @taosx. The docs, client libraries, example apps, and some supporting libs are Apache licensed, but the core is not.
We do have a forever free community offering[0], but that's free as in beer, not as in speech.
I think it's a great product (that's part of why I joined the company) but don't want any confusion about that.
For those who are looking for an alternative and are OK with centralization of Auth which is somewhat different from the goals of OAuth, check out the CAS standard -- it's an alternative to SAML more so than OAuth.
It's so simple I wrote (and abandoned) a golang library that implements v1[2]. I didn't need the proxy abilities in v2 (and doubt most orgs actually do) and I use JSON in some places before it was in the standard but it was very easy to implement and thus I can say it's easy to understand. I've meant to convert the project to Rust for a long time but at this point I'll probably never get to it.
I really wouldn't advise to use CAS in 2020. It's an historical curiosity that stopped being relevant some years ago.
It's an old protocol to do single sign on within a company. It worked well and I've seen it used in large companies circa 2010, allowing to support sso in their internal web applications, for employees.
CAS is irrelevant now. The world has standardized on OpenID Connect (and SAML as second choice). Anything that's on CAS was migrated or is pending migration to OIDC.
As a developer you will have to deal with OIDC (and maybe SAML) for integrations with google auth, facebook auth, microsoft active directory. You really don't want to go for a dying ecosystem (CAS), that's a dead end for your career.
Are you implementing OAuth 2 or interested in learning more? I would highly recommend combing through the OAuth 2.1 spec [1] as it incorporates the “best practices” that were added to 2.0 through additional RFCs.
May be worth mentioning--JWTs are not part of the OAuth. They're certainly used together quite a bit, but you can absolutely use regular session tokens too.
I say this as someone who thought JWT was a core part of OAuth and it only added to the perceived complexity of the implementation.
> May be worth mentioning--JWTs are not part of the OAuth /OIDC standard.
JWTs are a part of the OIDC standard; from the standard itself[1],
> The primary extension that OpenID Connect makes to OAuth 2.0 to enable End-Users to be Authenticated is the ID Token data structure. The ID Token is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when using a Client, and potentially other requested Claims. The ID Token is represented as a JSON Web Token (JWT) [JWT].
But your comment sounds like you're conflating OAuth & OIDC. (It is true for OAuth that you're not required to use JWTs.)
Yeah, I've run into this recently while adding SCIM support to our system. I had to reread the RFCs and docs repeatedly to reassure myself that JWTs were not actually a requirement for the OAuth2 bearer token. It's complete overkill for what we need but the implementation lib I was trying to use assumes it and it was really complicating things unnecessarily.
The one thing that really bugs me about the OAuth flow is what is described as step 3. When the application who wants to access data on your behalf is redirected to a login page where the user enters credentials and grants access.
In many apps, these login redirects happen inside the app window, hiding the url. And even if the URL isn’t hidden, there’s suddenly a browser window inside my app and many unconscious “security checks” fail to load.
I’d much rather have the OAuth provider send me an email or get a notification that can be actioned within the OAuth providers app so that I know I’m not giving my credentials to something that looks like the OAuth providers sign in page.
I never understood this either. So many apps pop up a window to enter my credentials to Google or Facebook or whatever in a manner that just screams don't put your password in here you have no idea who's hosting this form.
This was a really well-written post. I really liked the learn by doing approach accompanied by flow diagrams. It's easy to get lost in the weeks with OAAuth terminology and this really kept good focus. I also looked at some of the author's other posts on things Protobufs and Tries and found them similarly enjoyable. I look forward to reading future posts and hope you post them here as well.
The article briefly mentions that the Implicit Grant is a less secure and more simplified version of the Authorization Code grant, but then it doesn't elaborate (or it's possible I missed that bit). In an introductory article such as this, I think it's important to explain why it's less secure -- otherwise the Authorization Code grant seems like an unnecessary complication.
The implicit grant returns an access token directly upon authorization being granted. By removing the additional network request, it can make your system vulnerable via manipulation of redirect URLs. if you’re implementing an OAuth 2 server, you can address this by validating the provided redirect URLs, but you should be doing that regardless.
My advice is to just always use the auth code grant with the PKCE extension. TLDR of that extension:
1) client generates a “secret key” that it sends with the authorization request.
2) server associates that key with the authorization code it returns to the authorized client
3) client must present that key again in order to exchange the authorization code for the access token.
Prevents the authorization code from being intercepted and abused.
Oh, I wasn't clear: I understand the implicit grant and I indeed worked on the implementation of an authorization server in a past job (we validated redirect URLs, indeed!). My point is that OAuth flows are confusing enough for a beginner that it's important to explain why seemingly unnecessary hoops are there. This article doesn't (as far as I can see).
Yeah, I gathered you knew what you were talking about after I had already posted my comment lol. Just left it up in case any others have that question. :D
Would you think that, for an early-stage SaaS startup (enterprise B2B focus), the optimal strategy for implementing AuthN/AuthZ would be to use a managed service (e.g., Auth0) for MVP development and after that (perhaps, during pilots phase) migrate to an open source solution (e.g., Keycloak)?
Thank you for your comment. Yes, I'm planning to allow running third-party apps on the platform (the exact delivery options and relevant architectural details are still under consideration). My understanding is that using JWTs is the current best practice and much preferred way for authentication vs. the session-based approach. The platform that I plan to build should be both highly scalable and highly secure. I think that session cookies is not the right approach for these requirements, even if I would not need to allow running third-party apps. I'm curious about what people here think about this and hope that they will chime in. (I also would need SSO, external IdP integration, clustering, MFA, maybe passwordless authentication etc., hence my preference for managed services like Auth0. The idea is to focus on my core competencies and outsource important but non-core services to relevant solid providers, based on availability and feasibility, at least, for the near-to-mid term.)
Personally I don't think it's worth worrying about scaling like that until you actually need to. There are other reasons to choose JWTs, but I don't think scalability is a good one early on.
Thank you for sharing your thoughts. I'm not worried about scaling and other aspects, but I do think about them. In my opinion, architectural decisions are the most important ones (across technological dimension) and fixing wrong or suboptimal architectural decisions is costly and/or difficult and sometimes outright not feasible.
True, but you can further break architectural decisions down into those that are easy to change and those that aren't. If something is easy to change you may as well implement the simple version first.
The main confusion probably comes from the name Oauth, which seems to suggest that it is about Single Sign On/authentication, while in reality it's about granting site A access to your data at site B.
That's what Oauth is though. Oauth is NOT a single sign-on technology, it's about granting access to data across services. OpenID is a single sign-on protocol built on top of Oauth.
> OpenID is a single sign-on protocol built on top of Oauth.
Note that there's older OpenID (without "Connect"), which, to my knowledge, is more or less dead nowadays, and OpenID Connect, which is indeed built on top of OAuth. Both are indeed SSO, though.
Splitting hairs but no, Oauth has nothing to do with authentication. An introductory article like this should address the distinction between authentication and authorization in the first section IMO.
Was my comment unclear? "Oauth is NOT single sign-on" single sign-on = authentication. "sharing data across services" = authorizing your data from service A to be used in service B.
The distinction is irrelevant if you just consider that authorization (authZ) always happens after authentication (authN) but it is important when you realize that different components/systems/protocols might be used in each.
At that point, the separation is more about capabilities/responsibilities than "does it happen?"
I think that if you need that separation between your resource servers and authorization server, the OAuth dance can be a bit complicated, you can just use a simple api key. But as soon as you start to allow outside access to your systems, I'd suggest using an OAuth server (disclosure, I work for FusionAuth, a free as in beer competitor to Keycloak, Gluu, etc).
Additional things that I wish had been in the article:
* Don't use implicit flow, use the authorization code grant with PKCE.
* Don't use resource owner password flow; it was designed to allow existing systems to bridge into OAuth and shouldn't be used in new systems today.
Both the implicit flow and the resource owner password flow are not part of OAuth 2.1--here's a post I wrote about it: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
* Also, storing access tokens should be done carefully in the browser (httponly, secure cookies is the best way). If you can't do that, then use a server side proxy to store the access tokens. Otherwise your access tokens might get stolen by code executing in the browser.
* OIDC is built on top of OAuth and has a standard set of claims. If you can get by on what OIDC defines, you can switch between identity provider implementations fairly easily.