$appKey, 'response_type' => 'code', 'token_access_type' => 'offline', 'code_challenge' => self::challenge($verifier), 'code_challenge_method' => 'S256', ], '', '&', PHP_QUERY_RFC3986); } /** Trade the pasted code for a refresh token, which does not expire. */ public static function exchange(Transport $http, string $appKey, string $code, string $verifier): string { $response = $http->send('POST', self::TOKEN, [ 'Content-Type' => 'application/x-www-form-urlencoded', ], http_build_query([ 'grant_type' => 'authorization_code', 'code' => $code, 'client_id' => $appKey, 'code_verifier' => $verifier, ]), 30); $data = $response['status'] === 200 ? json_decode($response['body'], true) : null; $token = is_array($data) ? (string) ($data['refresh_token'] ?? '') : ''; if ($token !== '' && preg_match('/^[A-Za-z0-9._~-]+$/', $token)) { return $token; } $error = json_decode($response['body'], true); $reason = is_array($error) ? (string) ($error['error'] ?? '') : ''; if ($reason === 'invalid_grant') { throw new DestinationError(__('Dropbox did not accept that code. Codes work once, for a few minutes, and only with the Connect Dropbox link on this screen. Press Connect Dropbox again and paste the new code.', 'freshet-backup')); } throw new DestinationError(sprintf( /* translators: %s: Dropbox's error code, or the connection error */ __('Dropbox did not complete the connection (%s), so nothing was changed. Press Connect Dropbox again. If it keeps failing, the readme explains how to connect with your own Dropbox app.', 'freshet-backup'), $reason !== '' ? $reason : ($response['error'] ?? 'HTTP ' . $response['status']) )); } private static function base64url(string $bytes): string { return rtrim(strtr(base64_encode($bytes), '+/', '-_'), '='); } }