diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1848,6 +1848,8 @@
     'PhabricatorAuthSSHPublicKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php',
     'PhabricatorAuthSession' => 'applications/auth/storage/PhabricatorAuthSession.php',
     'PhabricatorAuthSessionEngine' => 'applications/auth/engine/PhabricatorAuthSessionEngine.php',
+    'PhabricatorAuthSessionEngineExtension' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtension.php',
+    'PhabricatorAuthSessionEngineExtensionModule' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php',
     'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
     'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
     'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
@@ -6206,6 +6208,8 @@
       'PhabricatorPolicyInterface',
     ),
     'PhabricatorAuthSessionEngine' => 'Phobject',
+    'PhabricatorAuthSessionEngineExtension' => 'Phobject',
+    'PhabricatorAuthSessionEngineExtensionModule' => 'PhabricatorConfigModule',
     'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
     'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
diff --git a/src/applications/auth/controller/PhabricatorLogoutController.php b/src/applications/auth/controller/PhabricatorLogoutController.php
--- a/src/applications/auth/controller/PhabricatorLogoutController.php
+++ b/src/applications/auth/controller/PhabricatorLogoutController.php
@@ -29,13 +29,6 @@
     $viewer = $this->getViewer();
 
     if ($request->isFormPost()) {
-
-      $log = PhabricatorUserLog::initializeNewLog(
-        $viewer,
-        $viewer->getPHID(),
-        PhabricatorUserLog::ACTION_LOGOUT);
-      $log->save();
-
       // Destroy the user's session in the database so logout works even if
       // their cookies have some issues. We'll detect cookie issues when they
       // try to login again and tell them to clear any junk.
@@ -45,8 +38,10 @@
           ->setViewer($viewer)
           ->withSessionKeys(array($phsid))
           ->executeOne();
+
         if ($session) {
-          $session->delete();
+          $engine = new PhabricatorAuthSessionEngine();
+          $engine->logoutSession($viewer, $session);
         }
       }
       $request->clearCookie(PhabricatorCookies::COOKIE_SESSION);
diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php
--- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php
+++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php
@@ -297,6 +297,24 @@
     }
   }
 
+  public function logoutSession(
+    PhabricatorUser $user,
+    PhabricatorAuthSession $session) {
+
+    $log = PhabricatorUserLog::initializeNewLog(
+      $user,
+      $user->getPHID(),
+      PhabricatorUserLog::ACTION_LOGOUT);
+    $log->save();
+
+    $extensions = PhabricatorAuthSessionEngineExtension::getAllExtensions();
+    foreach ($extensions as $extension) {
+      $extension->didLogout($user, array($session));
+    }
+
+    $session->delete();
+  }
+
 
 /* -(  High Security  )------------------------------------------------------ */
 
diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngineExtension.php b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtension.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtension.php
@@ -0,0 +1,23 @@
+<?php
+
+abstract class PhabricatorAuthSessionEngineExtension
+  extends Phobject {
+
+  final public function getExtensionKey() {
+    return $this->getPhobjectClassConstant('EXTENSIONKEY');
+  }
+
+  final public static function getAllExtensions() {
+    return id(new PhutilClassMapQuery())
+      ->setAncestorClass(__CLASS__)
+      ->setUniqueMethod('getExtensionKey')
+      ->execute();
+  }
+
+  abstract public function getExtensionName();
+
+  public function didLogout(PhabricatorUser $user, array $sessions) {
+    return;
+  }
+
+}
diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php
@@ -0,0 +1,49 @@
+<?php
+
+final class PhabricatorAuthSessionEngineExtensionModule
+  extends PhabricatorConfigModule {
+
+  public function getModuleKey() {
+    return 'sessionengine';
+  }
+
+  public function getModuleName() {
+    return pht('Engine: Session');
+  }
+
+  public function renderModuleStatus(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+
+    $extensions = PhabricatorAuthSessionEngineExtension::getAllExtensions();
+
+    $rows = array();
+    foreach ($extensions as $extension) {
+      $rows[] = array(
+        get_class($extension),
+        $extension->getExtensionKey(),
+        $extension->getExtensionName(),
+      );
+    }
+
+    $table = id(new AphrontTableView($rows))
+      ->setNoDataString(
+        pht('There are no registered session engine extensions.'))
+      ->setHeaders(
+        array(
+          pht('Class'),
+          pht('Key'),
+          pht('Name'),
+        ))
+      ->setColumnClasses(
+        array(
+          null,
+          null,
+          'wide pri',
+        ));
+
+    return id(new PHUIObjectBoxView())
+      ->setHeaderText(pht('SessionEngine Extensions'))
+      ->setTable($table);
+  }
+
+}
diff --git a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php
--- a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php
@@ -201,4 +201,9 @@
     return true;
   }
 
+  public function getPhabricatorURI() {
+    $config = $this->getProviderConfig();
+    return $config->getProperty(self::PROPERTY_PHABRICATOR_URI);
+  }
+
 }
diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php
--- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php
+++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php
@@ -62,19 +62,12 @@
     return $this;
   }
 
+  public function newResultObject() {
+    return new PhabricatorExternalAccount();
+  }
+
   protected function loadPage() {
-    $table = new PhabricatorExternalAccount();
-    $conn_r = $table->establishConnection('r');
-
-    $data = queryfx_all(
-      $conn_r,
-      'SELECT * FROM %T %Q %Q %Q',
-      $table->getTableName(),
-      $this->buildWhereClause($conn_r),
-      $this->buildOrderClause($conn_r),
-      $this->buildLimitClause($conn_r));
-
-    return $table->loadAllFromArray($data);
+    return $this->loadStandardPage($this->newResultObject());
   }
 
   protected function willFilterPage(array $accounts) {
@@ -116,61 +109,59 @@
     return $accounts;
   }
 
-  protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
-    $where = array();
-
-    $where[] = $this->buildPagingClause($conn_r);
+  protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+    $where = parent::buildWhereClauseParts($conn);
 
-    if ($this->ids) {
+    if ($this->ids !== null) {
       $where[] = qsprintf(
-        $conn_r,
+        $conn,
         'id IN (%Ld)',
         $this->ids);
     }
 
-    if ($this->phids) {
+    if ($this->phids !== null) {
       $where[] = qsprintf(
-        $conn_r,
+        $conn,
         'phid IN (%Ls)',
         $this->phids);
     }
 
-    if ($this->accountTypes) {
+    if ($this->accountTypes !== null) {
       $where[] = qsprintf(
-        $conn_r,
+        $conn,
         'accountType IN (%Ls)',
         $this->accountTypes);
     }
 
-    if ($this->accountDomains) {
+    if ($this->accountDomains !== null) {
       $where[] = qsprintf(
-        $conn_r,
+        $conn,
         'accountDomain IN (%Ls)',
         $this->accountDomains);
     }
 
-    if ($this->accountIDs) {
+    if ($this->accountIDs !== null) {
       $where[] = qsprintf(
-        $conn_r,
+        $conn,
         'accountID IN (%Ls)',
         $this->accountIDs);
     }
 
-    if ($this->userPHIDs) {
+    if ($this->userPHIDs !== null) {
       $where[] = qsprintf(
-        $conn_r,
+        $conn,
         'userPHID IN (%Ls)',
         $this->userPHIDs);
     }
 
-    if ($this->accountSecrets) {
+    if ($this->accountSecrets !== null) {
       $where[] = qsprintf(
-        $conn_r,
+        $conn,
         'accountSecret IN (%Ls)',
         $this->accountSecrets);
     }
 
-    return $this->formatWhereClause($where);
+    return $where;
   }
 
   public function getQueryApplicationClass() {
diff --git a/src/applications/people/storage/PhabricatorExternalAccount.php b/src/applications/people/storage/PhabricatorExternalAccount.php
--- a/src/applications/people/storage/PhabricatorExternalAccount.php
+++ b/src/applications/people/storage/PhabricatorExternalAccount.php
@@ -54,15 +54,13 @@
         'accountURI' => 'text255?',
       ),
       self::CONFIG_KEY_SCHEMA => array(
-        'key_phid' => null,
-        'phid' => array(
-          'columns' => array('phid'),
-          'unique' => true,
-        ),
         'account_details' => array(
           'columns' => array('accountType', 'accountDomain', 'accountID'),
           'unique' => true,
         ),
+        'key_user' => array(
+          'columns' => array('userPHID'),
+        ),
       ),
     ) + parent::getConfiguration();
   }