Problem/Motivation
Core provides two ways to login:
- The login form
- The REST login route
It is pretty common for modules that add additional protection to the login process (for example OTP) to not protect the REST login.
See for example https://www.drupal.org/sa-contrib-2025-056.
I think most websites don't use the REST login so we could disable it by default, this would harden websites using modules that forget to protect this route.
Steps to reproduce
curl --header "Content-type: application/json" --request POST --data '{"name":"username", "pass":"password"}' 'http://example.com/user/login?_format=json'
Proposed resolution
We could move the route (and other related routes) to the REST module.
This would reduce the number of websites with this route while providing an easy way to enable it if needed.
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|
Issue fork drupal-3530640
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
prudloff commentedI also had a look at how WordPress handles this: it is not possible to use a standard password to login with REST.
Users have to generate an application password that can then be used to login with the REST API.
These passwords are always long and randomly generated, this makes it more OK to not have 2FA on this login method because application passwords would be very hard to brute-force.
Comment #3
prudloff commentedThe change could be similar to this: https://www.drupal.org/node/3359827
Comment #4
longwaveShould the route move to rest.module? We could either enable it by default only if rest.module is enabled, or add it as a config option there?
Comment #5
catchMore than once I wondered why this wasn't provided by REST module so that makes sense to me.
Comment #6
danielvezaHow would this work in terms of BC? Would we need to deprecate the route in the user module when we move it to rest, then remove it from user in D12?
Comment #8
prudloff commentedI'm not sure how to deprecate the route.
We only support deprecations on route aliases but when can't make user.login.http an alias of the new route because users without the rest module will not have the new route.
Comment #9
longwaveIs there a use case for using the REST login route without having the REST module installed? It seems like a niche thing to do, we could just document in the release notes that you have to enable the REST module now if you are using this.
Comment #10
mstrelan commentedI had the same issue on #3542528: Deprecate route comment.new_comments_node_links, deprecated the old route and create a new one with a different name.
Comment #11
prudloff commentedThanks, I did something similar. But by looking at the code from #3159210: Support route aliasing (Symfony 5.4) and allow deprecating the route name it seems the deprecated key does nothing if the route is not an alias (it is still useful if someone is looking at the YAML but it will not trigger a runtime deprecation).
But having this info in the YAML + a CR might be enough here?
I think you could also be using this route with jsonapi.
Some tests RestLoginHttpTest are now failing, I need to investigate why.
Comment #12
prudloff commentedComment #13
prudloff commentedTests are now green.
Comment #14
longwaveDo we need to change the paths here? It would be better if users of the endpoints didn't have to update their code.
Comment #15
prudloff commentedHow would that work? Can two routes have the same path?
Or do you think we should remove the routes from the user module instead of deprecating them?
Comment #16
longwaveWondering if we can mark them as deprecated in user.routing.yml then alter the routes in rest.module (if it's installed) to remove the deprecation again. Then in Drupal 12 we move them directly to rest.routing.yml.
When do the deprecations get triggered for deprecated routes? It's not clear to me how people will discover this change if they are using the endpoints externally.
Comment #17
ironnuts commentedI see route aliasing a la Symfony 5.4 seems to have been agreed as a way to deprecate routes. See #19 and #20 in: old issue on route aliasing.
At #10 @mstrelan refers to his participation in a route deprecation involving the comment module: deprecation of a route
Comment #18
catchRoute aliasing and deprecation was only ever for deprecating route names, for people generating links to the old route etc. not for actual requests to the URL.
I think we probably need to trigger a deprecation from the controller in this case.
Comment #19
prudloff commentedYes I think we have to deprecate both the routes and the controller.
Currently the MR does this in user module:
And in rest module:
If we want to keep the same paths, I think we could do something like this in the rest module instead:
Comment #20
catch#19 would mean a stable API for clients (as long as the site itself enables rest module) and seems like it otherwise would work to me.
Comment #21
mstrelan commentedI think the user module could do a route alter to restore the deprecated route if the rest module is not installed, rather than the rest module doing it. But I will admit I haven't been following closely.
Comment #22
ironnuts commentedRe #19 I came across this issue controller resolver.The advantage of a controller resolver is the controller is never hit by the request. By putting the code inside it and making it conditional on the route, it is immaterial which core module is involved. Ideas like #21 might be easier to implement in the controller resolver?
I have left a couple of code comments.
Comment #23
smustgrave commentedHad this opened from yesterday but wanted to verify we could still deprecate in 11.3 with the alpha out and we are good.
Resolved the threads as the deprecations as is are correct for routes.
Reviewed the CR https://www.drupal.org/node/3552724 well detailed and written and before/after snippets for how others can replace their code.
Going to mark but will mention is this one of the larger changes that maybe should be removed in 13
Took a quick stab at credit saving.
Comment #24
longwaveI think we need to do something along the lines of #19, we don't want to force users/clients to change the paths they use just because of this.
Comment #25
longwaveImplemented #19 by changing the rest.module paths so they are the same as user.module's, and adding an event subscriber to change the deprecated controller to the non-deprecated one if REST is installed.
Comment #26
longwaveAdded some review comments as well.
Comment #27
longwaveEven wondering if we can keep the same route names somehow - then users with REST module see no changes at all, and users who were using this feature just have to enable it?
Comment #28
longwaveThis should work without changing paths for end users - now you either have to install REST module to allow REST logins, or do nothing if you already had it installed.
Comment #29
longwaveDid some more self review, maybe we should make further minor changes here?
Comment #30
smustgrave commentedOne of the deprecations appears to be pointed at 10.3? But they should be updated to 11.4 now I believe with beta out.
Comment #31
prudloff commentedI updated the deprecation and applied suggested changes.
I had to revert some of the suggested changes because it broke things.
Should we add a test to make sure DeprecatedUserRoutesSubscriber works correctly?
Comment #33
prudloff commentedI added a test for DeprecatedUserRoutesSubscriber.
Comment #34
smustgrave commentedSeems like high potential disruption. Should it be removed in 13 vs 12
Comment #35
longwaveAs a security improvement the benefits hopefully outweigh the disruption. If you are using these paths, you just need to enable the REST module. If you are referring to the route names internally, you do need to update your code, but I would hope that is rare - given most users of this functionality are doing it from outside Drupal to get access to Drupal.
Comment #36
smustgrave commentedGotcha,
Did leave some small comments on the MR.
Comment #37
prudloff commentedI applied the suggestions.
Note that the "new" classes are copied from the user module, that's why they don't use constructor promotion or other modern features.
Comment #38
smustgrave commentedBelieve feedback for this one has been addressed
Comment #39
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #40
smustgrave commentedThis one was positive, conflict in the baseline. Rebased and restoring status.
Comment #41
smustgrave commentedJK actually a phpstan error.
Comment #42
prudloff commentedI fixed the phpstan errors.
Comment #43
dcam commentedI validated the most recent changes. They are in line with modifications necessary for making PHPStan happy - adding trait functions to the baseline and updating a deprecated function call.
Since I'd never looked at this one before I gave the whole MR a review. But it's been looked at enough by other people. I didn't find anything to comment on. So all I can say is that the most recent changes are good.
Comment #44
catchUpdated the deprecation messages to be from 11.4.0, otherwise looks looks great so went ahead and committed/pushed to main, thanks!
We'll need a backport MR here for 11.x
Comment #47
kim.pepperThe CR is still draft
Comment #48
alexpott@kim.pepper thats because we need to backport to 11.x first.
Comment #49
kim.pepperah ok
Comment #51
longwaveBackported via cherry-pick from main and manual fix of merge conflicts.
Comment #52
longwaveThis is worth calling out in the release notes I think.
Comment #53
damienmckennaAgreed, I've seen several sites that have custom workarounds for this.
Comment #54
dcam commentedI waited a bit to see if there would be a follow-up commit to update the baseline, but since there hasn't been one yet I'm setting the status to Needs Work.
Aside from that, I reviewed the backport by comparing it to the version committed to main. They're practically identical with the exception of the baseline. Once that's updated, then I'll RTBC it.
Comment #55
longwaveComment #56
smustgrave commentedSeems like a good backport
Comment #57
longwaveSome trickiness here in the backport that the tests caught. In Drupal 11 we still need to support both
UserAuthInterfaceandUserAuthenticationInterface. The backportedRestAuthenticationControlleronly handled the latter, so I had to copy the code that handles both interfaces (including the deprecation notice) fromUserAuthenticationController.The BC test is in UserJsonBasicAuthDecoratedTest which now passes locally. The fix is self-contained in the last commit to the backport branch.
Comment #58
smustgrave commentedOh nice find, forgot about that fun even when I did the user deprecation removal.
Comment #60
catchCommitted/pushed the backport MR to 11.x, thanks!
Comment #64
prudloff commentedThis could cause regressions in contrib modules that alter user.login.http route with a route subscriber.
After this change, if the rest module is enabled the REST login request will go to the rest.login route which is not altered.
We should probably add some compatibility layer to avoid this.
One way to do it could be:
We will need a test that proves it does not break contrib modules altering the route.
Comment #65
catchPer #64 I've reverted this from the main, 11.x and 11.4.x branches.
Given this is security hardening, it would be good to try to get a better bc layer in and recommit it to 11.4 if we can, although not much time to do that and getting it into main/11.5 properly is more important than rushing.
Comment #71
znerol commentedOpened MR !15891 to fix phpstan in head. NR for that.
Comment #73
longwaveCommitted the baseline hotfix, thanks. Back to NW for #64.
Comment #75
joegraduateShould the status of the change record for this be changed back to draft since this change was reverted?
https://www.drupal.org/node/3552724
Comment #78
longwaveTried to address #64, also rebased against main and modernised a bit of the new REST controller.
Re #75 I also unpublished the change record for the time being.
Comment #80
catchI'm not sure how this fits in with #3578868: [policy, no patch] Move REST module to contrib - what should sites that want to support logging users in, which JSON:API doesn't cover, but don't want the rest of REST module do? Even if we don't move REST to contrib it seems like this might want to be in its own dedicated module instead?
Comment #81
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #82
smustgrave commentedThink this is going to miss the 12 removal timeframe and have to go to 13?