Jwt-auth: Updated Token Flow UX

Created on 10 May 2019  ·  7Comments  ·  Source: WP-API/jwt-auth

Issue Description

In https://github.com/WP-API/jwt-auth/commit/e4c66f91205b70d74df1ce341cd3dbbdf43827f7, we've committed a change that only allows tokens to be generated with data from a valid key pair.

I'm concerned that this change makes the functionality of this plugin inaccessible to most non-technical users, who expect to be able to log in using their username and password.

The key-pair approach has a few drawbacks that hurt usability (these are real-world issues that we ran into when we used a similar scheme for the WooCommerce plugin):

It's non-intuitive
Any other system that wants to use this method to integrate with WordPress will need to have an explanation/tutorial of how to set up a key pair in order to get started.

It's hard to use between devices
If I'm hoping to use this key for an app on my phone, there's no easy way to get it there. I don't want to type a long string of letters, numbers, and special characters on my phone – I'm likely to make a transcription error. We can remove the transcription difficulty level by generating a QR code that folks could scan, but then we come back to the problem above – folks may need to download a QR code app, or third-party devs would need to build a QR-code parsing library into their app in order to work with us.

It doesn't encourage good security hygiene
Ideally, users would carefully maintain their keys in order to ensure that none are out of date, uncompromised, etc. Unfortunately, we can't count on this, and compromised keys are even more dangerous than compromised tokens. It's also likely that an average user will create a new key each time they need one, rather than storing the secret somewhere safe.

Additionally – I'm concerned about adding an additional method for revoking sessions – WordPress already has a "Log Out Everywhere Else" button on the profile page – as a user, I'd expect that would log me out of any applications as well (or at least prompt me for whether I'd like to do that?)

It doesn't provide any additional security
If I, as an attacker, already have your username and password, there's nothing I can't do as you (assuming there's no 2FA in place).

This leads to the fact that, if I as a novice application developer want to make my user's life easier, I might try something inadvisable such as using a headless browser to log in to the user's account, create a token, read the details, then pull them back into my app.

What could be done instead?

The key currently serves an important purpose – it's used to allow revocation of a token, despite tokens potentially being very long-lived. However, we don't necessarily need a key pair to do this.

Taking inspiration from WordPress' session_tokens entry in usermeta – a stored set of JWT -specific revocation keys would allow the same functionality, and it would make it trivial to implement the "Log Out Everywhere Else" functionality – just delete that entry from the table.

What about 2FA?
I'm wondering if it might be possible to allow a hook that 2FA plugins could use to tell the client "please try again, providing a 2FA token" if one was required, but not provided. Additionally, another hook (or perhaps the same one?) could be used to allow the 2FA plugin to verify that the code provided is correct? The JWT system would make no assumptions about how 2FA is implemented – that'd be left to another plugin – we'd just provide the hooks to make such a thing possible.


Thanks for reading this far – all of the above is IMHO, but I'm hopeful we can simplify this process a little bit for users to make it work really well out-of-the-box for everyone.

I'm sure I've missed at least _something_, so I'm happy to hear any feedback folks may have on this!

question

Most helpful comment

Since a user has to log in to Wordpress (hopefully via https) in the first place, using a username and password, I really don't see what the security issue is of having the username/password pair being able to get an access token? It's all being sent over the same 'wire' at the end of day? Surely?

Enforcing the currently implemented application key-pair makes this authentication method absolutely impossible (in the real world) for it to be used for mobile applications.

There's just no sane way to get a mobile app to enter them.

I've tried the OAuth1a, OAuth2 and now two JWT plugins to implement my native apps with access and all of them fail 'out-of-the-box' , without some hacking around to try and get things to work for a user. This stuff is supposed to get easier, surely?

The third-party JWT plugin out there (with 10,000+ installs) supports user/pass, and works well for a native mobile application I've been building. But it fails on the lack of refresh tokens via the plugin, forcing the user to 'log in' again. (it also requires manual editing of wp-config)

This 'soon-to-be-official' (I hope!) plugin fails on the lack of mobile-user-friendliness and seemingly ignoring what kind of applications can realistically be developed for it. And that seems very short-sighted.

There's a reason why there's a lack of native mobile apps for posting to wp.org sites. And it's this.

Please, please please can we get some movement on this? (I've been working on a native app for years now, waiting for a 'proper' way to authenticate users, since the introduction of the REST API)

:)

All 7 comments

Thank you for taking the time to write all this out. If you feel anything that follows is harsh, it is not intended to be, I'm just giving you my opinion and is not the only one, there are likely a mixture of them and debate is fine.

I'm concerned that this change makes the functionality of this plugin inaccessible to most non-technical users, who expect to be able to log in using their username and password.

I get what you're trying to say here but at the same time what you're suggesting is that requiring a user to log into WordPress and create a key-pair is somehow to difficult for non-technical users that want to generate a token via the REST API programmatically. I'd argue that these are not _non-technical_ users at all.

As well, in every secure system there is going to be barriers to entry that the user must overcome to use it. Google doesn't allow you to create a JWT or OAuth connection by using your username and password in the API request. That's not secure. There has to be a way to ensure that a token or auth connection is attached to a user in a way that can be invalidated.

It's non-intuitive
Any other system that wants to use this method to integrate with WordPress will need to have an explanation/tutorial of how to set up a key pair in order to get started.

In order to have a secure model that supports the separation of access and authentication you have to have guardrails that keep long-lived tokens from becoming an infinite point of access. With the addition of refresh tokens a malicious user could create an access and refresh token with a user:pass combination that we could never invalidate if there was not a key-pair, or some equivalent, to invalidate the tokens against.

Telling the user to login to WordPress and go to their Profile to generate a key-pair is a small price to pay for security. As well, the directions are very simple.

It's hard to use between devices
If I'm hoping to use this key for an app on my phone, there's no easy way to get it there. I don't want to type a long string of letters, numbers, and special characters on my phone – I'm likely to make a transcription error. We can remove the transcription difficulty level by generating a QR code that folks could scan, but then we come back to the problem above – folks may need to download a QR code app, or third-party devs would need to build a QR-code parsing library into their app in order to work with us.

Please describe this app you keep referring to, why would it handle all the auth and login for you? That's not secure. You would be giving this app all the access and authentication it needs to do anything it wants to your website. You should consider that maybe the apps architecture is incorrect and this auth flow is to prevent the exact thing you're describing.

What you've describe to me is an app that stores your plaintext username and password so it can login to WordPress via the REST API to generate a token that is used to access resources, but is also fully capable of dumping your database through the same means of authentication and access. I don't see why we would want to support anything remotely close to this use-case as you've described it.

It doesn't encourage good security hygiene
Ideally, users would carefully maintain their keys in order to ensure that none are out of date, uncompromised, etc. Unfortunately, we can't count on this, and compromised keys are even more dangerous than compromised tokens. It's also likely that an average user will create a new key each time they need one, rather than storing the secret somewhere safe.

First, key-pairs are not attached to an expiration date and the only thing they can do right now is create tokens so I fail to see how they are _more dangerous_ than a compromised token since they are the thing that invalidates that token. Second, users do nothing carefully, and we should never expect them to understand the security implications of their actions. We have to assume they are extremely incompetent and likely lazy enough to never clean up after themselves. However, creating additional key-pairs is not a security issue. The security issue would be giving your plaintext username and password to a foreign app.

Additionally – I'm concerned about adding an additional method for revoking sessions – WordPress already has a "Log Out Everywhere Else" button on the profile page – as a user, I'd expect that would log me out of any applications as well (or at least prompt me for whether I'd like to do that?)

We can prompt the user to delete their key-pairs but I would never expect a logout to destroy my token. That's not the normal or expected behavior of a system like this and would likely create chaos and tons of support requests.

It doesn't provide any additional security
If I, as an attacker, already have your username and password, there's nothing I can't do as you (assuming there's no 2FA in place).

I categorically disagree. This approach provides additional security to access your REST resources and if that's not obvious then we need more documentation to make it obvious. You're arguing that if someone has your credentials already then you're screwed, but you're also suggesting that we use them to give access to the REST API. If that's how you want to access REST then use Basic Auth. Because that is exactly what we would be implementing if we use user:pass to create tokens.

This leads to the fact that, if I as a novice application developer want to make my user's life easier, I might try something inadvisable such as using a headless browser to log in to the user's account, create a token, read the details, then pull them back into my app.

This is where we would want to create an auth flow that allows for giving an app the key-pair through the use of a callback. I believe something like this has already been implemented in the application passwords plugin.

I'm 100% open to creating an auth flow for apps that when you go to it you are asked to login to WordPress, with any extras like 2factor added, that would generate a key-pair for you and then using some sort of callback give the app the key-pair. Then app developers don't have to create tons of bad solutions and the app doesn't store their user:pass.

What could be done instead?
The key currently serves an important purpose – it's used to allow revocation of a token, despite tokens potentially being very long-lived. However, we don't necessarily need a key pair to do this.

Correct, you could use many different approaches.

Taking inspiration from WordPress' session_tokens entry in usermeta – a stored set of JWT -specific revocation keys would allow the same functionality, and it would make it trivial to implement the "Log Out Everywhere Else" functionality – just delete that entry from the table.

This would never be a replacement of the current implementation it would have to be in addition to. I would, as an enterprise software developer, never use this method. So even though it is a valid approach it lacks the ability to expect a token to be valid for as long as it is not expired or the token is not revoked. In your scenario the second you logout the token is invalid. This would be IMO a deficiency in the auth flow that would cause countless problems with my apps ability to get resource access as it is now tightly coupled to the user being logged in.

What about 2FA?
I'm wondering if it might be possible to allow a hook that 2FA plugins could use to tell the client "please try again, providing a 2FA token" if one was required, but not provided. Additionally, another hook (or perhaps the same one?) could be used to allow the 2FA plugin to verify that the code provided is correct? The JWT system would make no assumptions about how 2FA is implemented – that'd be left to another plugin – we'd just provide the hooks to make such a thing possible.

This would IMO add an unnecessary layer of complexity that has the potential to create lots of support requests.

Thanks for reading this far – all of the above is IMHO, but I'm hopeful we can simplify this process a little bit for users to make it work really well out-of-the-box for everyone.

I'm sure I've missed at least something, so I'm happy to hear any feedback folks may have on this!

Epic post. Thank you for taking the time to write all this down. You really did give this a lot of thought. I want to say that I'm not trying to shoot anything down. Just that if we are going to allow tokens to be created with a user:pass combination then the way we do that needs to be very very secure and have zero potential for apps to store your login credentials.

The point of separating the access and authentication is purposeful and should not be taken lightly. We are building the potential REST authentication for 1/3 of the internet. So until we have a path forward I removed the ability to create tokens with user credentials. That doesn't mean we can't add it back, just that it needs a lot of discussion before we do.

Hi! From what I understand, the main pain point about the conversation here is usability. Indeed having users going to the site to login and create the key pairs will be a source of problems, or incovenience, for somes users.

Developers will likely try to find unsecured ways to avoid that in the same way today they remove functionality that requires strong passwords for the sake of their users.

Although I do understand the security aspect in the decision, and kind of agree with. But we need to find a way that can work for both.

It seems what you said here @valendesigns could be a possible solution:

I'm 100% open to creating an auth flow for apps that when you go to it you are asked to login to WordPress, with any extras like 2factor added, that would generate a key-pair for you and then using some sort of callback give the app the key-pair. Then app developers don't have to create tons of bad solutions and the app doesn't store their user:pass.

From what I understand, basically we would keep the current flow, which is more secure as we don't need to give our password to the app instead giving only the key-pair to generate the token, but that might be a hurdle for some.

Keeping this other flow where we wouldn't need to give our password to the foreign app. Instead, asking them to log to WordPress and generate the key-pair.

I do would like more information about this part:

We can prompt the user to delete their key-pairs but I would never expect a logout to destroy my token. That's not the normal or expected behavior of a system like this and would likely create chaos and tons of support requests.

It appears if I'm logged in site.com via the browser and the app, if I click in the "Log Out Everywhere Else" in the browser, I also would be logout in the app, no?! I can see apps where this would be the assuption. The expectation you would be logged out everywhere.

The same would need to happen when an account is deleted.

I have to agree with what was said about the key-pair UX being very user unfriendly.

It makes it very hard to build a native mobile app that can use this Auth method.

  • I had made a custom plugin a while ago for the (old) OAuth1a plugin which displayed an encrypted (with a short, settable password) QR code in the Applications admin page (when the app name and callback url matched my application), which then passed the client app info to the mobile app, to then allow the user to authenticate.

Granted (pun intended), JWT is a lot easier than OAuth1a ;)

Since a user has to log in to Wordpress (hopefully via https) in the first place, using a username and password, I really don't see what the security issue is of having the username/password pair being able to get an access token? It's all being sent over the same 'wire' at the end of day? Surely?

Enforcing the currently implemented application key-pair makes this authentication method absolutely impossible (in the real world) for it to be used for mobile applications.

There's just no sane way to get a mobile app to enter them.

I've tried the OAuth1a, OAuth2 and now two JWT plugins to implement my native apps with access and all of them fail 'out-of-the-box' , without some hacking around to try and get things to work for a user. This stuff is supposed to get easier, surely?

The third-party JWT plugin out there (with 10,000+ installs) supports user/pass, and works well for a native mobile application I've been building. But it fails on the lack of refresh tokens via the plugin, forcing the user to 'log in' again. (it also requires manual editing of wp-config)

This 'soon-to-be-official' (I hope!) plugin fails on the lack of mobile-user-friendliness and seemingly ignoring what kind of applications can realistically be developed for it. And that seems very short-sighted.

There's a reason why there's a lack of native mobile apps for posting to wp.org sites. And it's this.

Please, please please can we get some movement on this? (I've been working on a native app for years now, waiting for a 'proper' way to authenticate users, since the introduction of the REST API)

:)

The token flow UX is bonkers and impractical for real-world usage, users should be able to login with username and password on the system asking for an API key pair does not make any sense, you are not employing abstraction here, you're supposed to shield users from the technicalities of using your application, how will you feel that in order to log in to Facebook on your phone you need to log in to an admin portal on facebook and create an API key pair and then use the API key pair to log in, we aren't developing for developers, we're developing for the customer who does not know what the technical aspect of your service consists of, uninstalling right away. I really don't see how this plugin is useful for authentication.

This issue with user login > token flow is seriously holding up development of SO many apps that could be using JWT.

Months and months keep going by and it's simply not being addressed.

As I say above, the fact that users use a username/password pair to actually Log into Admin makes any argument for using not using username/password pairs to get a token seem somewhat invalid? Surely?

Just to update this thread – it sounds like this project is being superseded by one to implement a full OAuth flow in WP core. It should address the downsides of the approaches discussed here while retaining all of the strengths.

If you'd like to get involved with that, feel free to jump into #core-restapi on Slack.

Was this page helpful?
0 / 5 - 0 ratings