mirror of
https://ghproxy.net/https://github.com/elementor/wp2static.git
synced 2025-09-01 02:21:16 +08:00
278 lines
10 KiB
PHP
278 lines
10 KiB
PHP
<?php
|
|
namespace Dropbox;
|
|
|
|
/**
|
|
* OAuth 2 "authorization code" flow. (This SDK does not support the "token" flow.)
|
|
*
|
|
* Use {@link WebAuth::start()} and {@link WebAuth::finish()} to guide your
|
|
* user through the process of giving your app access to their Dropbox account.
|
|
* At the end, you will have an access token, which you can pass to {@link Client}
|
|
* and start making API calls.
|
|
*
|
|
* Example:
|
|
*
|
|
* <code>
|
|
* use \Dropbox as dbx;
|
|
*
|
|
* function getWebAuth()
|
|
* {
|
|
* $appInfo = dbx\AppInfo::loadFromJsonFile(...);
|
|
* $clientIdentifier = "my-app/1.0";
|
|
* $redirectUri = "https://example.org/dropbox-auth-finish";
|
|
* $csrfTokenStore = new dbx\ArrayEntryStore($_SESSION, 'dropbox-auth-csrf-token');
|
|
* return new dbx\WebAuth($appInfo, $clientIdentifier, $redirectUri, $csrfTokenStore, ...);
|
|
* }
|
|
*
|
|
* // ----------------------------------------------------------
|
|
* // In the URL handler for "/dropbox-auth-start"
|
|
*
|
|
* $authorizeUrl = getWebAuth()->start();
|
|
* header("Location: $authorizeUrl");
|
|
*
|
|
* // ----------------------------------------------------------
|
|
* // In the URL handler for "/dropbox-auth-finish"
|
|
*
|
|
* try {
|
|
* list($accessToken, $userId, $urlState) = getWebAuth()->finish($_GET);
|
|
* assert($urlState === null); // Since we didn't pass anything in start()
|
|
* }
|
|
* catch (dbx\WebAuthException_BadRequest $ex) {
|
|
* error_log("/dropbox-auth-finish: bad request: " . $ex->getMessage());
|
|
* // Respond with an HTTP 400 and display error page...
|
|
* }
|
|
* catch (dbx\WebAuthException_BadState $ex) {
|
|
* // Auth session expired. Restart the auth process.
|
|
* header('Location: /dropbox-auth-start');
|
|
* }
|
|
* catch (dbx\WebAuthException_Csrf $ex) {
|
|
* error_log("/dropbox-auth-finish: CSRF mismatch: " . $ex->getMessage());
|
|
* // Respond with HTTP 403 and display error page...
|
|
* }
|
|
* catch (dbx\WebAuthException_NotApproved $ex) {
|
|
* error_log("/dropbox-auth-finish: not approved: " . $ex->getMessage());
|
|
* }
|
|
* catch (dbx\WebAuthException_Provider $ex) {
|
|
* error_log("/dropbox-auth-finish: error redirect from Dropbox: " . $ex->getMessage());
|
|
* }
|
|
* catch (dbx\Exception $ex) {
|
|
* error_log("/dropbox-auth-finish: error communicating with Dropbox API: " . $ex->getMessage());
|
|
* }
|
|
*
|
|
* // We can now use $accessToken to make API requests.
|
|
* $client = dbx\Client($accessToken, ...);
|
|
* </code>
|
|
*
|
|
*/
|
|
class WebAuth extends WebAuthBase
|
|
{
|
|
/**
|
|
* The URI that the Dropbox server will redirect the user to after the user finishes
|
|
* authorizing your app. This URI must be HTTPS-based and
|
|
* <a href="https://www.dropbox.com/developers/apps">pre-registered with Dropbox</a>,
|
|
* though "localhost"-based and "127.0.0.1"-based URIs are allowed without pre-registration
|
|
* and can be either HTTP or HTTPS.
|
|
*
|
|
* @return string
|
|
*/
|
|
function getRedirectUri() { return $this->redirectUri; }
|
|
|
|
/** @var string */
|
|
private $redirectUri;
|
|
|
|
/**
|
|
* A object that lets us save CSRF token string to the user's session. If you're using the
|
|
* standard PHP <code>$_SESSION</code>, you can pass in something like
|
|
* <code>new ArrayEntryStore($_SESSION, 'dropbox-auth-csrf-token')</code>.
|
|
*
|
|
* If you're not using $_SESSION, you might have to create your own class that provides
|
|
* the same <code>get()</code>/<code>set()</code>/<code>clear()</code> methods as
|
|
* {@link ArrayEntryStore}.
|
|
*
|
|
* @return ValueStore
|
|
*/
|
|
function getCsrfTokenStore() { return $this->csrfTokenStore; }
|
|
|
|
/** @var object */
|
|
private $csrfTokenStore;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param AppInfo $appInfo
|
|
* See {@link getAppInfo()}
|
|
* @param string $clientIdentifier
|
|
* See {@link getClientIdentifier()}
|
|
* @param null|string $redirectUri
|
|
* See {@link getRedirectUri()}
|
|
* @param null|ValueStore $csrfTokenStore
|
|
* See {@link getCsrfTokenStore()}
|
|
* @param null|string $userLocale
|
|
* See {@link getUserLocale()}
|
|
*/
|
|
function __construct($appInfo, $clientIdentifier, $redirectUri, $csrfTokenStore, $userLocale = null)
|
|
{
|
|
parent::__construct($appInfo, $clientIdentifier, $userLocale);
|
|
|
|
Checker::argStringNonEmpty("redirectUri", $redirectUri);
|
|
|
|
$this->csrfTokenStore = $csrfTokenStore;
|
|
$this->redirectUri = $redirectUri;
|
|
}
|
|
|
|
/**
|
|
* Starts the OAuth 2 authorization process, which involves redirecting the user to the
|
|
* returned authorization URL (a URL on the Dropbox website). When the user then
|
|
* either approves or denies your app access, Dropbox will redirect them to the
|
|
* <code>$redirectUri</code> given to constructor, at which point you should
|
|
* call {@link finish()} to complete the authorization process.
|
|
*
|
|
* This function will also save a CSRF token using the <code>$csrfTokenStore</code> given to
|
|
* the constructor. This CSRF token will be checked on {@link finish()} to prevent
|
|
* request forgery.
|
|
*
|
|
* See <a href="https://www.dropbox.com/developers/core/docs#oa2-authorize">/oauth2/authorize</a>.
|
|
*
|
|
* @param string|null $urlState
|
|
* Any data you would like to keep in the URL through the authorization process.
|
|
* This exact state will be returned to you by {@link finish()}.
|
|
*
|
|
* @param boolean|null $forceReapprove
|
|
* If a user has already approved your app, Dropbox may skip the "approve" step and
|
|
* redirect immediately to your callback URL. Setting this to <code>true</code> tells
|
|
* Dropbox to never skip the "approve" step.
|
|
*
|
|
* @return array
|
|
* The URL to redirect the user to.
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
function start($urlState = null, $forceReapprove = false)
|
|
{
|
|
Checker::argStringOrNull("urlState", $urlState);
|
|
|
|
$csrfToken = self::encodeCsrfToken(Security::getRandomBytes(16));
|
|
$state = $csrfToken;
|
|
if ($urlState !== null) {
|
|
$state .= "|";
|
|
$state .= $urlState;
|
|
}
|
|
$this->csrfTokenStore->set($csrfToken);
|
|
|
|
return $this->_getAuthorizeUrl($this->redirectUri, $state, $forceReapprove);
|
|
}
|
|
|
|
private static function encodeCsrfToken($string)
|
|
{
|
|
return strtr(base64_encode($string), '+/', '-_');
|
|
}
|
|
|
|
/**
|
|
* Call this after the user has visited the authorize URL ({@link start()}), approved your app,
|
|
* and was redirected to your redirect URI.
|
|
*
|
|
* See <a href="https://www.dropbox.com/developers/core/docs#oa2-token">/oauth2/token</a>.
|
|
*
|
|
* @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;
|
|
$errorDescription = null;
|
|
if (isset($queryParams['error'])) {
|
|
$error = $queryParams['error'];
|
|
Checker::argString("queryParams['error']", $error);
|
|
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();
|
|
}
|
|
|
|
$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 ".Util::q($csrfTokenFromSession) .
|
|
", got ".Util::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);
|
|
}
|
|
}
|