diff --git a/src/applications/auth/provider/PhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorAuthProvider.php
index faa878d4a8..e73cda8e36 100644
--- a/src/applications/auth/provider/PhabricatorAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorAuthProvider.php
@@ -1,323 +1,341 @@
 <?php
 
 abstract class PhabricatorAuthProvider {
 
   private $providerConfig;
 
   public function attachProviderConfig(PhabricatorAuthProviderConfig $config) {
     $this->providerConfig = $config;
     return $this;
   }
 
   public function hasProviderConfig() {
     return (bool)$this->providerConfig;
   }
 
   public function getProviderConfig() {
     if ($this->providerConfig === null) {
       throw new Exception(
         "Call attachProviderConfig() before getProviderConfig()!");
     }
     return $this->providerConfig;
   }
 
   public function getConfigurationHelp() {
     return null;
   }
 
   public function getDefaultProviderConfig() {
     return id(new PhabricatorAuthProviderConfig())
       ->setProviderClass(get_class($this))
       ->setIsEnabled(1)
       ->setShouldAllowLogin(1)
       ->setShouldAllowRegistration(1)
       ->setShouldAllowLink(1)
       ->setShouldAllowUnlink(1);
   }
 
   public function getNameForCreate() {
     return $this->getProviderName();
   }
 
   public function getDescriptionForCreate() {
     return null;
   }
 
   public function getProviderKey() {
     return $this->getAdapter()->getAdapterKey();
   }
 
   public function getProviderType() {
     return $this->getAdapter()->getAdapterType();
   }
 
   public function getProviderDomain() {
     return $this->getAdapter()->getAdapterDomain();
   }
 
   public static function getAllBaseProviders() {
     static $providers;
 
     if ($providers === null) {
       $objects = id(new PhutilSymbolLoader())
         ->setAncestorClass(__CLASS__)
         ->loadObjects();
       $providers = $objects;
     }
 
     return $providers;
   }
 
   public static function getAllProviders() {
     static $providers;
 
     if ($providers === null) {
       $objects = self::getAllBaseProviders();
 
       $configs = id(new PhabricatorAuthProviderConfigQuery())
         ->setViewer(PhabricatorUser::getOmnipotentUser())
         ->execute();
 
       $providers = array();
       foreach ($configs as $config) {
         if (!isset($objects[$config->getProviderClass()])) {
           // This configuration is for a provider which is not installed.
           continue;
         }
 
         $object = clone $objects[$config->getProviderClass()];
         $object->attachProviderConfig($config);
 
         $key = $object->getProviderKey();
         if (isset($providers[$key])) {
           throw new Exception(
             pht(
               "Two authentication providers use the same provider key ".
               "('%s'). Each provider must be identified by a unique ".
               "key.",
               $key));
         }
         $providers[$key] = $object;
       }
     }
 
     return $providers;
   }
 
   public static function getAllEnabledProviders() {
     $providers = self::getAllProviders();
     foreach ($providers as $key => $provider) {
       if (!$provider->isEnabled()) {
         unset($providers[$key]);
       }
     }
     return $providers;
   }
 
   public static function getEnabledProviderByKey($provider_key) {
     return idx(self::getAllEnabledProviders(), $provider_key);
   }
 
   abstract public function getProviderName();
   abstract public function getAdapter();
 
   public function isEnabled() {
     return $this->getProviderConfig()->getIsEnabled();
   }
 
   public function shouldAllowLogin() {
     return $this->getProviderConfig()->getShouldAllowLogin();
   }
 
   public function shouldAllowRegistration() {
     return $this->getProviderConfig()->getShouldAllowRegistration();
   }
 
   public function shouldAllowAccountLink() {
     return $this->getProviderConfig()->getShouldAllowLink();
   }
 
   public function shouldAllowAccountUnlink() {
     return $this->getProviderConfig()->getShouldAllowUnlink();
   }
 
   public function buildLoginForm(
     PhabricatorAuthStartController $controller) {
     return $this->renderLoginForm($controller->getRequest(), $mode = 'start');
   }
 
   abstract public function processLoginRequest(
     PhabricatorAuthLoginController $controller);
 
   public function buildLinkForm(
     PhabricatorAuthLinkController $controller) {
     return $this->renderLoginForm($controller->getRequest(), $mode = 'link');
   }
 
   protected function renderLoginForm(
     AphrontRequest $request,
     $mode) {
     throw new Exception("Not implemented!");
   }
 
   public function createProviders() {
     return array($this);
   }
 
   protected function willSaveAccount(PhabricatorExternalAccount $account) {
     return;
   }
 
   public function willRegisterAccount(PhabricatorExternalAccount $account) {
     return;
   }
 
   protected function loadOrCreateAccount($account_id) {
     if (!strlen($account_id)) {
       throw new Exception(
         "loadOrCreateAccount(...): empty account ID!");
     }
 
     $adapter = $this->getAdapter();
     $adapter_class = get_class($adapter);
 
     if (!strlen($adapter->getAdapterType())) {
       throw new Exception(
         "AuthAdapter (of class '{$adapter_class}') has an invalid ".
         "implementation: no adapter type.");
     }
 
     if (!strlen($adapter->getAdapterDomain())) {
       throw new Exception(
         "AuthAdapter (of class '{$adapter_class}') has an invalid ".
         "implementation: no adapter domain.");
     }
 
     $account = id(new PhabricatorExternalAccount())->loadOneWhere(
       'accountType = %s AND accountDomain = %s AND accountID = %s',
       $adapter->getAdapterType(),
       $adapter->getAdapterDomain(),
       $account_id);
     if (!$account) {
       $account = id(new PhabricatorExternalAccount())
         ->setAccountType($adapter->getAdapterType())
         ->setAccountDomain($adapter->getAdapterDomain())
         ->setAccountID($account_id);
     }
 
     $account->setUsername($adapter->getAccountName());
     $account->setRealName($adapter->getAccountRealName());
     $account->setEmail($adapter->getAccountEmail());
     $account->setAccountURI($adapter->getAccountURI());
 
     $account->setProfileImagePHID(null);
     $image_uri = $adapter->getAccountImageURI();
     if ($image_uri) {
       try {
         $name = PhabricatorSlug::normalize($this->getProviderName());
         $name = $name.'-profile.jpg';
 
         // TODO: If the image has not changed, we do not need to make a new
         // file entry for it, but there's no convenient way to do this with
         // PhabricatorFile right now. The storage will get shared, so the impact
         // here is negligible.
         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
           $image_file = PhabricatorFile::newFromFileDownload(
             $image_uri,
             array(
               'name' => $name,
             ));
         unset($unguarded);
 
         if ($image_file) {
           $account->setProfileImagePHID($image_file->getPHID());
         }
       } catch (Exception $ex) {
         // Log this but proceed, it's not especially important that we
         // be able to pull profile images.
         phlog($ex);
       }
     }
 
     $this->willSaveAccount($account);
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
       $account->save();
     unset($unguarded);
 
     return $account;
   }
 
   public function getLoginURI() {
     $app = PhabricatorApplication::getByClass('PhabricatorApplicationAuth');
     $uri = $app->getApplicationURI('/login/'.$this->getProviderKey().'/');
     return PhabricatorEnv::getURI($uri);
   }
 
   public function getSettingsURI() {
     return '/settings/panel/external/';
   }
 
   public function getStartURI() {
     $app = PhabricatorApplication::getByClass('PhabricatorApplicationAuth');
     $uri = $app->getApplicationURI('/start/');
     return $uri;
   }
 
   public function isDefaultRegistrationProvider() {
     return false;
   }
 
   public function shouldRequireRegistrationPassword() {
     return false;
   }
 
   public function getDefaultExternalAccount() {
     throw new Exception("Not implemented!");
   }
 
   public function getLoginOrder() {
     return '500-'.$this->getProviderName();
   }
 
   protected function getLoginIcon() {
     return 'Generic';
   }
 
   public function isLoginFormAButton() {
     return false;
   }
 
   public function renderConfigPropertyTransactionTitle(
     PhabricatorAuthProviderConfigTransaction $xaction) {
 
     return null;
   }
 
   public function readFormValuesFromProvider() {
     return array();
   }
 
   public function readFormValuesFromRequest(AphrontRequest $request) {
     return array();
   }
 
   public function processEditForm(
     AphrontRequest $request,
     array $values) {
 
     $errors = array();
     $issues = array();
 
     return array($errors, $issues, $values);
   }
 
   public function extendEditForm(
     AphrontRequest $request,
     AphrontFormView $form,
     array $values,
     array $issues) {
     return;
   }
 
+  public function willRenderLinkedAccount(
+    PhabricatorUser $viewer,
+    PhabricatorObjectItemView $item,
+    PhabricatorExternalAccount $account) {
+
+    $account_view = id(new PhabricatorAuthAccountView())
+      ->setExternalAccount($account)
+      ->setAuthProvider($this);
+
+    $item->appendChild(
+      phutil_tag(
+        'div',
+        array(
+          'class' => 'mmr mml mst mmb',
+        ),
+        $account_view));
+  }
+
 }
diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
index 16b45d53cb..97c2687edb 100644
--- a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
+++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
@@ -1,346 +1,376 @@
 <?php
 
 abstract class PhabricatorAuthProviderOAuth extends PhabricatorAuthProvider {
 
   protected $adapter;
 
   abstract protected function newOAuthAdapter();
 
   public function getDescriptionForCreate() {
     return pht('Configure %s OAuth.', $this->getProviderName());
   }
 
   public function getAdapter() {
     if (!$this->adapter) {
       $adapter = $this->newOAuthAdapter();
       $this->adapter = $adapter;
       $this->configureAdapter($adapter);
     }
     return $this->adapter;
   }
 
   protected function configureAdapter(PhutilAuthAdapterOAuth $adapter) {
     $config = $this->getProviderConfig();
     $adapter->setClientID($config->getProperty(self::PROPERTY_APP_ID));
     $adapter->setClientSecret(
       new PhutilOpaqueEnvelope(
         $config->getProperty(self::PROPERTY_APP_SECRET)));
     $adapter->setRedirectURI($this->getLoginURI());
     return $adapter;
   }
 
   public function isLoginFormAButton() {
     return true;
   }
 
   protected function renderLoginForm(AphrontRequest $request, $mode) {
     $viewer = $request->getUser();
 
     if ($mode == 'link') {
       $button_text = pht('Link External Account');
     } else if ($this->shouldAllowRegistration()) {
       $button_text = pht('Login or Register');
     } else {
       $button_text = pht('Login');
     }
 
     $icon = id(new PHUIIconView())
       ->setSpriteSheet(PHUIIconView::SPRITE_LOGIN)
       ->setSpriteIcon($this->getLoginIcon());
 
     $button = id(new PHUIButtonView())
         ->setSize(PHUIButtonView::BIG)
         ->setColor(PHUIButtonView::GREY)
         ->setIcon($icon)
         ->setText($button_text)
         ->setSubtext($this->getProviderName());
 
     $adapter = $this->getAdapter();
     $adapter->setState(PhabricatorHash::digest($request->getCookie('phcid')));
 
     $uri = new PhutilURI($adapter->getAuthenticateURI());
     $params = $uri->getQueryParams();
     $uri->setQueryParams(array());
 
     $content = array($button);
 
     foreach ($params as $key => $value) {
       $content[] = phutil_tag(
         'input',
         array(
           'type' => 'hidden',
           'name' => $key,
           'value' => $value,
         ));
     }
 
     return phabricator_form(
       $viewer,
       array(
         'method' => 'GET',
         'action' => (string)$uri,
       ),
       $content);
   }
   public function processLoginRequest(
     PhabricatorAuthLoginController $controller) {
 
     $request = $controller->getRequest();
     $adapter = $this->getAdapter();
     $account = null;
     $response = null;
 
     $error = $request->getStr('error');
     if ($error) {
       $response = $controller->buildProviderErrorResponse(
         $this,
         pht(
           'The OAuth provider returned an error: %s',
           $error));
 
       return array($account, $response);
     }
 
     $code = $request->getStr('code');
     if (!strlen($code)) {
       $response = $controller->buildProviderErrorResponse(
         $this,
         pht(
           'The OAuth provider did not return a "code" parameter in its '.
           'response.'));
 
       return array($account, $response);
     }
 
     if ($adapter->supportsStateParameter()) {
       $phcid = $request->getCookie('phcid');
       if (!strlen($phcid)) {
         $response = $controller->buildProviderErrorResponse(
           $this,
           pht(
             'Your browser did not submit a "phcid" cookie with OAuth state '.
             'information in the request. Check that cookies are enabled. '.
             'If this problem persists, you may need to clear your cookies.'));
       }
 
       $state = $request->getStr('state');
       $expect = PhabricatorHash::digest($phcid);
       if ($state !== $expect) {
         $response = $controller->buildProviderErrorResponse(
           $this,
           pht(
             'The OAuth provider did not return the correct "state" parameter '.
             'in its response. If this problem persists, you may need to clear '.
             'your cookies.'));
       }
     }
 
     $adapter->setCode($code);
 
     // NOTE: As a side effect, this will cause the OAuth adapter to request
     // an access token.
 
     try {
       $account_id = $adapter->getAccountID();
     } catch (Exception $ex) {
       // TODO: Handle this in a more user-friendly way.
       throw $ex;
     }
 
     if (!strlen($account_id)) {
       $response = $controller->buildProviderErrorResponse(
         $this,
         pht(
           'The OAuth provider failed to retrieve an account ID.'));
 
       return array($account, $response);
     }
 
     return array($this->loadOrCreateAccount($account_id), $response);
   }
 
   const PROPERTY_APP_ID = 'oauth:app:id';
   const PROPERTY_APP_SECRET = 'oauth:app:secret';
 
   public function readFormValuesFromProvider() {
     $config = $this->getProviderConfig();
     $id = $config->getProperty(self::PROPERTY_APP_ID);
     $secret = $config->getProperty(self::PROPERTY_APP_SECRET);
 
     return array(
       self::PROPERTY_APP_ID     => $id,
       self::PROPERTY_APP_SECRET => $secret,
     );
   }
 
   public function readFormValuesFromRequest(AphrontRequest $request) {
     return array(
       self::PROPERTY_APP_ID     => $request->getStr(self::PROPERTY_APP_ID),
       self::PROPERTY_APP_SECRET => $request->getStr(self::PROPERTY_APP_SECRET),
     );
   }
 
   public function processEditForm(
     AphrontRequest $request,
     array $values) {
     $errors = array();
     $issues = array();
 
     $key_id = self::PROPERTY_APP_ID;
     $key_secret = self::PROPERTY_APP_SECRET;
 
     if (!strlen($values[$key_id])) {
       $errors[] = pht('Application ID is required.');
       $issues[$key_id] = pht('Required');
     }
 
     if (!strlen($values[$key_secret])) {
       $errors[] = pht('Application secret is required.');
       $issues[$key_secret] = pht('Required');
     }
 
     // If the user has not changed the secret, don't update it (that is,
     // don't cause a bunch of "****" to be written to the database).
     if (preg_match('/^[*]+$/', $values[$key_secret])) {
       unset($values[$key_secret]);
     }
 
     return array($errors, $issues, $values);
   }
 
   public function extendEditForm(
     AphrontRequest $request,
     AphrontFormView $form,
     array $values,
     array $issues) {
 
     $key_id = self::PROPERTY_APP_ID;
     $key_secret = self::PROPERTY_APP_SECRET;
 
     $v_id = $values[$key_id];
     $v_secret = $values[$key_secret];
     if ($v_secret) {
       $v_secret = str_repeat('*', strlen($v_secret));
     }
 
     $e_id = idx($issues, $key_id, $request->isFormPost() ? null : true);
     $e_secret = idx($issues, $key_secret, $request->isFormPost() ? null : true);
 
     $form
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('OAuth App ID'))
           ->setName($key_id)
           ->setValue($v_id)
           ->setError($e_id))
       ->appendChild(
         id(new AphrontFormPasswordControl())
           ->setLabel(pht('OAuth App Secret'))
           ->setName($key_secret)
           ->setValue($v_secret)
           ->setError($e_secret));
   }
 
   public function renderConfigPropertyTransactionTitle(
     PhabricatorAuthProviderConfigTransaction $xaction) {
 
     $author_phid = $xaction->getAuthorPHID();
     $old = $xaction->getOldValue();
     $new = $xaction->getNewValue();
     $key = $xaction->getMetadataValue(
       PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY);
 
     switch ($key) {
       case self::PROPERTY_APP_ID:
         if (strlen($old)) {
           return pht(
             '%s updated the OAuth application ID for this provider from '.
             '"%s" to "%s".',
             $xaction->renderHandleLink($author_phid),
             $old,
             $new);
         } else {
           return pht(
             '%s set the OAuth application ID for this provider to '.
             '"%s".',
             $xaction->renderHandleLink($author_phid),
             $new);
         }
       case self::PROPERTY_APP_SECRET:
         if (strlen($old)) {
           return pht(
             '%s updated the OAuth application secret for this provider.',
             $xaction->renderHandleLink($author_phid));
         } else {
           return pht(
             '%s set the OAuth application seceret for this provider.',
             $xaction->renderHandleLink($author_phid));
         }
     }
 
     return parent::renderConfigPropertyTransactionTitle($xaction);
   }
 
   protected function willSaveAccount(PhabricatorExternalAccount $account) {
     parent::willSaveAccount($account);
     $this->synchronizeOAuthAccount($account);
   }
 
   protected function synchronizeOAuthAccount(
     PhabricatorExternalAccount $account) {
     $adapter = $this->getAdapter();
 
     $oauth_token = $adapter->getAccessToken();
     $account->setProperty('oauth.token.access', $oauth_token);
 
     if ($adapter->supportsTokenRefresh()) {
       $refresh_token = $adapter->getRefreshToken();
       $account->setProperty('oauth.token.refresh', $refresh_token);
     } else {
       $account->setProperty('oauth.token.refresh', null);
     }
 
     $expires = $adapter->getAccessTokenExpires();
     $account->setProperty('oauth.token.access.expires', $expires);
   }
 
   public function getOAuthAccessToken(
     PhabricatorExternalAccount $account,
     $force_refresh = false) {
 
     if ($account->getProviderKey() !== $this->getProviderKey()) {
       throw new Exception("Account does not match provider!");
     }
 
     if (!$force_refresh) {
       $access_expires = $account->getProperty('oauth.token.access.expires');
       $access_token = $account->getProperty('oauth.token.access');
 
       // Don't return a token with fewer than this many seconds remaining until
       // it expires.
       $shortest_token = 60;
 
       if ($access_token) {
         if ($access_expires > (time() + $shortest_token)) {
           return $access_token;
         }
       }
     }
 
     $refresh_token = $account->getProperty('oauth.token.refresh');
     if ($refresh_token) {
       $adapter = $this->getAdapter();
       if ($adapter->supportsTokenRefresh()) {
         $adapter->refreshAccessToken($refresh_token);
 
         $this->synchronizeOAuthAccount($account);
         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
           $account->save();
         unset($unguarded);
       }
     }
 
     return null;
   }
 
+  public function willRenderLinkedAccount(
+    PhabricatorUser $viewer,
+    PhabricatorObjectItemView $item,
+    PhabricatorExternalAccount $account) {
+
+    // Get a valid token, possibly refreshing it.
+    $oauth_token = $this->getOAuthAccessToken($account);
+
+    $item->addAttribute(pht('OAuth2 Account'));
+
+    if ($oauth_token) {
+      $oauth_expires = $account->getProperty('oauth.token.access.expires');
+      if ($oauth_expires) {
+        $item->addAttribute(
+          pht(
+            'Active OAuth Token (Expires: %s)',
+            phabricator_datetime($oauth_expires, $viewer)));
+      } else {
+        $item->addAttribute(
+          pht(
+            'Active OAuth Token'));
+      }
+    } else {
+      $item->addAttribute(pht('No OAuth Access Token'));
+    }
+
+    parent::willRenderLinkedAccount($viewer, $item, $account);
+  }
+
+
 }
diff --git a/src/applications/auth/provider/PhabricatorAuthProviderPassword.php b/src/applications/auth/provider/PhabricatorAuthProviderPassword.php
index 0ed88fd599..e5a40752b0 100644
--- a/src/applications/auth/provider/PhabricatorAuthProviderPassword.php
+++ b/src/applications/auth/provider/PhabricatorAuthProviderPassword.php
@@ -1,242 +1,249 @@
 <?php
 
 final class PhabricatorAuthProviderPassword
   extends PhabricatorAuthProvider {
 
   private $adapter;
 
   public function getProviderName() {
     return pht('Username/Password');
   }
 
   public function getConfigurationHelp() {
     return pht(
       'You can select a minimum password length by setting '.
       '`account.minimum-password-length` in configuration.');
   }
 
   public function getDescriptionForCreate() {
     return pht(
       'Allow users to login or register using a username and password.');
   }
 
   public function getAdapter() {
     if (!$this->adapter) {
       $adapter = new PhutilAuthAdapterEmpty();
       $adapter->setAdapterType('password');
       $adapter->setAdapterDomain('self');
       $this->adapter = $adapter;
     }
     return $this->adapter;
   }
 
   public function getLoginOrder() {
     // Make sure username/password appears first if it is enabled.
     return '100-'.$this->getProviderName();
   }
 
   public function shouldAllowAccountLink() {
     return false;
   }
 
   public function shouldAllowAccountUnlink() {
     return false;
   }
 
   public function isDefaultRegistrationProvider() {
     return true;
   }
 
   public function buildLoginForm(
     PhabricatorAuthStartController $controller) {
     $request = $controller->getRequest();
     return $this->renderPasswordLoginForm($request);
   }
 
   public function buildLinkForm(
     PhabricatorAuthLinkController $controller) {
     throw new Exception("Password providers can't be linked.");
   }
 
   private function renderPasswordLoginForm(
     AphrontRequest $request,
     $require_captcha = false,
     $captcha_valid = false) {
 
     $viewer = $request->getUser();
 
     $dialog = id(new AphrontDialogView())
       ->setSubmitURI($this->getLoginURI())
       ->setUser($viewer)
       ->setTitle(pht('Login to Phabricator'))
       ->addSubmitButton(pht('Login'));
 
     if ($this->shouldAllowRegistration()) {
       $dialog->addCancelButton(
         '/auth/register/',
         pht('Register New Account'));
     }
 
     $dialog->addFooter(
       phutil_tag(
         'a',
         array(
           'href' => '/login/email/',
         ),
         pht('Forgot your password?')));
 
     $v_user = nonempty(
       $request->getStr('username'),
       $request->getCookie('phusr'));
 
     $e_user = null;
     $e_pass = null;
     $e_captcha = null;
 
     $errors = array();
     if ($require_captcha && !$captcha_valid) {
       if (AphrontFormRecaptchaControl::hasCaptchaResponse($request)) {
         $e_captcha = pht('Invalid');
         $errors[] = pht('CAPTCHA was not entered correctly.');
       } else {
         $e_captcha = pht('Required');
         $errors[] = pht('Too many login failures recently. You must '.
                     'submit a CAPTCHA with your login request.');
       }
     } else if ($request->isHTTPPost()) {
       // NOTE: This is intentionally vague so as not to disclose whether a
       // given username or email is registered.
       $e_user = pht('Invalid');
       $e_pass = pht('Invalid');
       $errors[] = pht('Username or password are incorrect.');
     }
 
     if ($errors) {
       $errors = id(new AphrontErrorView())->setErrors($errors);
     }
 
     $form = id(new AphrontFormLayoutView())
       ->setFullWidth(true)
       ->appendChild($errors)
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel('Username or Email')
           ->setName('username')
           ->setValue($v_user)
           ->setError($e_user))
       ->appendChild(
         id(new AphrontFormPasswordControl())
           ->setLabel('Password')
           ->setName('password')
           ->setError($e_pass));
 
     if ($require_captcha) {
         $form->appendChild(
           id(new AphrontFormRecaptchaControl())
             ->setError($e_captcha));
     }
 
     $dialog->appendChild($form);
 
     return $dialog;
   }
 
   public function processLoginRequest(
     PhabricatorAuthLoginController $controller) {
 
     $request = $controller->getRequest();
     $viewer = $request->getUser();
 
     $require_captcha = false;
     $captcha_valid = false;
     if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) {
       $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
         PhabricatorUserLog::ACTION_LOGIN_FAILURE,
         60 * 15);
       if (count($failed_attempts) > 5) {
         $require_captcha = true;
         $captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request);
       }
     }
 
     $response = null;
     $account = null;
     $log_user = null;
 
     if (!$require_captcha || $captcha_valid) {
       $username_or_email = $request->getStr('username');
       if (strlen($username_or_email)) {
         $user = id(new PhabricatorUser())->loadOneWhere(
           'username = %s',
           $username_or_email);
 
         if (!$user) {
           $user = PhabricatorUser::loadOneWithEmailAddress($username_or_email);
         }
 
         if ($user) {
           $envelope = new PhutilOpaqueEnvelope($request->getStr('password'));
           if ($user->comparePassword($envelope)) {
             $account = $this->loadOrCreateAccount($user->getPHID());
             $log_user = $user;
           }
         }
       }
     }
 
     if (!$account) {
       $log = PhabricatorUserLog::newLog(
         null,
         $log_user,
         PhabricatorUserLog::ACTION_LOGIN_FAILURE);
       $log->save();
 
       $request->clearCookie('phusr');
       $request->clearCookie('phsid');
 
       $response = $controller->buildProviderPageResponse(
         $this,
         $this->renderPasswordLoginForm(
           $request,
           $require_captcha,
           $captcha_valid));
     }
 
     return array($account, $response);
   }
 
   public function shouldRequireRegistrationPassword() {
     return true;
   }
 
   public function getDefaultExternalAccount() {
     $adapter = $this->getAdapter();
 
     return id(new PhabricatorExternalAccount())
       ->setAccountType($adapter->getAdapterType())
       ->setAccountDomain($adapter->getAdapterDomain());
   }
 
   protected function willSaveAccount(PhabricatorExternalAccount $account) {
     parent::willSaveAccount($account);
     $account->setUserPHID($account->getAccountID());
   }
 
   public function willRegisterAccount(PhabricatorExternalAccount $account) {
     parent::willRegisterAccount($account);
     $account->setAccountID($account->getUserPHID());
   }
 
   public static function getPasswordProvider() {
     $providers = self::getAllEnabledProviders();
 
     foreach ($providers as $provider) {
       if ($provider instanceof PhabricatorAuthProviderPassword) {
         return $provider;
       }
     }
 
     return null;
   }
 
+  public function willRenderLinkedAccount(
+    PhabricatorUser $viewer,
+    PhabricatorObjectItemView $item,
+    PhabricatorExternalAccount $account) {
+    return;
+  }
+
 }
diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php b/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php
index 6cf3f42da9..755fb1edd6 100644
--- a/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php
+++ b/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php
@@ -1,140 +1,129 @@
 <?php
 
 final class PhabricatorSettingsPanelExternalAccounts
   extends PhabricatorSettingsPanel {
 
   public function getPanelKey() {
     return 'external';
   }
 
   public function getPanelName() {
     return pht('External Accounts');
   }
 
   public function getPanelGroup() {
     return pht('Authentication');
   }
 
   public function isEnabled() {
     return true;
   }
 
   public function processRequest(AphrontRequest $request) {
     $viewer = $request->getUser();
 
     $providers = PhabricatorAuthProvider::getAllProviders();
 
     $accounts = id(new PhabricatorExternalAccountQuery())
       ->setViewer($viewer)
       ->withUserPHIDs(array($viewer->getPHID()))
       ->needImages(true)
       ->execute();
 
     $linked_head = id(new PhabricatorHeaderView())
       ->setHeader(pht('Linked Accounts and Authentication'));
 
     $linked = id(new PhabricatorObjectItemListView())
       ->setUser($viewer)
       ->setNoDataString(pht('You have no linked accounts.'));
 
     $login_accounts = 0;
     foreach ($accounts as $account) {
       if ($account->isUsableForLogin()) {
         $login_accounts++;
       }
     }
 
     foreach ($accounts as $account) {
       $item = id(new PhabricatorObjectItemView());
 
       $provider = idx($providers, $account->getProviderKey());
       if ($provider) {
         $item->setHeader($provider->getProviderName());
         $can_unlink = $provider->shouldAllowAccountUnlink();
         if (!$can_unlink) {
           $item->addAttribute(pht('Permanently Linked'));
         }
       } else {
         $item->setHeader(
           pht('Unknown Account ("%s")', $account->getProviderKey()));
         $can_unlink = true;
       }
 
       $can_login = $account->isUsableForLogin();
       if (!$can_login) {
         $item->addAttribute(
           pht(
             'Disabled (an administrator has disabled login for this '.
             'account provider).'));
       }
 
       $can_unlink = $can_unlink && (!$can_login || ($login_accounts > 1));
 
       $item->addAction(
         id(new PHUIListItemView())
           ->setIcon('delete')
           ->setWorkflow(true)
           ->setDisabled(!$can_unlink)
           ->setHref('/auth/unlink/'.$account->getProviderKey().'/'));
 
-      $account_view = id(new PhabricatorAuthAccountView())
-        ->setExternalAccount($account);
-
       if ($provider) {
-        $account_view->setAuthProvider($provider);
+        $provider->willRenderLinkedAccount($viewer, $item, $account);
       }
 
-      $item->appendChild(
-        phutil_tag(
-          'div',
-          array(
-            'class' => 'mmr mml mst mmb',
-          ),
-          $account_view));
-
       $linked->addItem($item);
     }
 
     $linkable_head = id(new PhabricatorHeaderView())
       ->setHeader(pht('Add External Account'));
 
     $linkable = id(new PhabricatorObjectItemListView())
       ->setUser($viewer)
       ->setNoDataString(
         pht('Your account is linked with all available providers.'));
 
     $accounts = mpull($accounts, null, 'getProviderKey');
 
     $providers = PhabricatorAuthProvider::getAllEnabledProviders();
     $providers = msort($providers, 'getProviderName');
     foreach ($providers as $key => $provider) {
       if (isset($accounts[$key])) {
         continue;
       }
 
       if (!$provider->shouldAllowAccountLink()) {
         continue;
       }
 
       $link_uri = '/auth/link/'.$provider->getProviderKey().'/';
 
       $item = id(new PhabricatorObjectItemView());
       $item->setHeader($provider->getProviderName());
       $item->setHref($link_uri);
       $item->addAction(
         id(new PHUIListItemView())
           ->setIcon('link')
           ->setHref($link_uri));
 
       $linkable->addItem($item);
     }
 
     return array(
       $linked_head,
       $linked,
       $linkable_head,
       $linkable,
     );
   }
 
 }