diff --git a/src/auth/PhutilOAuthAuthAdapter.php b/src/auth/PhutilOAuthAuthAdapter.php index 439f800..1e87247 100644 --- a/src/auth/PhutilOAuthAuthAdapter.php +++ b/src/auth/PhutilOAuthAuthAdapter.php @@ -1,229 +1,228 @@ getAuthenticateBaseURI()); - $uri->replaceQueryParam('client_id', $this->getClientID()); - $uri->replaceQueryParam('scope', $this->getScope()); - $uri->replaceQueryParam('redirect_uri', $this->getRedirectURI()); - $uri->replaceQueryParam('state', $this->getState()); - - foreach ($this->getExtraAuthenticateParameters() as $key => $value) { - $uri->replaceQueryParam($key, $value); - } + $params = array( + 'client_id' => $this->getClientID(), + 'scope' => $this->getScope(), + 'redirect_uri' => $this->getRedirectURI(), + 'state' => $this->getState(), + ) + $this->getExtraAuthenticateParameters(); + + $uri = new PhutilURI($this->getAuthenticateBaseURI(), $params); - return (string)$uri; + return phtuil_string_cast($uri); } public function getAdapterType() { $this_class = get_class($this); $type_name = str_replace('PhutilAuthAdapterOAuth', '', $this_class); return strtolower($type_name); } public function setState($state) { $this->state = $state; return $this; } public function getState() { return $this->state; } public function setCode($code) { $this->code = $code; return $this; } public function getCode() { return $this->code; } public function setRedirectURI($redirect_uri) { $this->redirectURI = $redirect_uri; return $this; } public function getRedirectURI() { return $this->redirectURI; } public function getExtraAuthenticateParameters() { return array(); } public function getExtraTokenParameters() { return array(); } public function getExtraRefreshParameters() { return array(); } public function setScope($scope) { $this->scope = $scope; return $this; } public function getScope() { return $this->scope; } public function setClientSecret(PhutilOpaqueEnvelope $client_secret) { $this->clientSecret = $client_secret; return $this; } public function getClientSecret() { return $this->clientSecret; } public function setClientID($client_id) { $this->clientID = $client_id; return $this; } public function getClientID() { return $this->clientID; } public function getAccessToken() { return $this->getAccessTokenData('access_token'); } public function getAccessTokenExpires() { return $this->getAccessTokenData('expires_epoch'); } public function getRefreshToken() { return $this->getAccessTokenData('refresh_token'); } protected function getAccessTokenData($key, $default = null) { if ($this->accessTokenData === null) { $this->accessTokenData = $this->loadAccessTokenData(); } return idx($this->accessTokenData, $key, $default); } public function supportsTokenRefresh() { return false; } public function refreshAccessToken($refresh_token) { $this->accessTokenData = $this->loadRefreshTokenData($refresh_token); return $this; } protected function loadRefreshTokenData($refresh_token) { $params = array( 'refresh_token' => $refresh_token, ) + $this->getExtraRefreshParameters(); // NOTE: Make sure we return the refresh_token so that subsequent // calls to getRefreshToken() return it; providers normally do not echo // it back for token refresh requests. return $this->makeTokenRequest($params) + array( 'refresh_token' => $refresh_token, ); } protected function loadAccessTokenData() { $code = $this->getCode(); if (!$code) { throw new PhutilInvalidStateException('setCode'); } $params = array( 'code' => $this->getCode(), ) + $this->getExtraTokenParameters(); return $this->makeTokenRequest($params); } private function makeTokenRequest(array $params) { $uri = $this->getTokenBaseURI(); $query_data = array( 'client_id' => $this->getClientID(), 'client_secret' => $this->getClientSecret()->openEnvelope(), 'redirect_uri' => $this->getRedirectURI(), ) + $params; $future = new HTTPSFuture($uri, $query_data); $future->setMethod('POST'); list($body) = $future->resolvex(); $data = $this->readAccessTokenResponse($body); if (isset($data['expires_in'])) { $data['expires_epoch'] = $data['expires_in']; } else if (isset($data['expires'])) { $data['expires_epoch'] = $data['expires']; } // If we got some "expires" value back, interpret it as an epoch timestamp // if it's after the year 2010 and as a relative number of seconds // otherwise. if (isset($data['expires_epoch'])) { if ($data['expires_epoch'] < (60 * 60 * 24 * 365 * 40)) { $data['expires_epoch'] += time(); } } if (isset($data['error'])) { throw new Exception(pht('Access token error: %s', $data['error'])); } return $data; } protected function readAccessTokenResponse($body) { // NOTE: Most providers either return JSON or HTTP query strings, so try // both mechanisms. If your provider does something else, override this // method. $data = json_decode($body, true); if (!is_array($data)) { $data = array(); parse_str($body, $data); } if (empty($data['access_token']) && empty($data['error'])) { throw new Exception( pht('Failed to decode OAuth access token response: %s', $body)); } return $data; } protected function getOAuthAccountData($key, $default = null) { if ($this->oauthAccountData === null) { $this->oauthAccountData = $this->loadOAuthAccountData(); } return idx($this->oauthAccountData, $key, $default); } }