    def _xsrf_token_id(self):
        """Value to be signed/encrypted for xsrf token

        include login info in xsrf token
        this means xsrf tokens are tied to logged-in users,
        and change after a user logs in.

        While the user is not yet logged in,
        an anonymous value is used, to prevent portability.
        These anonymous values are short-lived.
        """
        # cases:
        # 1. logged in, session id (session_id:user_id)
        # 2. logged in, no session id (anonymous_id:user_id)
        # 3. not logged in, session id (session_id:anonymous_id)
        # 4. no cookies at all, use single anonymous value (:anonymous_id)
        session_id = self.get_session_cookie()
        if self.current_user:
            if isinstance(self.current_user, User):
                user_id = self.current_user.cookie_id
            else:
                # this shouldn't happen, but may if e.g. a Service attempts to fetch a page,
                # which usually won't work, but this method should not be what raises
                user_id = ""
            if not session_id:
                # no session id, use non-portable anonymous id
                session_id = _anonymous_xsrf_id(self)
        else:
            # not logged in yet, use non-portable anonymous id
            user_id = _anonymous_xsrf_id(self)
        xsrf_id = f"{session_id}:{user_id}".encode("utf8", "replace")
        return xsrf_id
Value to be signed/encrypted for xsrf token

include login info in xsrf token
this means xsrf tokens are tied to logged-in users,
and change after a user logs in.

While the user is not yet logged in,
an anonymous value is used, to prevent portability.
These anonymous values are short-lived.
jupyterhub/handlers/base.py