When you ask a consultant if you should use the Resource Owner Password Credentials (ROPC) grant type, the standard response is: “It depends”. Whilst this is true, I’m going to take a stand and say no. Unfortunately, a lot of people see the username & password fields and say “ah! That’s the one for me!”, and I spend way too much of my time trying to convince them it’s a bad idea after they’ve already spent a lot of time implementing it.
So, let’s take a look at the ROPC grant type, why it’s so tempting, and what we can do to convince other developers and stake holders that it is a bad idea.
There’s been a few articles already about how the ROPC grant type is a bad idea for modern applications, my favourite of which is Lutando Ngqakaza’s “The resource owner password grant is an anti-pattern in the face of OAuth & OpenID Connect”. I recommend you check this out if you want to see a different approach to the same argument.
Why the Resource Owner Password Credentials Grant Type Exists
Let’s see what the spec says:
The resource owner password credentials grant type is suitable in cases where the resource owner has a trust relationship with the client, such as the device operating system or a highly privileged application. The authorization server should take special care when enabling this grant type and only allow it when other flows are not viable.
Okay, that’s fairly clear cut. For one, it’s where you have an existing trust relationship with the client application. I would interpret this as you own the system/device or at the very least the private client application running on the device. Secondly, it’s there for client applications that just cannot support other authorization flows.
This is a fair use case, and an excellent example of such an application was discussed by Lutando and John Korsnes: an Apple TV. Here the application doesn’t have the ability to open up a browser to navigate to your authorization server to request tokens and securely authenticate.
It is also used to migrate existing clients using direct authentication schemes such as HTTP Basic or Digest authentication to OAuth by converting the stored credentials to an access token.
This is another really useful way to use the ROPC grant type, to drag our legacy applications into this millennium and away from authorization methods released in the original HTTP specification. Here we get the immediate benefits of access tokens with expirations, scoped access, and everything else we know and love about OAuth. Check out the Credential Sharing section of my The Wrong Ways to Protect an API article for why you don’t want to use authentication/authorization methods like HTTP Basic.
Why you shouldn’t use the Resource Owner Password Credentials Grant Type
Well, for one, it is impersonation. When you integrate with an OAuth Provider or OpenID Connect Provider, you’re after delegation or authentication respectively. When using the ROPC grant type there is no way to know if the resource owner (the user) is really making that request. The Resource Owner Password Credentials grant type is not authentication.
It is also teaching your users bad habits, as the collection of ROPC are very similar to a phishing attack. You are being asked by some random application for your credentials to another. Imagine if an application asked for your Google or Facebook credentials, instead of redirecting you to the Google or Facebook login screen.
If you own the authorization server as well as the client application then this is a little more forgivable, however if you are telling external parties to use this method then you are exposing your user’s credentials to those applications. This is also addressed in the specification, with a warning that passwords could be leaked both maliciously and unintentionally:
This grant type carries a higher risk than other grant types because it maintains the password anti-pattern this protocol seeks to avoid. The client could abuse the password, or the password could unintentionally be disclosed to an attacker (e.g., via log files or other records kept by the client).
We’re also trusting the client application to request the correct scopes and to notify the user what it is accessing. The user has no control over the authorization process. Once the user enters their credentials, that’s their part in the process finished with. Armed with these credentials the client application can then request any scopes it wants to from the authorization server. Whilst we can mitigate this by controlling what scopes the client application is authorized to request from within the authorization server, the client application could still be accessing the user’s resources without their knowledge or consent.
If you use ROPC over other flows, then there is no Single Sign On. The resource owner is never actually authenticating with the authorization server, you’re just using the token endpoint. If you’re using OpenID Connect, there’s also no way of using this flow to get an identity token.
Much like credential sharing & HTTP Basic Authenticaton, we also restrict authentication methods, in this case to just username and password. Multifactor authentication isn’t an option without either going off spec or implementing some custom approach local to each client application.
Federated identity is also unavailable to this grant type, as we need the user’s username and password to request tokens. Sure, you could use the external identity providers directly, but then what’s the point of your central authorization server? This is doubly true when talking about OpenID Connect, where you are trying to extract authentication logic from all of your client applications.
There’s a lot of similarities between the ROPC grant type and credential sharing or HTTP Basic authentication, two methods widely considered insecure. Surely that must tell you something?
Once again, I’ll quote the spec:
The authorization server and client SHOULD minimize use of this grant type and utilize other grant types whenever possible.
Questions to Ask Yourself before using the Resource Owner Password Credentials Grant Type
ROPC is a purposely flawed grant type, meant only for when there is no other alternative. So, before you use it, ask yourself:
- Can my client application access a browser? The answer is most probably yes, in which case don’t use ROPC, use a different grant type. Even WinForms can access a browser.
- Does the “resource owner [have] a trust relationship with the client, such as the device operating system or a highly privileged application”? A public SPA running on a user’s device (browser or native) is not a “highly privileged application”. Use a different grant type.
- Do I give a damn about security? If the answer is yes, don’t use the ROPC grant type. If the answer is no, then what are you doing here? Go do something fun.
These Excuses are not Valid
- “I don’t have time right now…” Welcome to the world of technical debt. How are you going to convince stake holders to spend time updating your authentication processes again, when there are new features and products to implement. Their answer will be: “Yeah, but what we have is good enough right?”, or the dreaded: “Well why didn’t you do that in the first place?”.
- “But I only have one application” And that means you should half-ass its implementation?
- “But UX!” No. It is not okay to use this grant type because you want to keep the login screen in your application so that you can style it. There are valid “security reasons” against using this grant type, don’t let anyone convince you to do otherwise for the sake of UX. Typical UX used to be that you would log out of an app and then bookmark the login page. Do you do this on Microsoft or Google applications? No. You may not earn as much money as these companies, but why should you behave any differently.
If I Still can’t Convince you
If you’re going to still go through with it, please at least make sure you have addressed the following:
Securing the Token Endpoint for Resource Owner Password Credentials
If you’ve used ROPC in a public client, you can safely assume that your client credentials are now being used by other people/apps, and someone is trying to brute force your Token endpoint. Therefore, the Token endpoint must be hardened against attacks aimed at user credentials. So not only the brute force guessing of username & password combinations, but also account enumeration techniques, including timing attacks or leaks from inappropriate response messages. Think about implementing protection against these along with some sort of throttling or lockout policy. Basically, you need to protect it the same way you would a HTTP Basic Authentication endpoint.
Ideally, the Token endpoint shouldn’t really need to be bothered with this stuff, so you are unlikely to find these features implemented by default in any OAuth or OpenID Connect provider libraries.
Using Resource Owner Password Credentials with a SPA
The ROPC grant type allows for refresh tokens, so make sure these are disabled for the client application at the authorization server level (don’t allow it to request the offline_access scope). There is no way for a SPA to refresh access tokens when using the ROPC grant type. To get a new token, you would have to resupply credentials each time. There is no secure way around this.
Access token lifetime and scoping must also be taken into account, just like when using other grant types or flows that are designed for end user devices such as the browser.
The only time I would concede you the ROPC grant type is when you are using a browserless device such as an Apple TV, or you’re asking me to migrate one of your legacy apps from the early 2000’s. But then again, I’ve integrated a VB.NET WebForms application with IdentityServer before, so I probably wouldn’t even give you that one…
If you have a client-side, browser based application, start with the Implicit flow.
If you have a native application, start with the Authorization Code flow.
If you use this grant type despite the above, I will be nasty to you and say mean things about you both behind your back and to your face.
For alternative views and takes on this argument: