Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Access token expiration #86

Closed
anderea1 opened this issue Jul 11, 2022 · 9 comments
Closed

Access token expiration #86

anderea1 opened this issue Jul 11, 2022 · 9 comments

Comments

@anderea1
Copy link

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?

@anderea1
Copy link
Author

anderea1 commented Jul 13, 2022

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);
    }
}					

@JorgeSolisC
Copy link

@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

@karunkshrestha
Copy link

karunkshrestha commented Sep 5, 2022

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.

@fri3ndly
Copy link

fri3ndly commented Oct 3, 2022

I'm also getting the 'token is malformed' error

@fri3ndly
Copy link

fri3ndly commented Oct 3, 2022

I just generated a refresh token using this guide:
https://gist.github.com/phuze/755dd1f58fba6849fbf7478e77e2896a

@spatie-bot
Copy link

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.

@lowv-developer
Copy link

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);
        });
    }
}

@mho22
Copy link

mho22 commented Apr 25, 2023

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

@chris-arated
Copy link

@itemshopp Exactly what I needed, thanks so much for sharing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants