dropbox-api icon indicating copy to clipboard operation
dropbox-api copied to clipboard

Bad Request 400 if using App Key and App Secret

Open skunkbad opened this issue 4 years ago • 9 comments

I'm converting some existing code to use your package because the old package locked us into Guzzle 6.x. So I already have an app key, app secret, and non-expiring access token.

According to your documentation, it looks like I should be able to supply the app key and app secret in an array to the constructor of Client, or I can provide the access token as a string. The access token as a string works to connect, but with the array of app key and app secret I get a Bad Request 400 error. My test code shown below:

public function testDropboxConnection()
{
	require WILDFISH_CONFIGS_DIR . '.dropbox.php';    
	$dropboxConfig = getWassDropboxConfig('production');    
	try{
		$client = new \Spatie\Dropbox\Client( $dropboxConfig['accessToken'] );

		// For some reason this doesn't work...
		/*$client = new \Spatie\Dropbox\Client([
			$dropboxConfig['appKey'], 
			$dropboxConfig['appSecret']
		]);*/    
		dump( $client->getAccountInfo() );
	}
	catch( \Spatie\Dropbox\Exceptions\BadRequest $e )
	{
		if( ENVIRONMENT == 'development' && TRUE )
			dump( $e );
	}
	catch( \Exception $e )
	{
		if( ENVIRONMENT == 'development' && TRUE )
			dump( $e );
	}
}

Since I have it working with the access token, it's not critical for me that you fix this, but it did waste some of my time, so I thought I'd leave this here so you can take a look.

skunkbad avatar Sep 21 '21 19:09 skunkbad

Hi, i confirm this. It works with short-lived token but it doesn't work with app key/secret

Code example:

# $client = new Client('APP_TOKEN' ); # works with short-lived token
$client = new Client( [ 'APP_KEY', 'APP_SECRET' ] ); # doesn't work with app key/secret
$adapter = new DropboxAdapter($client);
$filesystem = new Filesystem($adapter, ['case_sensitive' => false]);
$filesystem->put('/some/path/to/file/' . $file , $file_content);

Error:

  +dropboxCode: null
  #message: ""
  #code: 0
  #file: "/var/www/app/current/vendor/spatie/dropbox-api/src/Client.php"
  #line: 667
  trace: {
    /var/www/app/current/vendor/spatie/dropbox-api/src/Client.php:667 {
      Spatie\Dropbox\Client->determineException(ClientException $exception): Exception …
      › if (in_array($exception->getResponse()->getStatusCode(), [400, 409])) {
      ›     return new BadRequest($exception->getResponse());
      › }
    }

preBit avatar Sep 23 '21 05:09 preBit

@preBit

if it is working with short-lived token so how can we manage short-lived token after it expire?

Any solutions for token using that we dont need to add token manually when site goes to production mode.

divyesh1354 avatar Oct 02 '21 06:10 divyesh1354

App Authentication (Access key with Secret) only works on a couple of endpoints as per the http documentation

https://api.dropboxapi.com/2/check/app and https://content.dropboxapi.com/2/files/get_thumbnail_v2 to be specific.

You need to do everything else with a valid user token. If it's your own user token, it can be generated as non-expiring via your App console. If it's other user tokens you need to use the token refresh methods as documented in the readme.

Cheers.

smartssa avatar Oct 26 '21 15:10 smartssa

See above linked dropbox documentation.

In my process, i fetch an offline token. https://www.dropbox.com/oauth2/authorize?client_id=[APP_KEY]&response_type=code&token_access_type=offline

  • if token_access_type=offline, then the access token payload returned by a successful /oauth2/token call will contain a short-lived access_token and a long-lived refresh_token.
  • the refresh_token can be used to request a new short-lived access token as long as a user's approval remains valid. ...

GDmac avatar Jan 12 '22 13:01 GDmac

I am having the same issue. The endpoint "List Folder" is covered by App Authentication but does not work with this package.

The error message I get is

Spatie\Dropbox\Exceptions\BadRequest path/unsupported_content_type/. at vendor/spatie/dropbox-api/src/Client.php:667 +2 vendor frames

matthewhutchings avatar Jan 19 '22 16:01 matthewhutchings

image Hello, @GDmac I was reading the documentation above. I am not sure how to implement AutoRefreshingDropBoxTokenService. Please help

Jnyawach avatar May 19 '22 14:05 Jnyawach

See https://github.com/spatie/dropbox-api/issues/75 In essence, you check if the accessToken is not expired, if so, then acquire a new accessToken with help of the refreshToken.

GDmac avatar Jun 01 '22 08:06 GDmac

Can you give an example? I can't get it to work with

use Spatie\Dropbox\TokenProvider;
$tokenProvider = new AutoRefreshingDropBoxTokenService($refreshToken);
$client = new Spatie\Dropbox\Client($tokenProvider);

juliocoliveira avatar Aug 08 '22 22:08 juliocoliveira

I had tried to put the code from https://www.dropbox.com/oauth2/authorize?client_id=%5BAPP_KEY%5D&response_type=code&token_access_type=offline to use as access token but it does not work. Can you please help?

juliocoliveira avatar Aug 08 '22 23:08 juliocoliveira

@juliocoliveira here's an example of the implementation of the file AutoRefreshingDropBoxTokenService.php

<?php

namespace App\Services;

use Exception;
use GuzzleHttp\Client as HttpClient;
use Spatie\Dropbox\TokenProvider;


class AutoRefreshingDropBoxTokenService implements TokenProvider
{
   private string $key;

   private string $secret;

   private string $refreshToken;

   public function __construct()
   {
       $this->key = config('filesystems.disks.dropbox.key');
       $this->secret = config('filesystems.disks.dropbox.secret');
       $this->refreshToken = config('filesystems.disks.dropbox.refresh_token');
   }

   public function getToken(): string
   {
       return $this->refreshToken();
//        return Cache::remember('access_token', 14000, function () {
//            return $this->refreshToken();
//        });
   }

   public function refreshToken(): string|bool
   {
       try {
           $client = new HttpClient();
           $res = $client->request(
               'POST',
               "https://{$this->key}:{$this->secret}@api.dropbox.com/oauth2/token",
               [
                   'form_params' => [
                       'grant_type' => 'refresh_token',
                       'refresh_token' => $this->refreshToken,
                   ],
               ]
           );

           if ($res->getStatusCode() == 200) {
               $response = json_decode($res->getBody(), true);

               return trim(json_encode($response['access_token']), '"');
           } else {
               return false;
           }
       } catch (Exception $e) {
//            ray("[{$e->getCode()}] {$e->getMessage()}");

           return false;
       }
   }
}

You should also have the .env variables:

DROPBOX_APP_KEY=<App key from dropbox website>
DROPBOX_APP_SECRET=<App scecret from  dropbox website>
DROPBOX_ACCESS_TOKEN=<token generated on dropbox website>
DROPBOX_REFRESH_TOKEN=<this is created on the step 4 below >

How to get refresh token

  1. Visit in the browser: https://www.dropbox.com/oauth2/authorize?client_id=<APP_KEY>&response_type=code&token_access_type=offline
  2. copy that access code to use in the step below
  3. in the terminal use the access code provided above curl https://api.dropbox.com/oauth2/token -d code=<ACCESS_CODE> -d grant_type=authorization_code -u <APP_KEY>:<APP_SECRET>
  4. save refresh token in env variable

Hope it helps

ruibeard avatar Aug 22 '22 15:08 ruibeard

Ahm thanks. Very helpful.

juliocoliveira avatar Aug 22 '22 15:08 juliocoliveira