Needs review
Project:
Drupal core
Version:
main
Component:
user.module
Priority:
Normal
Category:
Task
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
5 Jun 2013 at 18:46 UTC
Updated:
18 May 2026 at 06:53 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
swentel commentedIt's already on the UserController, so I think we're done here ?
Comment #2
ParisLiakos commentednope. i dont think we should suggest calling controller methods directly. still the API call here is user_logout() which is procedural
Comment #3
Crell commentedWe need a service or method on a service that explicitly logs out an active user. It should be called FROM a controller, but not be on a controller. Controllers should not have meaningful business logic in them.
Comment #4
dawehnerI really wonder whether it is worth to make an interface here and I can't get some sleep.
Comment #6
ParisLiakos commentedif we are introducing a separate service for this i think we should generalize it more and include similar functions eg
user_login_finalize
maybe call it UserHandler? or AccountHandler
Comment #7
dawehnerMark will come up with a better name anyway if needed.
Comment #8
dawehnerUps.
Comment #10
ParisLiakos commentedWe have UserAuth now, its probably a nice place to put those there, too
Comment #16
mile23Comment #17
jibranWhich include file are we killing here?
Comment #19
kim.pepperYeah, I agree. This is just methods on user.module
Comment #26
avpadernoThe issue summary must be updated too. It is not completely clear what should be done and why.
Comment #28
kim.pepperComment #29
kim.pepperComment #30
kim.pepperComment #31
kim.pepperComment #32
andypostIt looks nice but naming confusing me, as just landed #2536052: Remove try...catch from SessionHandler::write with another session handler
Maybe user module should just decorate
\Drupal\Core\Session\SessionHandler?it's the real storage service which is mussing in related issue
Comment #33
andypostComment #34
smustgrave commentedAll threads appear to be resolved and tests all green.
This was tagged for CR updates so moving to NW for that.
Good job everyone!
Comment #35
kim.pepperUpdated CR
Comment #36
smustgrave commentedUpdated CR reads well.
Thanks @kim.pepper!
Comment #37
lauriiiAdded few comments to the MR. Tagging for framework manager review to get a review on the new API.
Comment #38
kim.pepperThanks for the reviews. Addressed all feedback.
Comment #39
smustgrave commentedWith 10.2 approaching going to remark this for committers/framework managers to take a look.
Comment #40
alexpott#32 was never really addressed - the name UserSessionHandler implies something that extends \SessionHandlerInterface like \Drupal\Core\Session\SessionHandler but this is not that.
Also by introducing \Drupal\user\UserSessionHandlerInterface and the service we are creating a new swappable API which feels like something we don't actually want to do. Are there use-cases?
We also need to update the deprecations for 10.3.
Comment #41
kim.pepperRemoved
UserSessionHandlerInterfaceand updated deprecation notices to 10.3.0.Not sure what a better name is? Maybe
UserLoginHandler?Back to NR for feedback on that suggestion.
Comment #42
andypostOTOH there's
SessionManagerInterfacewhich can be extended withfinalizeLogin()andfinalizeLogout()methodsComment #43
znerol commentedI'd prefer if
SessionManagerInterfacewill go away in the long run. All of the remaining code inSessionManageris supposed to be extracted to session handler decorators or even to upstream.Comment #45
kim.pepperHow about
UserSessionFinalizerwithfinalizeLogin()andfinalizeLogout()?Comment #46
andypostit sounds good idea, needs to check which services can get decorators to react of login/logout
Comment #47
andypostComment #49
larowlanLooks great.
Couple of places where we're missing docs (can't use @inheritdoc if there is no parent interface).
#45 sounds like a good idea.
This feels like a case where we would want to make the service final to make it a bit harder to swap out.
Looks to have failing tests.
Comment #50
kim.pepperComment #51
smustgrave commentedAppears all feedback has been addressed.
Since this has gone through several rounds of reviews I'm going to remark it vs the new #needs-review-queue-initiative strategy of waiting a week.
Comment #52
alexpottI think #51 is premature. #45 suggests a name for the class and there's general agreement but that's not what is the MR.
Also the service class is not final as suggested by #49
Comment #53
kim.pepperRenamed to
UserSessionFinalizerand made the service classfinal.Comment #54
kim.pepperGetting this error in the test failure which I think is unrelated:
Looks like it was a random fail.
Comment #55
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 #56
kim.pepperRebased on 11.x
Comment #57
andypostThank you! It looks ready!
btw as the new service is public it makes it an extension point, literally hooks user_(ligin|logout) can be implemented as the service decorators?
Comment #58
smustgrave commentedAppears feedback has been addressed.
@andypost let me know if I'm wrong but sounds like in #57 you're asking a general question correct?
Comment #59
catchThe issue summary and change record no longer match the MR.
Comment #60
kim.pepperComment #61
kim.pepperUpdated CR and IS. As I just updated the service and method names, and there are no code changes, I think it's safe to re-RTBC.
Comment #62
quietone commentedI'm triaging RTBC issues. I read the IS and the comments. I didn't find any unanswered questions.
I read the MR and made some comments. One of which is questioning the name of the service so I am setting to NR to settle that.
Comment #63
quietone commentedI chatted with @kim.pepper who wasn't not opposed to changing UserSessionFinalizer to UserSessionFinalize. Therefore, I have made a commit for that. And also updated a comment.
Comment #64
quietone commentedI did not update the change record. Happy to do that if folks agree to the name change.
Comment #65
smustgrave commentedI think Finalize makes sense too. I updated the CR and issue summary to reflect that.
Comment #66
catchThere are some things I'm not sure about with this one but not sure I can articulate all of them. Left some review comments on the MR for now.
Comment #67
longwave@catch asked for further opinions on this. My first thought was that really we are just moving code around; procedural code is now in a service that isn't meant to be swapped (because it's declared final), so what do we gain here?
Also, a new service with seven very different dependencies is a bit of a code smell. Should login and logout be events instead? Then each subsystem can declare event subscribers and react as they see fit, and each part would be replaceable individually if someone wanted to switch something out?
Comment #68
znerol commentedI was thinking the same.
Comment #69
berdir> @catch asked for further opinions on this. My first thought was that really we are just moving code around; procedural code is now in a service that isn't meant to be swapped (because it's declared final), so what do we gain here?
See #1905334: Only load all modules when a hook gets invoked. What we gain is that we move one of the few remaining .module functions called from controllers and other OOP code, allowing us to load .module files only when a hook is invoked.
> Should login and logout be events instead? Then each subsystem can declare event subscribers and react as they see fit, and each part would be replaceable individually if someone wanted to switch something out?
We already have login/logout hooks. This is the API responsible for calling these hooks. I think we shouldn't have events *and* hooks for the same thing, so is the proposal to replace the existing hooks with an event and the sole API for finalizing login/logout would be to invoke the respective event?
That does sound sensible, we do have some existing examples for that, for example around file name sanitization. But that would also make this a considerably larger issue as we'd need to convert existing hook_user_login/logout implementations, deprecate the hook and also think about the API design of these hooks. One of the major use cases for hook_user_login() is to control where the user is redirected to afterwards and that's only possible with workarounds atm which tend to break other implementations of that hook.
FWIW, basically everything the service does is a user module thing, I don't think we could really split that up much apart from separating login and logout.
> Given we log separately here, do we need the logging to be done in the finalize service itself? I think we could take that out of the service and callers are responsible for logging if they want to.
I can see an argument for both options. Ensuring that each login is properly logged seems important from a security perspective, so it seems sensible to do it in the API. But separating would avoid duplicate logs and logs could be more specific.
Comment #70
smustgrave commentedSo this is something that should still move forward?
Comment #71
catchI think what @longwave meant here was that the methods added here would be a wrapper around the even dispatcher, and the implementation would move into the event - e.g. the logging could then be its own event listener which a site could remove if they wanted to. I haven't thought through the impact of this myself yet.
Comment #72
cmlaraSpeaking as a voice for contrib that interfaces with authentication.
My reading is that user_login_finalize() is currently public API and that it is intended to be called whenever a module wishes to indicate a user has logged-in and user_logout() is intended to be called whenever we wish to logout a user no matter how they were logged into the system.
Both of these global functions currently perform double duty, where they trigger actions for contrib to act upon and they setup/tear down User module internal information (setting the user in the session).
I recently mistakenly thought user_login_finalize() was only used for session based auth and that all others would provide their own authentication provider event listener, however in TFA we recently encountered a bug (due to our increasing security) that forced me to realize the user_login_finalize() method is called by modules that perform external authentication, ref #3426189: 2.x-dev incompatible with other modules calling user_login_finalize().
This raises the question what is the future vision for this new service? Is it intended to only be used by the user module and the rest of us should invoking the hooks ourselves or is it intended to still be called by 3rd party contrib to 'log in' a user?
If its intended to be used by the user module only than I would suggest the deprecation should be changed to indicate these methods have been deprecated with no direct replacment, the user should instead call the necessary hooks/events themselves. The service may not even be necessary if the actions can be done in-line.
If its intended to be used by contrib than I believe we need to either put this on an interface or at a minimal make the class non-final to allow it to be mocked in unit tests. If the class stays final with no interface a helper trait (that provisions it in a dummy mode) for unit tests would be helpful to avoid boilerplate code in contrib. Addtionaly it would likely be helpful if the User module specific items (setting up a session with the UID set) are moved outside of the service and instead done where elsewhere.
From a TFA maintainer standpoint:
Having this as a service that could be decorated would allow a long term cleaner fix to #3426189: 2.x-dev incompatible with other modules calling user_login_finalize() and allow us to be involved in more locations to provide enhanced security.
Having this as internal we could likely provide our own authentication provider and cleanup some of our code. It would probably make intercepting the login form more complicated however I doubt it would be a blocker.
In either case I do agree that user_login_finalize() and user_logout() should be be removed from procedural code.
Tagging as related to #2901795: Facilitate 2FA+MultiFactor compatibility (2FA/two-factor -> MFA/multi-factor) as user_login_finalize() and replacements impact the MFA ecosystem.
Comment #73
solideogloria commentedGlad to see there's an issue for this. I was going to open an issue if there wasn't one already.
Comment #74
smustgrave commentedOnly moving to NW for the open threads from @catch on the MR.
Comment #75
solideogloria commented#144538: User logout is vulnerable to CSRF was committed, which added a new
UserLogoutConfirmform callinguser_logout().The MR will need to be rebased and then the form will need to be updated to use the new service.
Comment #76
donquixote commentedI think I would prefer to see two distinct services.
Comment #78
znerol commentedAnybody up for a reroll of this one? I might have some ideas on how to continue here. I do not intend to touch the MR just yet, maybe I can keep my role as a reviewer a little bit longer here.
Comment #79
kim.pepperI had a go at rebasing on 11.x which was what it was originally back in March 2024.
Comment #80
avpadernoChanges are first done in the main branch; the merge request should be for the main branch, not the 11.x one.
Comment #81
kristiaanvandeneyndeMoving the code 1:1 is fine by me, but @catch raises some valid points regarding dependencies. We should split up the code in 2 services so we don't load dependencies on login or logout that we don't need.
I don't think we need to wrap the logger dependency in a closure per se, but rather the whole service wherever it's injected. Then we can keep the logging for now to keep this a 1:1 conversion and decide whether we want to make callers responsible at a later time.
Comment #82
znerol commentedThese functions are reminders of the time when the
usermodule was the only source of user accounts, the login form was the only way to login into a Drupal site and TFA was only used by a small circle of bankers and top secret government agencies.The world has changed since then, and Drupal core needs to adapt. Not in this MR, but let's prepare for it.
The responsibility of the service introduced in this issue is to safely change the privilege level of a user agent after a credential check. In most cases the credential check is a login form, sometimes a password reset. The web installer just grants the current user agent access to user 1. Outside core it could also be social login via OIDC or some other kind of SSO maybe combined with TFA, etc.
Regardless of how the credential check was performed, the important thing is to renew the session id and tell the authentication provider that a user account is associated with this session.
Subsequent requests are checked by the Cookie authentication provider until the user agent is logged out.
I'd like this issue to prepare for two things in the future, both have minimal influence for this MR:
Both of those points could be achieved with the following function (rough sectch):
For the first iteration, I just like to suggest to add the
$loginMechanismparameter. It is useful on its own even if it only appears in the log message for now. The$loginDataparameter could be added later on - its optional anyway.Also I'd suggest to place all of the code into one class in the
\Drupal\Core\Sessionnamespace. Accepting the smell for now that updating the timestamp on the user entity isn't the responsibility of core.For the class name I suggest
UserAgentSession. That would result in the following methods:\Drupal\Core\Session\UserAgentSession::login($account, $mechanism)\Drupal\Core\Session\UserAgentSession::logout()For the mechanism parameter there should be consistent naming pattern. I'd suggest to namespace it using what is known as the
providerfrom plugins.\Drupal::service(UserAgentSession::class)->login($account, 'core.install');RegisterForm:$this->userAgentSession->login($account, 'user.register_form')UserAuthenticationController$this->userAgentSession->login($account, 'user.http_login')UserController$this->userAgentSession->login($account, 'user.password_reset')UserLoginForm$this->userAgentSession->login($account, 'user.login_form')I agree that this requires quite some dependencies at this stage. The following dependencies will go away over time:
SessionManageras part of #3570824: Deprecate SessionManager::destroy() and rely on PHP lazy sessionsEntityTypeManagerwhen the timestamp update code is extractedAlso I'd like to suggest to pass in the
Requestinstead of injecting the request stack. A$requestobject is readily available at every call site.Finally I'd like to make it really clear that I'm not proposing to rework everything here, but just to move the procedural code to a future proof location and with one (
$loginMechanism) or two if you like ($request) additional parameter(s).Comment #83
heddnPer question in slack, I'm +1 on currently keeping these things in user.module. Long term is long term. This is a very tightly scoped issue and shouldn't be moving things around too much
Comment #84
znerol commentedSo, the plan is to ignore #82 for the moment but address the MR discussions. I'll stick around for reviews.
Comment #85
znerol commentedI switched the target branch of the MR to
main. Please start with a rebase.Comment #86
kim.pepperRebased on main. Haven't made any other changes yet.
Comment #89
nicxvan commentedThis needs a rebase and versions updated.
Comment #90
kim.pepperRebased and updated versions in the deprecation messages.
Leaving at NW as there are a lot of other comments that haven't been addressed yet.
Comment #91
kim.pepperGot the tests passing at least :-D
Comment #92
kim.pepperAs per the feedback, I split out the Login and Logout finalizers into their own services.
Bumping to needs review as I think we should re-address the comment about using service closures.
Comment #93
kim.pepperSplit out the tests too.
Comment #94
kim.pepperUpdated the IS and CR
Comment #95
kim.pepperComment #96
berdirDidn't read the whole issue or MR yet, just jumping in to ask whether it was discussed if we need the interfaces or not. More and more, we're not adding interfaces for new services when we don't see a use case where someone would want to exchange it with a different implementation. See the recent locale refactoring issues for example, or the field purger.
Comment #97
kim.pepperNo, it wasn't discussed, I just added them. I wasn't aware we were not adding interfaces anymore. 🤔
Comment #98
kim.pepperRemoved the interfaces.
Comment #99
kim.pepperThanks for reviewing.
Outstanding feedback with open questions/comments:
Comment #100
kim.pepperThese all seem fairly trivial to me, with a high chance of bike-shedding. My preferences would be to get this 13 year old issue in and punt anything like where we should be doing logging to follow ups.
Comment #101
znerol commentedRe #99
This is addressed in #3570824: Deprecate SessionManager::destroy() and rely on PHP lazy sessions.