Overview

Classes

  • Dropbox\AppInfo
  • Dropbox\ArrayEntryStore
  • Dropbox\AuthBase
  • Dropbox\AuthInfo
  • Dropbox\Client
  • Dropbox\OAuth1AccessToken
  • Dropbox\OAuth1Upgrader
  • Dropbox\Path
  • Dropbox\RootCertificates
  • Dropbox\Security
  • Dropbox\SSLTester
  • Dropbox\Util
  • Dropbox\WebAuth
  • Dropbox\WebAuthBase
  • Dropbox\WebAuthNoRedirect
  • Dropbox\WriteMode

Interfaces

  • Dropbox\ValueStore

Exceptions

  • Dropbox\AppInfoLoadException
  • Dropbox\AuthInfoLoadException
  • Dropbox\DeserializeException
  • Dropbox\Exception
  • Dropbox\Exception_BadRequest
  • Dropbox\Exception_BadResponse
  • Dropbox\Exception_BadResponseCode
  • Dropbox\Exception_InvalidAccessToken
  • Dropbox\Exception_NetworkIO
  • Dropbox\Exception_OverQuota
  • Dropbox\Exception_ProtocolError
  • Dropbox\Exception_RetryLater
  • Dropbox\Exception_ServerError
  • Dropbox\HostLoadException
  • Dropbox\StreamReadException
  • Dropbox\WebAuthException_BadRequest
  • Dropbox\WebAuthException_BadState
  • Dropbox\WebAuthException_Csrf
  • Dropbox\WebAuthException_NotApproved
  • Dropbox\WebAuthException_Provider
  • Overview
  • Class
  1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 
<?php
namespace Dropbox;

/**
 * OAuth 2 "authorization code" flow.  (This SDK does not support the "token" flow.)
 *
 * Use {@link start} and {@link 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 `$_SESSION`, you can pass in something like
     * `new ArrayEntryStore($_SESSION, 'dropbox-auth-csrf-token')`.
     *
     * If you're not using $_SESSION, you might have to create your own class that provides
     * the same `get()`/`set()`/`clear()` 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
     * `$redirectUri` 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 `$csrfTokenStore` 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 `true` 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 `list(string $accessToken, string $userId, string $urlState)`, where
     *    `$accessToken` can be used to construct a {@link Client}, `$userId`
     *    is the user ID of the user's Dropbox account, and `$urlState` 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);
    }
}
Dropbox SDK for PHP API documentation generated by ApiGen