Problem/Motivation
Despite past efforts to convert the session as well as the user authentication subsystems to OO code, the current status of both is still insufficient for release. This is likely due to both subsystems being worked on in separate efforts. However, for historical reasons sessions are hard-wired to user authentication and vice versa. This issue provides the big picture and a roadmap outlining the steps necessary to separate both subsystems and finalize the API for session and user authentication.
The most prominent sign of the current defects in both components is the presence of the global $user:
git grep -l -i 'global.*user'
Authentication/AuthenticationManager.php
Authentication/Provider/Cookie.php
Routing/Enhancer/AuthenticationEnhancer.php
Session/AccountInterface.php
Session/SessionHandler.php
Session/SessionManager.php
Session/SessionManagerInterface.php
Session/UserSession.php
Step 1: Remove the dependency of AccountProxy on AuthenticationManager
The AccountProxy currently triggers authentication automatically whenever one of the methods of the current_user service is accessed. As a result it has a dependency on the AuthenticationManager which is responsible for checking credentials on an incoming request and based on that setting the current user. This circular dependency is hidden because the AuthenticationManager acts on the global $user.
Thus in order to remove the global $user from AuthenticationManager, it is necessary to first remove automatic authentication from AccountProxy.
Issues which need to be resolved:
- #2286971: Remove dependency of current_user on request and authentication manager (also removes
AuthenticationEnhancer
This also will fix:
User interface changes: None
API changes: Reverse dependency between AuthenticationManager and AccountProxy.
Expected impact on core and contrib code: Minor
Step 2: Register symfony session components in the DIC and inject the session service into the request object
Can and should be resolved in parallel to Step 1.
In Symfony based applications, the session is an object on the request. However, in Drupal services wishing to control the session currently do so by invoking methods on SessionManager. This is appropriate as a temporary solution. However, in order to maintain interoperability the final API should conform to what Symfony components expect.
NB: Whether the session will still be accessible from the DIC or only from the request in Symfony 3.0 is currently under discussion, (see [Symfony 3.0][RFC] The session must not be a service #10557).
- #2229145: Register symfony session components in the DIC and inject the session service into the request object
- #2372389: Expose session handler in container
User interface changes: None
API changes: Access and control the session by $request->getSession() (API Addition).
Expected impact on core and contrib code: Minor
Step 3: Remove user authentication related code from the session component (and remove global $user)
Based on the previous steps, user authentication can be separated completely from sessions by assigning the responsibility for loading and setting the current user to the authentication provider.
#2228393: Decouple session from cookie based user authentication
User interface changes: None
API changes: None
Expected impact on core and contrib code: Minor
Step 4: Audit session and user authentication components
Steps 1-3 block:
Comments
Comment #1
znerol commentedComment #2
znerol commentedComment #3
znerol commentedComment #4
catchAt least some of this is critical, would be great to figure out exactly what and divide up the issue summary.
Comment #5
catchComment #6
catchComment #7
yesct commentedComment #8
xjmComment #9
xjmComment #10
effulgentsia commentedComment #11
almaudoh commentedComment #12
almaudoh commentedComment #13
almaudoh commentedComment #14
skipyT commentedComment #15
almaudoh commented#2338727: Replace static SessionManager::$enabled property with WriteSafeSessionHandler class and resolve hidden circular dependency between SessionManager and SessionHandler and #2229145: Register symfony session components in the DIC and inject the session service into the request object are in, #2372389: Expose session handler in container is RTBC. Making progress...
Comment #16
berdirNote: I removed almost all remaining calls to global $user in #2328645: Remove remaining global $user. I believe some of the issues here will make parts of it prettier, and I kept one part (the interaction between SessionHandler and Cookie), but we could already remove all other global $user's, if that helps.
Comment #17
berdirWhat is left here? Do we really still need this issue?
#2286971: Remove dependency of current_user on request and authentication manager is RTBC, #2283637: Provide test coverage to prove that an AuthenticationProvider can initiate a session is critical on it's own and hopefully a test-only issue after the other one is in. Global user is gone (yay yay yay) and #2286591: [meta] Security audit the Authentication component is also critical on it's own as well.
Edit: OK, #2228393: Decouple session from cookie based user authentication is still left and #2345611: Load user entity in Cookie AuthenticationProvider instead of using manual queries but I'm not sure if they really are part of the critical-ness of this meta.
Maybe we can demote this to major and keep it to keep track of the various related issues, but all the parts that are critical already are on their own.
Comment #18
znerol commentedThere is still quite a bit of problematic interaction between the session subsystem and the current user.
Comment #19
cpj commentedI agree with @znerol #18. Now is not the time to demote this to major. Lets first wait and see how some of these related issues turn out and then evaluate what still needs to be done.
Comment #20
xjm(Updating certain "Needs D8 critical triage" issues to a less misleading tag name.)
Comment #21
znerol commentedStep 1 and 2 are more or less completed. The goal is to be able to start the security audit after devdays2015.
The most sever problem we have right now is that the session subsystem still assumes that it is the only authority when it comes to user authentication. This leads to the following issues:
This is because
SessionHandler::write()uses theuidof the currently logged in user even if it was not authenticated by the cookie authentication method.Same reason as above.
Both of those issues are resolved by #2228393: Decouple session from cookie based user authentication. A test is being worked on in the second of the above issues. The following issues are likely duplicates: #1277682: Move responsibility for global $user out of session, #2272189: Uncouple session handling from user module,
#2283637: Provide test coverage to prove that an AuthenticationProvider can initiate a session deals with the session not being usable when using authentication providers in addition with cookie auth. This is supposed to be a test-only issue after #2286971: Remove dependency of current_user on request and authentication manager landed.
#2437761: CSRF token seed and possibly other session data lost when set after a session regenerate contains a fix and a test. Should be ready soon.
To be continued...
Comment #22
znerol commentedLooking through #2286971: Remove dependency of current_user on request and authentication manager turned quite a few non-critical follow-ups which need some work:
None of them seems related to the open critical ones mentioned in the previous comment.
Edit: the only open follow-up from step 2 is #2424451: Session manager (and others) should not initialize database connection if no query needs to be run.
Comment #23
znerol commentedComment #24
znerol commentedThe critical part of step 3 is done, follow-ups are:
The following are test-only patches (and probably could be merged?):
Comment #25
almaudoh commented#2432585: Improve authentication manager service construction to support custom global service providers would also be good DX for contrib to allow for easy modification of list of default authentication providers.
Comment #26
xjmComment #27
znerol commentedComment #28
webchickTagging, since next steps here are unclear.
Comment #29
pfrenssenMy understanding was that we have to finish the 5 remaining child issues to bring this to conclusion.
Comment #30
dawehner@pfrenssen are any of those particularly critical? There are some, like the moving of AccountInterface, I can't be critical as least for me.
Do you mind adding those which are left onto the issue summary directly?
Comment #31
webchickAgreed; remaining child issues are:
There's definitely still value in keeping this issue open as a tracking issue for the work required to finalize this API. However, thanks to several peoples' very hard work (yay!) this is no longer release-blocking to Drupal 8. Downgrading as a result.
Obviously, if release-blocking things come up as you're completing the above list, feel free to make additional critical issue for them.
Comment #32
pfrenssenYes indeed all the important stuff is done, now we just need to do the finishing touches.
Comment #33
znerol commentedFiled #2488116: Document the session subsystem, we probably should do the same for the authentication subsystem?
Comment #34
joelpittetAdded this child to remove the duplicate function of drupal_set_message() which likely still fails.
#2554261: Fix docs on test without injected drupal_set_message() AggregatorPluginSettingsBaseTest
Comment #35
wim leersPer #2603046-23: Support anonymous users, another thing that is missing in the current APIs, is cacheability metadata.
We want to make sure that not all code needs to be aware of what mechanism is used to handle sessions. I.e. whether that uses a cookie, some kind of header, or something else still.
\Drupal\Core\Session\SessionConfigurationInterfaceactually is meant to encapsulate that. And it does. But it's unfortunately unaware of cache contexts, because that was overlooked. The default implementation,\Drupal\Core\Session\SessionConfigurationuses cookies.So, what is missing is:
And
Comment #36
wim leersBecause of #35.
Comment #39
andypostComment #40
dawehner@Wim Leers
Issue or it simply never happens :)
Comment #41
wim leers#40: good point!
Created #2826138: Authentication system lacks cacheability metadata as a child issue of this.
Comment #50
mxr576Is this still a "finalize" or rather a "rebuild"?
I bumped into #3187037: Lost Symfony features cased by missing parent calls in SessionManager this week and then I started to follow the trail of various session related issues and there are plenty of those (some of them already referenced here). I saw with my own eyes what kind of workarounds needs to be done in those modules that try to extend Drupal >= 8's authentication system. Drupal 9 is out, but as of today, there is still no stable TFA/MFA solution for Drupal >=8 and I doubt that TOTP would be even possible with the current API.
The problem is that you need stable SAML/OIDC/TFA/MFA/TOTP solutions if you are working with enterprise customers, these are required in certain industries.
Last week I saw a presentation (related blogpost) about how much Symfony's Security component improved in 5.2 and a primary side-effect of the rewritten API is that custom authenticators (like 2fa) can work without nasty workarounds. (And the original API was not that bad it was based on the Java Spring framework.)
So I think my main question is, should we try to maintain and improve our own Authentication API or we should rather move closer to what upstream does and leverage what it can provide to us?
(And I did not mention those Drupal core issues which are about how we should call "visitors" and "logged-in" users instead of "anonymous" and "authenticated", I think those are also slightly related to this topic.)
Comment #51
cpj commentedHi @mxr576 - the age of some of these session related issues speaks to the difficulty of resolving them. I have watched the evolution of the Symfony security subsystem, and have used most of the different variants since Symfony 2.1. I have also worked with Drupal's authentication systems for a number of years.
The PHP session system is old and the underlying C libraries are poorly understood. This has lead to unnecessary breaking changes in PHP over the years and there remains a catalog of unresolved issues.
The original Symfony security system was overly complicated and also poorly understood. Ryan Weaver has done great work in recent years to improve this, first with Guard and then with the more recent core library improvements. There are still some areas for improvement in Symfony security but the current situation is acceptable.
My opinion is that there are no simple answers here. My preference would be to see Drupal to use more of the Symfony Security libraries and to avoid custom solutions. Problems will still arise but may be easier and faster to resolve if the session & authentication layers are standardised.
Comment #52
mxr576That would be mine too.
Thanks for sharing how things were evolved in the past years, I was not sure if adopting the Security component was considered in the early days of Drupal 8 or not. I checked the issue queue but I did not find anything useful.
Of course, switching to the Security component is would be a long-shot plan. I have just wanted to know if that is on the table or not, what are others thinking about the raised problems, what others see as possible short-term and long-term solutions for resolving them?
Comment #53
moshe weitzman commentedHi all. User maintainer here. I'm open to the Symfony Security component. I'm not too familiar with it. I think we need some clarity around where it will be used and how core and contrib code would change. Thats probably worth a new issue, that you should link from here.
Comment #54
andypostMaybe better to file issue to core ideas queue to get more visibility to review of SF security components?
OTOH it could use #2286591: [meta] Security audit the Authentication component
Comment #55
colanSo, maybe we should postpone that one on this one (getting the Symfony stuff in first)? Or just drop it if we trust Symfony.
Comment #56
berdirMoving closer Symfony has always been the goal I think, after a lot of initial work towards that, progress has been slow the last few years. It's hard to progress with our BC policy here.
That said, #2238561: Use the default PHP session ID instead of generating a custom one just got in, which I think is a pretty major step towards having less custom Drupal things and more Symfony/standard PHP and was blocking various other issues AFAIK.
However, while my knowledge about it is limited, I think the Security component covers much more than just session/auth management? Not sure if that's in scope for this issue.
Comment #57
cpj commentedHi @Berdir,
I agree that the release of #2238561 is a big step forward, which should make it easier to progress many of the related / blocked issues.
The Symfony Security component is complicated and does cover more than the scope of this issue, but perhaps it is possible to use just those parts that are needed here ?
Comment #58
colanRight. We don't have to use all of it, but let's use what we can. If other things in there are useful too, let's worry about those things in other issues.
Comment #59
andypostAs session bags introduced one issue could be closed #2865991: provide an API to forcibly start a session
Comment #61
andypostCommited #2473875: Convert uses of $_SESSION to symfony session retrieved from the request
Comment #65
andypostComment #67
znerol commentedComment #68
znerol commentedComment #69
andypostBig change finally commited #2484991: Add the session to the request in KernelTestBase, BrowserTestBase, and drush
Comment #70
andypostPHP gonna stop support GET/POST sessions https://wiki.php.net/rfc/deprecate-get-post-sessions
Comment #71
catchComment #72
andypostLast global session conversion is done #3109970: Convert uses of $_SESSION in forms and batch with follow-up #3413153: Remove calls to Request::hasSession()
Comment #73
bradjones1Re: #70, the deprecation is only for specifying the session in a query or request parameter. So the vast majority (if not, dare I say, all) Drupal installs are likely unaffected by that change.
Comment #74
andypostPHP 8.4 beta1 is out and lots (3k) of core tests fail because of #3465836: PHP 8.4 session.sid_length and session.sid_bits_per_character are deprecated
Comment #75
andypostPassing SID via URL is deprecated in PHP 8.4 #3470075: PHP 8.4 GET/POST sessions are deprecated