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

Issue fork drupal-3530640

Command icon 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

prudloff created an issue. See original summary.

prudloff’s picture

I 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.

prudloff’s picture

The change could be similar to this: https://www.drupal.org/node/3359827

longwave’s picture

Should 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?

catch’s picture

More than once I wondered why this wasn't provided by REST module so that makes sense to me.

danielveza’s picture

How 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?

prudloff’s picture

I'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.

longwave’s picture

Is 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.

mstrelan’s picture

I'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.

I 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.

prudloff’s picture

Status: Active » Needs work

Thanks, 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?

Is there a use case for using the REST login route without having the REST module installed?

I think you could also be using this route with jsonapi.

Some tests RestLoginHttpTest are now failing, I need to investigate why.

prudloff’s picture

Issue summary: View changes
prudloff’s picture

Status: Needs work » Needs review

Tests are now green.

longwave’s picture

Do we need to change the paths here? It would be better if users of the endpoints didn't have to update their code.

prudloff’s picture

How 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?

longwave’s picture

Wondering 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.

ironnuts’s picture

I 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.

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.

At #10 @mstrelan refers to his participation in a route deprecation involving the comment module: deprecation of a route

catch’s picture

Route 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.

prudloff’s picture

Yes I think we have to deprecate both the routes and the controller.

Currently the MR does this in user module:

  • The routes are deprecated (this does not trigger a deprecation when generating a link to these routes because they are not aliases).
  • The controller is deprecated (for static analysis tools).
  • Instantiating the controller triggers a runtime deprecation.

And in rest module:

  • The controller is duplicated (same code but without the deprecations).
  • Added new routes (with different path) that use this new controller.

If we want to keep the same paths, I think we could do something like this in the rest module instead:

  • If the rest module is installed, alter the user routes to use the new non-deprecated controller.
  • Add new non-deprecated route names that are aliases of user routes (for now).
catch’s picture

#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.

mstrelan’s picture

I 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.

ironnuts’s picture

Re #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.

smustgrave’s picture

Title: Disable the user.login.http route by default » Disable the user.login.http route by default and move to REST
Status: Needs review » Reviewed & tested by the community
Issue tags: +Needs Review Queue Initiative

Had 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.

longwave’s picture

Status: Reviewed & tested by the community » Needs work

I 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.

longwave’s picture

Status: Needs work » Needs review

Implemented #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.

longwave’s picture

Status: Needs review » Needs work

Added some review comments as well.

longwave’s picture

Even 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?

longwave’s picture

Status: Needs work » Needs review

This 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.

longwave’s picture

Did some more self review, maybe we should make further minor changes here?

smustgrave’s picture

Status: Needs review » Needs work

One of the deprecations appears to be pointed at 10.3? But they should be updated to 11.4 now I believe with beta out.

prudloff’s picture

I 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?

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

prudloff’s picture

Status: Needs work » Needs review

I added a test for DeprecatedUserRoutesSubscriber.

smustgrave’s picture

Seems like high potential disruption. Should it be removed in 13 vs 12

longwave’s picture

As 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.

smustgrave’s picture

Status: Needs review » Needs work

Gotcha,

Did leave some small comments on the MR.

prudloff’s picture

Status: Needs work » Needs review

I 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.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Believe feedback for this one has been addressed

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The 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.

smustgrave’s picture

Status: Needs work » Reviewed & tested by the community

This one was positive, conflict in the baseline. Rebased and restoring status.

smustgrave’s picture

Status: Reviewed & tested by the community » Needs work

JK actually a phpstan error.

prudloff’s picture

Status: Needs work » Needs review

I fixed the phpstan errors.

dcam’s picture

Status: Needs review » Reviewed & tested by the community

I 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.

catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

Updated 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

  • catch committed 20ee7c83 on main
    task: #3530640 Disable the user.login.http route by default and move to...
kim.pepper’s picture

The CR is still draft

alexpott’s picture

@kim.pepper thats because we need to backport to 11.x first.

kim.pepper’s picture

ah ok

longwave’s picture

Status: Patch (to be ported) » Needs review

Backported via cherry-pick from main and manual fix of merge conflicts.

longwave’s picture

This is worth calling out in the release notes I think.

damienmckenna’s picture

This is worth calling out in the release notes I think.

Agreed, I've seen several sites that have custom workarounds for this.

dcam’s picture

Status: Needs review » Needs work

I 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.

longwave’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Seems like a good backport

longwave’s picture

Status: Reviewed & tested by the community » Needs review

Some trickiness here in the backport that the tests caught. In Drupal 11 we still need to support both UserAuthInterface and UserAuthenticationInterface. The backported RestAuthenticationController only handled the latter, so I had to copy the code that handles both interfaces (including the deprecation notice) from UserAuthenticationController.

The BC test is in UserJsonBasicAuthDecoratedTest which now passes locally. The fix is self-contained in the last commit to the backport branch.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Oh nice find, forgot about that fun even when I did the user deprecation removal.

  • catch committed c0e71efe on 11.x
    task: #3530640 Disable the user.login.http route by default and move to...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed the backport MR to 11.x, thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

prudloff’s picture

This 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:

  1. Run DeprecatedUserRoutesSubscriber after any contrib route subscriber.
  2. If the user routes have been altered, copy these alterations on the rest routes.

We will need a test that proves it does not break contrib modules altering the route.

catch’s picture

Version: 11.x-dev » main
Status: Closed (fixed) » Needs work

Per #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.

  • catch committed ee959a7c on 11.4.x
    Revert "task: #3530640 Disable the user.login.http route by default and...

  • catch committed 539a0b89 on 11.x
    Revert "task: #3530640 Disable the user.login.http route by default and...

  • catch committed 21eace16 on main
    Revert "task: #3530640 Disable the user.login.http route by default and...

znerol made their first commit to this issue’s fork.

znerol’s picture

Status: Needs work » Needs review

Opened MR !15891 to fix phpstan in head. NR for that.

  • longwave committed fa72652e on main
    fix: #3530640 PHPStan baseline hotfix
    
    by: znerol
    
longwave’s picture

Status: Needs review » Needs work

Committed the baseline hotfix, thanks. Back to NW for #64.

joegraduate’s picture

Should the status of the change record for this be changed back to draft since this change was reverted?
https://www.drupal.org/node/3552724

longwave-bot made their first commit to this issue’s fork.

longwave’s picture

Status: Needs work » Needs review
Issue tags: -11.4.0 release notes, -12.0.0 release notes

Tried 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.

prudloff changed the visibility of the branch main to hidden.

catch’s picture

I'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?

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new3.17 KB

The 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.

smustgrave’s picture

Think this is going to miss the 12 removal timeframe and have to go to 13?