diff --git a/core/core.services.yml b/core/core.services.yml
index 4e42b3e..d49416d 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1129,7 +1129,7 @@ services:
     alias: session_handler.storage
   session_handler.storage:
     class: Drupal\Core\Session\SessionHandler
-    arguments: ['@request_stack', '@database']
+    arguments: ['@request_stack', '@database', '@current_user']
     tags:
       - { name: backend_overridable }
   session_handler.write_check:
diff --git a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
index 7db5d9b..644104f 100644
--- a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
+++ b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
@@ -7,7 +7,12 @@
 
 namespace Drupal\Core\Authentication\Provider;
 
+use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Authentication\AuthenticationProviderInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Session\AnonymousUserSession;
+use Drupal\Core\Session\UserSession;
+use Drupal\Core\Site\Settings;
 use Drupal\Core\Session\SessionConfigurationInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -24,13 +29,23 @@ class Cookie implements AuthenticationProviderInterface {
   protected $sessionConfiguration;
 
   /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
    * Constructs a new cookie authentication provider.
    *
    * @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
    *   The session configuration.
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
    */
-  public function __construct(SessionConfigurationInterface $session_configuration) {
+  public function __construct(SessionConfigurationInterface $session_configuration, Connection $connection) {
     $this->sessionConfiguration = $session_configuration;
+    $this->connection = $connection;
   }
 
   /**
@@ -44,12 +59,48 @@ public function applies(Request $request) {
    * {@inheritdoc}
    */
   public function authenticate(Request $request) {
-    if ($request->getSession()->start()) {
-      // @todo Remove global in https://www.drupal.org/node/2228393
-      global $_session_user;
-      return $_session_user;
-    }
+    if ($session = $request->getSession()) {
+      // Handle the case of first time visitors and clients that don't store
+      // cookies (eg. web crawlers).
+      $cookies = $request->cookies;
+      if (!$cookies->has($session->getName())) {
+        return new AnonymousUserSession();
+      }
+
+      $values = $this->connection->query("SELECT u.*, s.* FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.default_langcode = 1 AND s.sid = :sid", array(
+        ':sid' => Crypt::hashBase64($session->getId()),
+      ))->fetchAssoc();
 
+      // We found the client's session record and they are an authenticated,
+      // active user.
+      if ($values && $values['uid'] > 0 && $values['status'] == 1) {
+        // Add roles element to $user.
+        $rids = $this->connection->query("SELECT ur.roles_target_id as rid FROM {user__roles} ur WHERE ur.entity_id = :uid", array(
+          ':uid' => $values['uid'],
+        ))->fetchCol();
+        $values['roles'] = array_merge(array(DRUPAL_AUTHENTICATED_RID), $rids);
+        $user = new UserSession($values);
+      }
+      elseif ($values) {
+        // The user is anonymous or blocked. Only preserve two fields from the
+        // {sessions} table.
+        $user = new UserSession(array(
+          'session' => $values['session'],
+          'access' => $values['access'],
+        ));
+      }
+      else {
+        // The session has expired.
+        $user = new AnonymousUserSession();
+      }
+      // Likewise, do not update access time more than once per 180 seconds.
+      if ($user->isAuthenticated() && REQUEST_TIME - $user->getLastAccessedTime() > Settings::get('session_write_interval', 180)) {
+        /** @var \Drupal\user\UserStorageInterface $storage */
+        $storage = \Drupal::entityManager()->getStorage('user');
+        $storage->updateLastAccessTimestamp($user, REQUEST_TIME);
+      }
+      return $user;
+    }
     return NULL;
   }
 
diff --git a/core/lib/Drupal/Core/Session/SessionHandler.php b/core/lib/Drupal/Core/Session/SessionHandler.php
index 8d9aa96..05b5887 100644
--- a/core/lib/Drupal/Core/Session/SessionHandler.php
+++ b/core/lib/Drupal/Core/Session/SessionHandler.php
@@ -34,11 +34,11 @@ class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
   protected $connection;
 
   /**
-   * An associative array of obsolete sessions with session id as key, and db-key as value.
+   * The current user account.
    *
-   * @var array
+   * @var \Drupal\Core\Session\AccountInterface
    */
-  protected $obsoleteSessionIds = array();
+  protected $currentUser;
 
   /**
    * Constructs a new SessionHandler instance.
@@ -47,10 +47,13 @@ class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
    *   The request stack.
    * @param \Drupal\Core\Database\Connection $connection
    *   The database connection.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user account.
    */
-  public function __construct(RequestStack $request_stack, Connection $connection) {
+  public function __construct(RequestStack $request_stack, Connection $connection, AccountInterface $current_user) {
     $this->requestStack = $request_stack;
     $this->connection = $connection;
+    $this->currentUser = $current_user;
   }
 
   /**
@@ -64,58 +67,30 @@ public function open($save_path, $name) {
    * {@inheritdoc}
    */
   public function read($sid) {
-    // @todo Remove global in https://www.drupal.org/node/2228393
-    global $_session_user;
-
-    // Handle the case of first time visitors and clients that don't store
-    // cookies (eg. web crawlers).
-    $cookies = $this->requestStack->getCurrentRequest()->cookies;
-    if (empty($sid) || !$cookies->has($this->getName())) {
-      $_session_user = new UserSession();
-      return '';
-    }
+    // Read the session data from the database.
+    $record = $this->connection->select('sessions', 's')
+      ->fields('s')
+      ->condition('sid', Crypt::hashBase64($sid))
+      ->execute()
+      ->fetchAssoc();
 
-    $values = $this->connection->query("SELECT u.*, s.* FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.default_langcode = 1 AND s.sid = :sid", array(
-      ':sid' => Crypt::hashBase64($sid),
-    ))->fetchAssoc();
-
-    // We found the client's session record and they are an authenticated,
-    // active user.
-    if ($values && $values['uid'] > 0 && $values['status'] == 1) {
-      // Add roles element to $user.
-      $rids = $this->connection->query("SELECT ur.roles_target_id as rid FROM {user__roles} ur WHERE ur.entity_id = :uid", array(
-        ':uid' => $values['uid'],
-      ))->fetchCol();
-      $values['roles'] = array_merge(array(AccountInterface::AUTHENTICATED_ROLE), $rids);
-      $_session_user = new UserSession($values);
-    }
-    elseif ($values) {
-      // The user is anonymous or blocked. Only preserve two fields from the
-      // {sessions} table.
-      $_session_user = new UserSession(array(
-        'session' => $values['session'],
-        'access' => $values['access'],
-      ));
+    if (isset($record['session'])) {
+      return $record['session'];
     }
     else {
-      // The session has expired.
-      $_session_user = new UserSession();
+      return '';
     }
-
-    return $_session_user->session;
   }
 
   /**
    * {@inheritdoc}
    */
   public function write($sid, $value) {
-    $user = \Drupal::currentUser();
-
     // The exception handler is not active at this point, so we need to do it
     // manually.
     try {
       $fields = array(
-        'uid' => $user->id(),
+        'uid' => $this->currentUser->id(),
         'hostname' => $this->requestStack->getCurrentRequest()->getClientIP(),
         'session' => $value,
         'timestamp' => REQUEST_TIME,
@@ -124,13 +99,6 @@ public function write($sid, $value) {
         ->keys(array('sid' => Crypt::hashBase64($sid)))
         ->fields($fields)
         ->execute();
-
-      // Likewise, do not update access time more than once per 180 seconds.
-      if ($user->isAuthenticated() && REQUEST_TIME - $user->getLastAccessedTime() > Settings::get('session_write_interval', 180)) {
-        /** @var \Drupal\user\UserStorageInterface $storage */
-        $storage = \Drupal::entityManager()->getStorage('user');
-        $storage->updateLastAccessTimestamp($user, REQUEST_TIME);
-      }
       return TRUE;
     }
     catch (\Exception $exception) {
@@ -156,8 +124,6 @@ public function close() {
    * {@inheritdoc}
    */
   public function destroy($sid) {
-
-
     // Delete session data.
     $this->connection->delete('sessions')
       ->condition('sid', Crypt::hashBase64($sid))
@@ -166,8 +132,12 @@ public function destroy($sid) {
     // Reset $_SESSION and current user to prevent a new session from being
     // started in \Drupal\Core\Session\SessionManager::save().
     $_SESSION = array();
-    \Drupal::currentUser()->setAccount(new AnonymousUserSession());
 
+    // @todo: Manipulating the current user is not the business of the session
+    // handler. This should be moved to the authentication mananger.
+    $this->currentUser->setAccount(new AnonymousUserSession());
+
+    // @todo: cookie management should be moved to Cookie auth manager.
     // Unset the session cookies.
     $this->deleteCookie($this->getName());
 
@@ -194,6 +164,8 @@ public function gc($lifetime) {
    *
    * @param string $name
    *   Name of session cookie to delete.
+   *
+   * @todo: cookie management should be moved to Cookie auth manager.
    */
   protected function deleteCookie($name) {
     $cookies = $this->requestStack->getCurrentRequest()->cookies;
diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/SessionManager.php
index 7c3ac21..2c6f09a 100644
--- a/core/lib/Drupal/Core/Session/SessionManager.php
+++ b/core/lib/Drupal/Core/Session/SessionManager.php
@@ -121,10 +121,6 @@ public function start() {
     }
 
     if (empty($result)) {
-      // @todo Remove global in https://www.drupal.org/node/2228393
-      global $_session_user;
-      $_session_user = new AnonymousUserSession();
-
       // Randomly generate a session identifier for this request. This is
       // necessary because \Drupal\user\SharedTempStoreFactory::get() wants to
       // know the future session ID of a lazily started session in advance.
@@ -187,7 +183,7 @@ public function save() {
       return;
     }
 
-    if ($user->isAnonymous() && $this->isSessionObsolete()) {
+    if (!$user || ($user->isAnonymous() && $this->isSessionObsolete())) {
       // There is no session data to store, destroy the session if it was
       // previously started.
       if ($this->getSaveHandler()->isActive()) {
@@ -211,8 +207,6 @@ public function save() {
    * {@inheritdoc}
    */
   public function regenerate($destroy = FALSE, $lifetime = NULL) {
-    $user = \Drupal::currentUser();
-
     // Nothing to do if we are not allowed to change the session.
     if ($this->isCli()) {
       return;
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index fbc900c..76edefc 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -575,8 +575,6 @@ function template_preprocess_username(&$variables) {
  * @see hook_user_login()
  */
 function user_login_finalize(UserInterface $account) {
-  \Drupal::currentUser()->setAccount($account);
-  \Drupal::logger('user')->notice('Session opened for %name.', array('%name' => $account->getUsername()));
   // Update the user table timestamp noting user has logged in.
   // This is also used to invalidate one-time login links.
   $account->setLastLoginTime(REQUEST_TIME);
@@ -588,7 +586,9 @@ function user_login_finalize(UserInterface $account) {
   // This is called before hook_user_login() in case one of those functions
   // fails or incorrectly does a redirect which would leave the old session
   // in place.
+  \Drupal::currentUser()->setAccount($account);
   \Drupal::service('session')->migrate();
+  \Drupal::logger('user')->notice('Session opened for %name.', array('%name' => $account->getUsername()));
 
   \Drupal::moduleHandler()->invokeAll('user_login', array($account));
 }
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index 4bee869..2d92cdf 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -17,7 +17,7 @@ services:
       - { name: access_check, applies_to: _user_is_logged_in }
   authentication.cookie:
     class: Drupal\Core\Authentication\Provider\Cookie
-    arguments: ['@session_configuration']
+    arguments: ['@session_configuration', '@database']
     tags:
       - { name: authentication_provider, priority: 0 }
   cache_context.user:
