protected function _finish($code, $originalRedirectUri) { // This endpoint requires "Basic" auth. $clientCredentials = $this->appInfo->getKey() . ":" . $this->appInfo->getSecret(); $authHeaderValue = "Basic " . base64_encode($clientCredentials); $response = RequestUtil::doPostWithSpecificAuth($this->clientIdentifier, $authHeaderValue, $this->userLocale, $this->appInfo->getHost()->getApi(), "1/oauth2/token", array("grant_type" => "authorization_code", "code" => $code, "redirect_uri" => $originalRedirectUri)); if ($response->statusCode !== 200) { throw RequestUtil::unexpectedStatus($response); } $parts = RequestUtil::parseResponseJson($response->body); if (!array_key_exists('token_type', $parts) or !is_string($parts['token_type'])) { throw new Exception_BadResponse("Missing \"token_type\" field."); } $tokenType = $parts['token_type']; if (!array_key_exists('access_token', $parts) or !is_string($parts['access_token'])) { throw new Exception_BadResponse("Missing \"access_token\" field."); } $accessToken = $parts['access_token']; if (!array_key_exists('uid', $parts) or !is_string($parts['uid'])) { throw new Exception_BadResponse("Missing \"uid\" string field."); } $userId = $parts['uid']; if ($tokenType !== "Bearer" && $tokenType !== "bearer") { throw new Exception_BadResponse("Unknown \"token_type\"; expecting \"Bearer\", got " . Client::q($tokenType)); } return array($accessToken, $userId); }
/** * Given an existing active OAuth 1 access token, make a Dropbox API call to get a new OAuth 2 * access token that represents the same user and app. * * See <a href="https://www.dropbox.com/developers/core/docs#oa1-from-oa1">/oauth2/token_from_oauth1</a>. * * @param OAuth1AccessToken $oauth1AccessToken * * @return string * The OAuth 2 access token. * * @throws Exception */ function createOAuth2AccessToken($oauth1AccessToken) { OAuth1AccessToken::checkArg("oauth1AccessToken", $oauth1AccessToken); $response = self::doPost($oauth1AccessToken, "1/oauth2/token_from_oauth1"); if ($response->statusCode !== 200) { throw RequestUtil::unexpectedStatus($response); } $parts = RequestUtil::parseResponseJson($response->body); if (!array_key_exists('token_type', $parts) || !is_string($parts['token_type'])) { throw new Exception_BadResponse("Missing \"token_type\" field."); } $tokenType = $parts['token_type']; if (!array_key_exists('access_token', $parts) || !is_string($parts['access_token'])) { throw new Exception_BadResponse("Missing \"access_token\" field."); } $accessToken = $parts['access_token']; if ($tokenType !== "Bearer" && $tokenType !== "bearer") { throw new Exception_BadResponse("Unknown \"token_type\"; expecting \"Bearer\", got " . Client::q($tokenType)); } return $accessToken; }
protected function _finish($code, $originalRedirectUri) { $url = RequestUtil::buildUri($this->appInfo->getHost()->getApi(), "1/oauth2/token"); $params = array("grant_type" => "authorization_code", "code" => $code, "redirect_uri" => $originalRedirectUri, "locale" => $this->userLocale); $curl = RequestUtil::mkCurlWithoutAuth($this->clientIdentifier, $url); // Add Basic auth header. $basic_auth = $this->appInfo->getKey() . ":" . $this->appInfo->getSecret(); $curl->addHeader("Authorization: Basic " . base64_encode($basic_auth)); $curl->set(CURLOPT_POST, true); $curl->set(CURLOPT_POSTFIELDS, RequestUtil::buildPostBody($params)); $curl->set(CURLOPT_RETURNTRANSFER, true); $response = $curl->exec(); if ($response->statusCode !== 200) { throw RequestUtil::unexpectedStatus($response); } $parts = RequestUtil::parseResponseJson($response->body); if (!array_key_exists('token_type', $parts) or !is_string($parts['token_type'])) { throw new Exception_BadResponse("Missing \"token_type\" field."); } $tokenType = $parts['token_type']; if (!array_key_exists('access_token', $parts) or !is_string($parts['access_token'])) { throw new Exception_BadResponse("Missing \"access_token\" field."); } $accessToken = $parts['access_token']; if (!array_key_exists('uid', $parts) or !is_string($parts['uid'])) { throw new Exception_BadResponse("Missing \"uid\" string field."); } $userId = $parts['uid']; if ($tokenType !== "Bearer" && $tokenType !== "bearer") { throw new Exception_BadResponse("Unknown \"token_type\"; expecting \"Bearer\", got " . Client::q($tokenType)); } return array($accessToken, $userId); }
/** * Call this after the user has visited the authorize URL ({@link start()}), approved your app, * and was redirected to your redirect URI. * * @param array $queryParams * The query parameters on the GET request to your redirect URI. * * @return array * A <code>list(string $accessToken, string $userId, string $urlState)</code>, where * <code>$accessToken</code> can be used to construct a {@link Client}, <code>$userId</code> * is the user ID of the user's Dropbox account, and <code>$urlState</code> is the * value you originally passed in to {@link start()}. * * @throws Exception * Thrown if there's an error getting the access token from Dropbox. * @throws WebAuthException_BadRequest * @throws WebAuthException_BadState * @throws WebAuthException_Csrf * @throws WebAuthException_NotApproved * @throws WebAuthException_Provider */ function finish($queryParams) { Checker::argArray("queryParams", $queryParams); $csrfTokenFromSession = $this->csrfTokenStore->get(); Checker::argStringOrNull("this->csrfTokenStore->get()", $csrfTokenFromSession); // Check well-formedness of request. if (!isset($queryParams['state'])) { throw new WebAuthException_BadRequest("Missing query parameter 'state'."); } $state = $queryParams['state']; Checker::argString("queryParams['state']", $state); $error = null; if (isset($queryParams['error'])) { $error = $queryParams['error']; Checker::argString("queryParams['error']", $error); $errorDescription = null; if (isset($queryParams['error_description'])) { $errorDescription = $queryParams['error_description']; Checker::argString("queryParams['error_description']", $errorDescription); } } $code = null; if (isset($queryParams['code'])) { $code = $queryParams['code']; Checker::argString("queryParams['code']", $code); } if ($code !== null && $error !== null) { throw new WebAuthException_BadRequest("Query parameters 'code' and 'error' are both set;" . " only one must be set."); } if ($code === null && $error === null) { throw new WebAuthException_BadRequest("Neither query parameter 'code' or 'error' is set."); } // Check CSRF token if ($csrfTokenFromSession === null) { throw new WebAuthException_BadState(); } // Sanity check to make sure something hasn't gone terribly wrong. assert(strlen($csrfTokenFromSession) > 20); $splitPos = strpos($state, "|"); if ($splitPos === false) { $givenCsrfToken = $state; $urlState = null; } else { $givenCsrfToken = substr($state, 0, $splitPos); $urlState = substr($state, $splitPos + 1); } if (!Security::stringEquals($csrfTokenFromSession, $givenCsrfToken)) { throw new WebAuthException_Csrf("Expected " . Client::q($csrfTokenFromSession) . ", got " . Client::q($givenCsrfToken) . "."); } $this->csrfTokenStore->clear(); // Check for error identifier if ($error !== null) { if ($error === 'access_denied') { // When the user clicks "Deny". if ($errorDescription === null) { throw new WebAuthException_NotApproved("No additional description from Dropbox."); } else { throw new WebAuthException_NotApproved("Additional description from Dropbox: {$errorDescription}"); } } else { // All other errors. $fullMessage = $error; if ($errorDescription !== null) { $fullMessage .= ": "; $fullMessage .= $errorDescription; } throw new WebAuthException_Provider($fullMessage); } } // If everything went ok, make the network call to get an access token. list($accessToken, $userId) = $this->_finish($code, $this->redirectUri); return array($accessToken, $userId, $urlState); }