Value of fe_typo_user cookie vs ses_id in fe_sessions table

Issue #7 resolved
Michael Stopp created an issue

In the documentation for \nn\t3::FrontendUserAuthentication()->loginBySessionId() you say:

Die Session-ID entspricht dem TYPO3 Cookie fe_typo_user. In der Regel gibt es für jede Fe-User-Session einen Eintrag in der Tabelle fe_sessions. Bis zu Typo3 v11 entsprach die Spalte ses_id exakt dem Cookie-Wert. Ab Typo3 v11 wird der Wert zusätzlich gehashed.

I cannot reproduce this in a v10 installation. When I search for the session id as returned by $GLOBALS['TSFE']->fe_user->user['ses_id'] in the fe_sessions table, I will find a matching record. But when I compare this with the value of the fe_typo_user cookie, it doesn’t match. (I also compared the hashed version for v11, but this produces a different value as well.)

What’s wrong here? (BTW, I’m talking about an anonymous fe_user session, i.e. no logged in user.)

Comments (6)

  1. David Bascom repo owner

    Thanks for reporting! We‘ll be checking this coming week!

    One question: What was your usecase exactly? Were you trying to switch between anonymous user sessions using their Session-IDs from the fe_typo_user-cookie?

  2. Michael Stopp reporter

    We have an extension that handles a multi-step registration process, so we need FE sessions to track users and their progress. Now we have a (legitimate) scenario, where the extension should be running in an iframe on a foreign site (different domain). This causes problems with lost sessions, even if you set [FE][cookieSameSite] to “none” in the global settings of T3. On Safari + Safari mobile it only seems to work, if you change some additional settings in the privacy settings of the browser. This isn’t really an option in real life.

    Whenever a user progresses to the next step, data on a form is submitted to the server with a POST request. So my idea was: if I add the ses_id in a hidden field on our form, I could write a middleware that looks for our own ses_id value in the POST data and adds it to the request header (only if the fe_typo_user cookie isn’t there), as if the client had sent the fe_typo_user cookie.

    When I couldn’t find out, how the value of the fe_typo_user cookie was generated in the core or in what way it was related to the ses_id of the FE user session, I stumbled upon your extension (Google is your friend…) and the information in your documentation quoted above. I must admit, this isn’t really an issue with your extension, I’m just puzzled, why this seems to work in your extension (I assume it does…), when it doesn’t correspond at all with my experiences.

    So; sorry, if I’m wasting your time, but it just seemed to me that you were dealing with a similar problem, which I couldn’t get solved so far. Maybe you know something that I don’t 😉 ? Thanks anyway. And BTW: your extension looks really interesting, but I hadn’t yet found time to test it.

  3. David Bascom repo owner

    Yes – indeed. We have been struggeling with similar problems due to the new privacy policies from Chrome and Safari which (fortunately) don’t allow cross-domain cookies anymore.

    With our EXT:nnrestapi we switched to JWT (Json Web Tokens) to authenticate a frontend-user. It was the same topic: CORS and Cross-Domain-Cookie problems. The basic idea can be outlined like this:

    In ext_localconf.php we add an hook for the postUserLookUp during the standard TYPO3 authentication process:

    // Hook in `/sysext/core/Classes/Authentication/AbstractUserAuthentication.php` to auth the frontend-user
    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postUserLookUp']['nnrestapi'] = \Nng\Nnrestapi\Hooks\FrontendUserAuthenticationHook::class . '->postUserLookUp';
    

    The hook does a couple of things, but the main idea is: It reads a session-ID from a custom Authentication-Header and get’s the frontend-user associated to the session.

    To put it in simple words: You could be setting your own cookie, pass a session-ID with your form-data – or (what is a little more elegant) send an AUTH-Header via PHP with your request. The hook needs to modify the $params['pObj']->user and set the fe_user-data row. Something like this:

    class FrontendUserAuthenticationHook {
    
      public function postUserLookUp( &$params, &$parent = null ) {
    
        // some logic here to validate the user and get the fe_user-row from the DB
        if (checkSessionOrHeaderOrSomething()) {
    
          // some logic here, to get the right user ;)
          $user = getTheFeUserRow();
    
          // pass the user back so TYPO3 can do the rest
          $params['pObj']->user = $user;
        }
      }
    
    }
    

    Hope that helps!

  4. Michael Stopp reporter

    Hey, many thanks for this detailed answer! I’ll look into this, looks really promising.

    Just for context: the motivation behind my approach with a middleware (obviously run before typo3/cms-frontend/authentication), which inserts the missing cookie, was to have a solution that requires minimal change to the existing application. Inserting a hidden field with the current session id is about 3 lines of code. And with the middleware I was planning, everything would be back to normal, as if the cookie had always been there.

    But I hope the same can be achieved with your hint and the postUserLookUp hook, just in a different way. I’ll let you know, if this can be used for anonymous FE sessions as well. Thanks again!

  5. David Bascom repo owner

    Ok – we found it and fixed it in the dev branch. Will be in the next release (coming week).

    The way TYPO3 hashes the sessionID in v10 was md5 not sha256 like in v11 – you can compare the differences for the TYPO3 versions in \Nng\Nnhelpers\Utilities\Encrypt->hashSessionId()

    Here is how to generate the fe_sessions.ses_id stored in the database from the session-ID stored in the fe_typo_user-cookie. If you’re implementing this without nnhelpers, don't forget this will break when you update to v11. You will need to replace md5 with sha256:

    $key = sha1($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . 'core-session-backend');
    return hash_hmac('md5', $sessionIdFromCookie, $key);
    

    So the basic usage (when using nnhelpers) would be:

    $sessionId = $_POST['your_post_var'] ?? \nn\t3::FrontendUser()->getSessionId();
    if ($sessionId) {
      \nn\t3::FrontendUserAuthentication()->loginBySessionId( $sessionId );
    }
    

    Have fun, save time.

    Thought on the side: If you are using anonymous sessions, things could get a lot more complicated – depending on your settings, TYPO3 might change the session-ID of the cookie with every page reload.

  6. Log in to comment