Changeset View
Changeset View
Standalone View
Standalone View
src/auth/PhutilGoogleAuthAdapter.php
| <?php | <?php | ||||
| /** | /** | ||||
| * Authentication adapter for Google OAuth2. | * Authentication adapter for Google Open ID Connect. | ||||
| */ | */ | ||||
| final class PhutilGoogleAuthAdapter extends PhutilOAuthAuthAdapter { | final class PhutilGoogleAuthAdapter extends PhutilOpenIDConnectAuthAdapter { | ||||
| public function getAdapterType() { | public function getAdapterType() { | ||||
| return 'google'; | return 'google'; | ||||
| } | } | ||||
| public function getAdapterDomain() { | public function getAdapterDomain() { | ||||
| return 'google.com'; | return 'google.com'; | ||||
| } | } | ||||
| public function getAccountID() { | public function getAccountID() { | ||||
| $emails = $this->getOAuthAccountData('emails', array()); | $email = $this->getOAuthAccountData('email', null); | ||||
| foreach ($emails as $email) { | |||||
| if (idx($email, 'type') == 'account') { | if ($email != null) { | ||||
| return idx($email, 'value'); | return $email; | ||||
| } | |||||
| } | } | ||||
| throw new Exception( | throw new Exception( | ||||
| pht( | pht( | ||||
| 'Expected to retrieve an "account" email from Google Plus API call '. | 'Expected to retrieve an "account" email from Google API call '. | ||||
| 'to identify account, but failed.')); | 'to identify account, but failed.')); | ||||
| } | } | ||||
| public function getAccountEmail() { | public function getAccountEmail() { | ||||
| return $this->getAccountID(); | return $this->getAccountID(); | ||||
| } | } | ||||
| public function getAccountName() { | public function getAccountName() { | ||||
| // Guess account name from email address, this is just a hint anyway. | // Guess account name from email address, this is just a hint anyway. | ||||
| $email = $this->getAccountEmail(); | $email = $this->getAccountEmail(); | ||||
| $email = explode('@', $email); | $email = explode('@', $email); | ||||
| $email = head($email); | $email = head($email); | ||||
| return $email; | return $email; | ||||
| } | } | ||||
| public function getAccountImageURI() { | public function getAccountImageURI() { | ||||
| $image = $this->getOAuthAccountData('image', array()); | $image_uri = $this->getOAuthAccountData('picture', null); | ||||
| $uri = idx($image, 'url'); | |||||
| // Change the "sz" parameter ("size") from the default to 100 to ask for | // Change the "sz" parameter ("size") from the default to 100 to ask for | ||||
| // a 100x100px image. | // a 100x100px image. | ||||
| if ($uri !== null) { | if ($image_uri !== null) { | ||||
| $uri = new PhutilURI($uri); | $image_uri = new PhutilURI($image_uri); | ||||
| $uri->setQueryParam('sz', 100); | $image_uri->setQueryParam('sz', 100); | ||||
| $uri = (string)$uri; | $image_uri = (string)$image_uri; | ||||
| } | } | ||||
| return $uri; | return $image_uri; | ||||
| } | } | ||||
| public function getAccountURI() { | public function getAccountURI() { | ||||
| return $this->getOAuthAccountData('url'); | return $this->getOAuthAccountData('url'); | ||||
| } | } | ||||
| public function getAccountRealName() { | public function getAccountRealName() { | ||||
| $name = $this->getOAuthAccountData('name', array()); | $name = $this->getOAuthAccountData('name', array()); | ||||
| // TODO: This could probably be made cleaner by looking up the API, but | // TODO: This could probably be made cleaner by looking up the API, but | ||||
| // this should work to unbreak logins. | // this should work to unbreak logins. | ||||
| $parts = array(); | $parts = array(); | ||||
| $parts[] = idx($name, 'givenName'); | $parts[] = $this->getOAuthAccountData('given_name', array()); | ||||
| unset($name['givenName']); | $parts[] = $this->getOAuthAccountData('family_name', array()); | ||||
epriestley: This looks wrong ("famiily" is misspelled). | |||||
| $parts[] = idx($name, 'familyName'); | |||||
| unset($name['familyName']); | |||||
| $parts = array_merge($parts, $name); | $parts = array_merge($parts, $name); | ||||
| $parts = array_filter($parts); | $parts = array_filter($parts); | ||||
| return implode(' ', $parts); | return implode(' ', $parts); | ||||
| } | } | ||||
| protected function getAuthenticateBaseURI() { | protected function getAuthenticateBaseURI() { | ||||
| return 'https://accounts.google.com/o/oauth2/auth'; | return 'https://accounts.google.com/o/oauth2/auth'; | ||||
| Show All 20 Lines | final class PhutilGoogleAuthAdapter extends PhutilOpenIDConnectAuthAdapter { | ||||
| public function getExtraTokenParameters() { | public function getExtraTokenParameters() { | ||||
| return array( | return array( | ||||
| 'grant_type' => 'authorization_code', | 'grant_type' => 'authorization_code', | ||||
| ); | ); | ||||
| } | } | ||||
| protected function loadOAuthAccountData() { | protected function loadOAuthAccountData() { | ||||
| $uri = new PhutilURI('https://www.googleapis.com/plus/v1/people/me'); | $uri = new PhutilURI('https://oauth2.googleapis.com/tokeninfo'); | ||||
| $uri->setQueryParam('access_token', $this->getAccessToken()); | $uri->setQueryParam('id_token', $this->getIdToken()); | ||||
| $future = new HTTPSFuture($uri); | $future = new HTTPSFuture($uri); | ||||
| list($status, $body) = $future->resolve(); | list($status, $body) = $future->resolve(); | ||||
| if ($status->isError()) { | if ($status->isError()) { | ||||
| $this->tryToThrowSpecializedError($status, $body); | $this->tryToThrowSpecializedError($status, $body); | ||||
| throw $status; | throw $status; | ||||
| } | } | ||||
| Show All 26 Lines | private function tryToThrowSpecializedError($status, $raw_body) { | ||||
| } | } | ||||
| $error = $body['error']['errors'][0]; | $error = $body['error']['errors'][0]; | ||||
| $domain = idx($error, 'domain'); | $domain = idx($error, 'domain'); | ||||
| $reason = idx($error, 'reason'); | $reason = idx($error, 'reason'); | ||||
| if ($domain == 'usageLimits' && $reason == 'accessNotConfigured') { | if ($domain == 'usageLimits' && $reason == 'accessNotConfigured') { | ||||
| throw new PhutilAuthConfigurationException( | throw new PhutilAuthConfigurationException( | ||||
| pht( | pht( | ||||
| 'Google returned an "%s" error. This usually means you need to '. | 'Google returned an "%s" error.'. | ||||
| 'enable the "Google+ API" in your Google Cloud Console, under '. | 'You can try checking the configuration on %s.'. | ||||
| '"APIs".'. | 'The Application ID this install is using is "%s".'. | ||||
| "\n\n". | |||||
| 'Around March 2014, Google made some API changes which require this '. | |||||
| 'configuration adjustment.'. | |||||
| "\n\n". | |||||
| 'Normally, you can resolve this issue by going to %s, then '. | |||||
| 'clicking "API Project", then "APIs & auth", then turning the '. | |||||
| '"Google+ API" on. The names you see on the console may be '. | |||||
| 'different depending on how your integration is set up. If you '. | |||||
| 'are not sure, you can hunt through the projects until you find '. | |||||
| 'the one associated with the right Application ID under '. | |||||
| '"Credentials". The Application ID this install is using is "%s".'. | |||||
| "\n\n". | "\n\n". | ||||
| '(If you are unable to log into Phabricator, you can use '. | '(If you are unable to log into Phabricator, you can use '. | ||||
| '"%s" to recover access to an administrator account.)'. | '"%s" to recover access to an administrator account.)'. | ||||
| "\n\n". | "\n\n". | ||||
| 'Full HTTP Response'. | 'Full HTTP Response'. | ||||
| "\n\n%s", | "\n\n%s", | ||||
| 'accessNotConfigured', | 'accessNotConfigured', | ||||
| 'https://console.developers.google.com/', | 'https://console.developers.google.com/', | ||||
| $this->getClientID(), | $this->getClientID(), | ||||
| 'bin/auth recover', | 'bin/auth recover', | ||||
| $raw_body)); | $raw_body)); | ||||
Not Done Inline ActionsThis looks wrong, since the message is adjusted but the parameters are not, so the %s conversions won't line up. epriestley: This looks wrong, since the message is adjusted but the parameters are not, so the `%s`… | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
This looks wrong ("famiily" is misspelled).