Index: src/__celerity_resource_map__.php =================================================================== --- src/__celerity_resource_map__.php +++ src/__celerity_resource_map__.php @@ -1949,6 +1949,20 @@ ), 'disk' => '/rsrc/js/application/owners/owners-path-editor.js', ), + 'javelin-behavior-persona-login' => + array( + 'uri' => '/res/128fdf56/rsrc/js/application/auth/behavior-persona-login.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-resource', + 2 => 'javelin-stratcom', + 3 => 'javelin-workflow', + 4 => 'javelin-util', + ), + 'disk' => '/rsrc/js/application/auth/behavior-persona-login.js', + ), 'javelin-behavior-phabricator-active-nav' => array( 'uri' => '/res/9c8d3df8/rsrc/js/core/behavior-active-nav.js', Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -962,6 +962,7 @@ 'PhabricatorAuthProviderOAuthGoogle' => 'applications/auth/provider/PhabricatorAuthProviderOAuthGoogle.php', 'PhabricatorAuthProviderOAuthTwitch' => 'applications/auth/provider/PhabricatorAuthProviderOAuthTwitch.php', 'PhabricatorAuthProviderPassword' => 'applications/auth/provider/PhabricatorAuthProviderPassword.php', + 'PhabricatorAuthProviderPersona' => 'applications/auth/provider/PhabricatorAuthProviderPersona.php', 'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php', 'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php', 'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php', @@ -3116,6 +3117,7 @@ 'PhabricatorAuthProviderOAuthGoogle' => 'PhabricatorAuthProviderOAuth', 'PhabricatorAuthProviderOAuthTwitch' => 'PhabricatorAuthProviderOAuth', 'PhabricatorAuthProviderPassword' => 'PhabricatorAuthProvider', + 'PhabricatorAuthProviderPersona' => 'PhabricatorAuthProvider', 'PhabricatorAuthRegisterController' => 'PhabricatorAuthController', 'PhabricatorAuthStartController' => 'PhabricatorAuthController', 'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController', Index: src/applications/auth/provider/PhabricatorAuthProvider.php =================================================================== --- src/applications/auth/provider/PhabricatorAuthProvider.php +++ src/applications/auth/provider/PhabricatorAuthProvider.php @@ -384,6 +384,7 @@ array( 'method' => 'optional string', 'uri' => 'string', + 'sigil' => 'optional string', )); $viewer = $request->getUser(); @@ -404,11 +405,11 @@ ->setSpriteIcon($this->getLoginIcon()); $button = id(new PHUIButtonView()) - ->setSize(PHUIButtonView::BIG) - ->setColor(PHUIButtonView::GREY) - ->setIcon($icon) - ->setText($button_text) - ->setSubtext($this->getProviderName()); + ->setSize(PHUIButtonView::BIG) + ->setColor(PHUIButtonView::GREY) + ->setIcon($icon) + ->setText($button_text) + ->setSubtext($this->getProviderName()); $uri = $attributes['uri']; $uri = new PhutilURI($uri); @@ -432,6 +433,7 @@ array( 'method' => idx($attributes, 'method', 'GET'), 'action' => (string)$uri, + 'sigil' => idx($attributes, 'sigil'), ), $content); } Index: src/applications/auth/provider/PhabricatorAuthProviderPersona.php =================================================================== --- /dev/null +++ src/applications/auth/provider/PhabricatorAuthProviderPersona.php @@ -0,0 +1,79 @@ +adapter) { + $adapter = new PhutilAuthAdapterPersona(); + $this->adapter = $adapter; + } + return $this->adapter; + } + + protected function renderLoginForm( + AphrontRequest $request, + $mode) { + + Javelin::initBehavior( + 'persona-login', + array( + 'loginURI' => $this->getLoginURI(), + )); + + return $this->renderStandardLoginButton( + $request, + $mode, + array( + 'uri' => $this->getLoginURI(), + 'sigil' => 'persona-login-form', + )); + } + + public function isLoginFormAButton() { + return true; + } + + public function processLoginRequest( + PhabricatorAuthLoginController $controller) { + + $request = $controller->getRequest(); + $adapter = $this->getAdapter(); + + $account = null; + $response = null; + + if (!$request->isAjax()) { + throw new Exception("Expected this request to come via Ajax."); + } + + $assertion = $request->getStr('assertion'); + if (!$assertion) { + throw new Exception("Expected identity assertion."); + } + + $adapter->setAssertion($assertion); + $adapter->setAudience(PhabricatorEnv::getURI('/')); + + try { + $account_id = $adapter->getAccountID(); + } catch (Exception $ex) { + // TODO: Handle this in a more user-friendly way. + throw $ex; + } + + return array($this->loadOrCreateAccount($account_id), $response); + } + +} Index: webroot/rsrc/js/application/auth/behavior-persona-login.js =================================================================== --- /dev/null +++ webroot/rsrc/js/application/auth/behavior-persona-login.js @@ -0,0 +1,41 @@ +/** + * @provides javelin-behavior-persona-login + * @requires javelin-behavior + * javelin-resource + * javelin-stratcom + * javelin-workflow + * javelin-util + */ + +JX.behavior('persona-login', function(config) { + + JX.Stratcom.listen( + 'submit', + 'persona-login-form', + function(e) { + e.kill(); + navigator.id.request(); + }); + + var onloaded = function() { + // Before installing watch(), log the user out, because we know they don't + // have a valid session if they're hitting this page. If we don't do this, + // Persona may immediately trigger a login event, which prevents the user + // from selecting another authentication mechanism. + navigator.id.logout(); + + navigator.id.watch({ + loggedInUser: null, + onlogin: onlogin, + onlogout: JX.bag + }); + }; + + var onlogin = function(assertion) { + new JX.Workflow(config.loginURI, {assertion: assertion}) + .start(); + }; + + var persona_library = 'https://login.persona.org/include.js'; + JX.Resource.load(persona_library, onloaded); +});