flysystem-dropbox
flysystem-dropbox copied to clipboard
Access token expiration
Hi, I am using spatie/flysystem-dropbox (^1.2) and I get dropbox directories with: $dirs = Storage::disk('dropbox')->directories(); or files with: $files = Storage::disk('dropbox')->files($foldername);
I wrote these 2 variable in .env file: DROPBOX_ACCESS_TOKEN=sl.BLOC_r... DROPBOX_APP_SECRET=...
I got DROPBOX_ACCESS_TOKEN here https://www.dropbox.com/developers/apps
After 4 hours I get this error:
Client error: POST https://api.dropboxapi.com/2/files/list_folder
resulted in a 401 Unauthorized
response:
{"error_summary": "expired_access_token/.", "error": {".tag": "expired_access_token"}}
{"exception":"[object] (GuzzleHttp\Exception\ClientException(code: 401): Client error: POST https://api.dropboxapi.com/2/files/list_folder
resulted in a 401 Unauthorized
response:
{"error_summary": "expired_access_token/.", "error": {".tag": "expired_access_token"}}
at /home/xxx/laravel/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113)
I manually solve it going back to https://www.dropbox.com/developers/apps, generating a new token and writing it in .env. How can I avoid this manual step?
A workmate helped me: instead of using an access token we manually got a refresh token (it should never expire) and used it. To get a refresh token see the accepted solution here: https://www.dropboxforum.com/t5/Dropbox-API-Support-Feedback/Issue-in-generating-access-token/td-p/59266
.env:
DROPBOX_APP_KEY=xxx DROPBOX_APP_SECRET=xxx DROPBOX_REFRESH_TOKEN=xxx
config\app.php:
'providers' => [ // ... App\Providers\DropboxServiceProvider::class, ]
config\filesystems.php:
'disks' => [ // ... 'dropbox' => [ 'driver' => 'dropbox', 'refreshToken' => env('DROPBOX_REFRESH_TOKEN'), 'appKey' => env('DROPBOX_APP_KEY'), 'appSecret' => env('DROPBOX_APP_SECRET'), ], ]
app\Adapters\AutoRefreshingDropBoxTokenService.php:
<?php
namespace App\Adapters;
class AutoRefreshingDropBoxTokenService
{
public function getToken($key, $secret, $refreshToken)
{
try {
$client = new \GuzzleHttp\Client();
$res = $client->request("POST", "https://{$key}:{$secret}@api.dropbox.com/oauth2/token", [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
]
]);
if ($res->getStatusCode() == 200) {
return json_decode($res->getBody(), TRUE)['access_token'];
} else {
info(json_decode($res->getBody(), TRUE));
return false;
}
} catch (\Exception $e) {
info($e->getMessage());
return false;
}
}
}
app\Providers\DropboxServiceProvider.php:
<?php
namespace App\Providers;
use Storage;
use League\Flysystem\Filesystem;
use Illuminate\Support\ServiceProvider;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
use App\Adapters\AutoRefreshingDropBoxTokenService;
class DropboxServiceProvider extends ServiceProvider
{
public function register()
{
//
}
public function boot()
{
Storage::extend('dropbox', function ($app, $config) {
$token = new AutoRefreshingDropBoxTokenService;
$client = new DropboxClient($token->getToken($config['appKey'], $config['appSecret'], $config['refreshToken']));
return new Filesystem(new DropboxAdapter($client), ['case_sensitive' => false]);
});
}
}
Use example:
use Storage;
use Illuminate\Filesystem\Filesystem; // needed?
//...
$dirs = Storage::disk('dropbox')->directories();
foreach ($dirs as $dir) {
$files = Storage::disk('dropbox')->files($dir);
foreach ($files as $file) { // $file contains the folder name
$array_temp = explode('/', $file);
$filename = $array_temp[count($array_temp) - 1];
// copy file in local storage
Storage::disk('local')->writeStream( 'local_folder/' . $filename, Storage::disk('dropbox')->readStream( $file ));
// delete dropbox file
Storage::disk('dropbox')->delete($file);
}
}
@anderea1 The refresh token how did you generate it?
The refresh_token is the same one you generated in DROPBOX_ACCESS_TOKEN here https://www.dropbox.com/developers/apps
I get a 'Refresh token is malformed' error when using the token generated in the dropbox app console, as the refresh token curl https://api.dropbox.com/oauth2/token -d grant_type=refresh_token -d refresh_token=refresh-token -u appkey:secret
{"error": "invalid_grant", "error_description": "refresh token is malformed"}
Still unsure how to get the refresh token described above.
I'm also getting the 'token is malformed' error
I just generated a refresh token using this guide: https://gist.github.com/phuze/755dd1f58fba6849fbf7478e77e2896a
Dear contributor,
because this issue seems to be inactive for quite some time now, I've automatically closed it. If you feel this issue deserves some attention from my human colleagues feel free to reopen it.
app\Providers\DropboxServiceProvider.php:
namespace App\Providers;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
use App\Adapters\AutoRefreshingDropBoxTokenService;
class DropboxServiceProvider extends ServiceProvider
{
public function register()
{
//
}
public function boot()
{
Storage::extend('dropbox', function ($app, $config) {
$token = new AutoRefreshingDropBoxTokenService;
$client = new DropboxClient(
$token->getToken($config['appKey'],
$config['appSecret'],
$config['refreshToken'])
);
$adapter = new DropboxAdapter($client);
$driver = new Filesystem($adapter, ['case_sensitive' => false]);
return new FilesystemAdapter($driver, $adapter);
});
}
}
I tried something else personally, without using AutoRefreshingDropBoxTokenService [ not necessary when using the flysystem-dropbox
] and it is less verbose :
You will still need to authorize the access to the Dropbox App using this link :
https://www.dropbox.com/oauth2/authorize?client_id<YOUR_APP_KEY>&response_type=code&token_access_type=offline
This will give you the authorization_code
needed to retrieve a refresh_token
with this curl request :
curl https://api.dropbox.com/oauth2/token -d code=<ACCESS_CODE> -d grant_type=authorization_code -u <APP_KEY>:<APP_SECRET>
Giving you access to the refresh_token
needed in the DROPBOX_REFRESH_TOKEN
indicated below . The other elements are available in your Dropbox App.
.env.example
DROPBOX_APP_KEY=
DROPBOX_APP_SECRET=
DROPBOX_REFRESH_TOKEN=
DROPBOX_TOKEN_URL=https://${DROPBOX_APP_KEY}:${DROPBOX_APP_SECRET}@api.dropbox.com/oauth2/token
config/filesystems.php
'dropbox' => [
'driver' => 'dropbox',
'token_url' => env('DROPBOX_TOKEN_URL'),
'refresh_token' => env('DROPBOX_REFRESH_TOKEN'),
],
`app/Providers/DropboxServiceProvider.php
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
use GuzzleHttp\Client;
class DropboxServiceProvider extends ServiceProvider
{
public function boot(): void
{
Storage::extend( 'dropbox', function( Application $app, array $config )
{
$resource = ( new Client() )->post( $config[ 'token_url' ] , [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => $config[ 'refresh_token' ]
]
]);
$accessToken = json_decode( $resource->getBody(), true )[ 'access_token' ];
$adapter = new DropboxAdapter( new DropboxClient( $accessToken ) );
return new FilesystemAdapter( new Filesystem( $adapter, $config ), $adapter, $config );
});
}
}
A PR in code and documentation should be interesting. Should I PR this ? The previous code is also indicated in the Laravel 10 Documentation - Custom Filesystems
@Itemshopp Exactly what I needed, thanks so much for sharing.