Don't use the OAuth password grant type

Scott Brady
Scott Brady
OAuth ・ Updated March 2022 16 March 2022

OAuth’s Resource Owner Password Credentials (ROPC) grant type, aka the password grant, was included in the original OAuth 2.0 specification as a temporary measure back in 2012. It was designed as a quick way of migrating old applications from legacy authentication mechanisms, such as HTTP Basic Authentication or credential sharing, and onto an OAuth tokenized architecture.

However, even though it was created as a temporary measure in 2012, and despite the community’s best efforts, the odd architect or two will still see the grant type’s username & password fields and say, “Aha! That’s the grant type for me!”. ROPC is especially common in mobile apps, where it has the most significant security issues.

Unfortunately, once an organization has implemented ROPC, it is not easy to convince them it was a bad idea. That is until the use of ROPC blocks a critical feature, and all of a sudden, you find yourself needing to re-implement your auth stack.

In this article, you will learn why the password grant type is such a bad idea for modern systems, why it is so tempting when first learning OAuth & OpenID Connect, and how there are no remaining use cases for ROPC.

I’ve designed this article to help you convince developers, architects, and stakeholders that using ROPC is a bad idea. In the comments section below, let me know if it helped, what arguments worked, and what arguments worked against you!

As of OAuth 2.1, the ROPC grant type is now deprecated, and its use is discouraged by the OAuth security best practices.

Why you should not use the resource owner password credentials grant type

Here are the main reasons why you should never use the Resource Owner Password Credentials (ROPC) grant type, aka the password grant:

  1. ROPC is impersonation, not authentication
  2. ROPC exposes end-user credentials to applications
  3. The user cannot consent
  4. ROPC encourages phishing
  5. ROPC cannot support Multi-Factor Authentication (MFA)
  6. ROPC cannot support Single Sign-On (SSO, OpenID Connect only)
  7. ROPC cannot support federation
  8. ROPC is insecure for mobile applications and single-page apps
  9. ROPC has been deprecated by the OAuth working group

Keep reading for a full breakdown of each of these issues.

ROPC vs. authorization code

OAuth allows a user to delegate access to a protected resource (an API) to a client application. It allows the user to say to an application, “you are allowed to access this API on my behalf”. This delegated access results in the client application receiving an access token that it can use to call the API while acting on the user’s behalf. Whether or not the user is even allowed to access the API is a separate concern.

For this process to happen, OAuth has the client application redirect the user to the authorization server’s authorization endpoint, where the user will have to identify themselves and then give their consent for the application to act on their behalf. It is a user-interactive flow where the end-user (the resource owner) interacts directly with the authorization server. Here’s a basic sequence diagram using the recommended authorization code flow.

A client application asking to do something on the users behalf, the authorization server checking who the user is and that they are happy with the request, and then the authorization server responding with an authorization code that can be swapped for tokens.

ROPC, on the other hand, has the client application capture the end-users credentials itself and replay them against the authorization server. The user does not consent or identify themselves at the authorization server; instead, the client application just swaps their credentials for tokens.

A client application asking for a token to act on the users behalf, but telling the authorization server that the user is okay with this and to trust them since they know the user's credentials. The authorization server then reluctantly responds with tokens.

The ROPC flow only uses the token endpoint with a grant_type of password, swapping end-user credentials for tokens in a single HTTP request:

POST /token HTTP/1.1
  Host: auth.example.com
  Content-Type: application/x-www-form-urlencoded

  grant_type=password
  &username=scott
  &password=ilovecats
  &client_id=s7CieS3lru4
  &client_secret=a1d8f7b4896b40989549d641aa3ffbdc

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 pretty clear-cut. There’s a warning in the first paragraph, but as it says, this is only suitable when 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 client application running on the device.

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 migration use-case is the one remaining reasons for the ROPC grant type: to drag your legacy applications into this millennium and away from authorization methods released in the original HTTP specification. By moving to the ROPC grant type, you stop attaching user credentials to every request made to your API and instead get the immediate benefits of short-lived access tokens, scoped access, and everything else you 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.

In the early days of OAuth, the ROPC flow saw use with devices that did not have access to a browser; however, you can now solve that problem by using OAuth’s device flow.

Mobile applications (or other native application types) were also a common use case. However, this goes against OAuth standards and loses access to modern OAuth security features, all for the sake of a native login form and trying to treat user authentication as a single API call. You’ll see why ROPC in a mobile app is such a bad idea throughout the rest of this article.

Why the resource owner password credentials grant type is not authentication nor suitable for modern applications

Now that you’ve seen how the password grant type works, and why the original OAuth 2.0 specification introduced it, let’s see why you should no longer use it.

But before we deep dive into the issues with ROPC, let’s quote the original OAuth 2.0 specification one last time:

The authorization server and client SHOULD minimize use of this grant type and utilize other grant types whenever possible.

ROPC is impersonation, not authentication

The resource owner password credentials grant type involves the application impersonating the user.

When you use OAuth, you’re after delegation (“can I call this API on your behalf”), while OpenID Connect gives you the ability for Single Sign-On and to access identity data. Both require the user to authenticate and optionally consent at the authorization server or identity provider. While OAuth is not user authentication, it does require the user to authenticate and consent to the client application’s authorization request. The user must prove their identity in order to delegate access.

With the ROPC flow, this does not happen. Instead, the client application takes the user’s credentials for itself and swaps them for tokens.

There is no way for the authorization server to distinguish the user from the client application, and, as a result, this means that the client application is impersonating the user. The resource owner password credentials grant type is not authentication.

User credentials are only as secure as your weakest application

One big benefit of using OAuth and OpenID Connect is that user credentials are now only passed into a single application: your authorization server. However, with the password grant, every application, including 3rd parties, would need to gather the user’s credentials. ROPC significantly increases the attack surface on user credentials.

ROPC exposes user credentials to the client application

With ROPC, every client application must be as secure as your authorization server and account for every type of attack against end-user authentication systems. This is a significant amount of work and can be near impossible for certain types of applications.

For example, consider a “public” client application such as a mobile application running on the end-user’s phone or a Single Page Application (SPA) running in the user’s browser. These applications cannot keep a secret and have unique security considerations. As a result, they will never be as secure as a server-side application running within a data center. The argument of 1st party apps vs. 3rd party apps does not change this.

If you have many applications, this issue becomes more apparent, with your user’s credentials only as secure as your weakest application.

ROPC does not support user consent

When using the ROPC grant, your users do not consent to the application accessing specific APIs or identity data. Instead, the application can ask for whatever it wants; after all, it is impersonating the user.

As a result, a client application could gain access to the user’s email or storage accounts rather than the calendar access it advertised to the user. Would you be happy giving an application full access to your Google account, or would you prefer to control what it can do?

ROPC encourages phishing

ROPC also undermines efforts to train your users to be safe against phishing. With ROPC, you’re teaching the user that entering their credentials into any random application is okay. Again, imagine if an application asked for your Google or Facebook credentials instead of redirecting you to the Google or Facebook login screen.

ROPC does not support Multi-Factor Authentication (MFA)

The ROPC grant type cannot support 2FA. Much like credential sharing & HTTP Basic Authentication, ROPC also restricts authentication methods. In this case, an ROPC token request can only support something the user knows, a username and password.

You could go off-spec and expand the token request to include extra parameters, such as an OTP, but still, you are restricted to something that the user knows, a shared secret that can be repeated across applications in an API call. This reinforces my view that TOTP is more something you know rather than something you have. As a result, ROPC will never support modern challenge-response mechanisms such as FIDO2 (WebAuthn) or push-based authentication.

Rather than going off-spec, you could build custom APIs to handle MFA. However, I have never seen this done well, especially if each custom API ends up minting a new access token each time, as it attempts to elevate the session within the impersonating application.

Any custom approach will make life much harder for you when it comes to 3rd party integration, which further undermines the benefits of OAuth. Rather than simply saying “we support OAuth”, you now have to send across reams of documentation that describe your custom user authentication flow and support your customers when integration inevitably goes wrong.

OAuth’s security best practices also warn of the mess you’ll find yourself in when trying to hack the ROPC grant to support MFA. This advice comes from painfully earnt experience; it never works out.

[...] adapting the resource owner password credentials grant to two-factor authentication, authentication with cryptographic credentials (cf. WebCrypto [webcrypto], WebAuthn [webauthn]), and authentication processes that require multiple steps can be hard or impossible.

ROPC cannot support Single Sign-On (SSO)

If you are using OpenID Connect, then the use of ROPC will disable any chance of SSO. Because the client application is impersonating the user and swapping their credentials for tokens, you are not creating an SSO session at the identity provider. If another application makes an authorization request to the identity provider, it will display a login page since it does not know who the user is.

This lack of SSO leads to fun conversations with stakeholders when it’s time for federation. For example, they hear that they use OpenID Connect, and they can see that the user is logged into the mobile app. So why can you not simply federate with this 3rd party? After all, isn’t the user already logged into the mobile app? Unfortunately, this leads to some hard conversations and dodgy workarounds that require going off-spec for all parties, which is not always possible.

It is also worth considering that many OpenID Connect implementations will not return an identity token when using the password grant type.

ROPC cannot support federation

Since you need the user’s username and password to make an ROPC token request, federation is impossible unless the other authorization server also supports OAuth’s ROPC grant type.

You could have your client applications federate with the external identity providers directly, but then who’s tokens protect your APIs, and what’s the point of your central authorization server? This is doubly true when talking about OpenID Connect, where you can extract authentication logic from all of your client applications.

ROPC is insecure for mobile applications and Single-Page Applications (SPAs)

ROPC is at its worst when used in a public client application, such as a mobile app, where the client application is running on someone else’s device and cannot keep a secret.

Since the mobile app cannot keep a secret, it cannot authenticate itself. Therefore, when using ROPC, you must allow unauthenticated access to your token endpoint. Now you have a token endpoint that is a prime target for attackers, where they can make a single API call and start guessing user credentials.

To defend against this, at the very least, you would need to apply rate-limiting to your token endpoint, and ideally, you would want to build some sort of device binding or anti-forgery mechanism. However, since the request will always be coming from code running on someone else’s machine, in my opinion, you are only adding obstacles for attackers rather than actually securing the endpoint. You are also going way off spec again, making it harder for both 1st and 3rd party developers to use your system.

When using ROPC this way, an attacker can trivially mimic your app. There are very few hurdles for them to integrate with your “authentication” system, allowing them to create a fake app that can provide the user with real functionality while harvesting their credentials and data.

Public clients such as mobile applications, desktop applications, and JavaScript running within the browser have unique security profiles addressed by additional standards on top of OAuth, such as PKCE and various best practices documents. You cannot use new OAuth standards or follow best practices when using the ROPC grant.

ROPC has been deprecated by the OAuth working group

ROPC is now officially obsolete, according to the OAuth working group. The OAuth security best practices say you MUST NOT use the resource owner password credentials grant, and the grant is purposely omitted from OAuth 2.1.

Here’s the current wording from the security best practices document (draft 19):

The resource owner password credentials grant MUST NOT be used. This grant type insecurely exposes the credentials of the resource owner to the client. Even if the client is benign, this results in an increased attack surface (credentials can leak in more places than just the AS) and users are trained to enter their credentials in places other than the AS.

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:

  • Is my application considered a legacy application? Do you consider your SPA built on an experimental JavaScript framework a legacy application? If the answer is no, then do not use the ROPC grant type; use OAuth properly. The ROPC grant type was designed for applications considered legacy in 2012. The JavaScript framework you’re using hasn’t even been released yet.
  • 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. If you don’t have access to a browser or your input device is a TV remote, check out the OAuth device flow.
  • 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 or mobile app running on a user’s device 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. Unfortunately, you will need to convince stakeholders 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?” and “What do you mean we can’t federate, I thought you said we use OpenID Connect?”
  • “But I only have one application.” Having a single, 1st party application does not mean that you should use ROPC. Again, you will have that fun conversation with stakeholders when it’s time to scale and build the CEO’s dream project, only to find you cannot implement SSO with what you have.
  • “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, do not 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. Start noting what mobile apps use browser-based flows for initial login. You will be surprised.

Using ROPC securely

If you still have to use the ROPC grant or are in the tricky situation of already using it, I recommend the following advice.

Secure the token endpoint for use with resource owner password credentials

If you are using the ROPC grant in a public app (e.g. a mobile app), then operate as if another application is impersonating you – that someone else is using your unsecured token endpoint and trying to brute force user credentials.

Secure the token endpoint with network-level rate-limiting and blocking. Secure your token endpoint’s error responses from account enumeration risks (e.g. “invalid password for user”). Consider implementing some method of anti-forgery or device binding so that only your applications can make an unauthenticated call to the token endpoint. Again, this is not foolproof, but it can stop less determined attackers.

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. Instead, you will need to add it yourself.

Using resource owner password credentials with a SPA

A browser-based application, such as a JavaScript SPA, where code is running within the browser, is again a public client and, in my opinion, is the worst place for the ROPC grant. Not only can it not keep a secret, but unlike a mobile app, it does not have any secure storage and is vulnerable to cross-site scripting (XSS).

The ROPC grant type does allow for refresh tokens, but because of the security risks of storing tokens in the browser, I would have the authorization server ensure a SPA cannot ask for them (e.g. prevent the use of the offline_access scope). But this means that 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.

If you are building a SPA, do not use the ROPC grant. Instead, choose a better approach for authentication and API access, such as using the BFF pattern or the authorization code flow. The BFF pattern may save you in this instance.

Mobile federation

If you find yourself with a mobile application that uses ROPC and desperately needs to federate with another identity provider, consider taking inspiration from the UK’s Open Banking by turning your mobile app into your authorization server.

By using the app2app flow, your mobile app becomes an OAuth authorization server, hijacking any authorization requests made to your authorization server and using the mobile app’s long-lived session to identify the current user.

This approach requires a custom API in your actual authorization server to validate the authorization request and generate authorization codes. I’ll likely blog about this more in the future.

Create an exit plan

If you are already using the ROPC grant, start planning how to migrate off of it and ensure that no new applications are released using this grant type.

It’ll be a tough sell, but by using the above article to communicate why SSO, MFA, and 3rd party logins are not possible with the current design, hopefully, you will have enough evidence to argue your case.

Summary

There are no remaining use cases for the ROPC grant. It has served its purpose, and now it is obsolete. There is more to user authentication than a login form and an API call. Don’t code yourself into a corner by using ROPC. Do everything you can to prevent new applications from being released with this grant type.

Content is licensed under CC BY 4.0. Remember, don't copy and paste code written by strangers on the internet.