forrest
forrest copied to clipboard
Reusing the access/refresh tokens
Hi,
I'm confused about reusing the tokens, the package seems to support only one-time use communication? Here's the flow for multi-user connections:
- Let the user connect using
WebServer
authentication method - Store the access/refresh tokens for the future use
- Synchronize the contacts/leads etc. on a daily basis
The tokens can be stored in the session, cache, or object, all of those are short-term storage as I understand.
Moreover, even if I store the tokens myself after Forrest::callback();
I still can't reuse them because there's no method that supports such functionality? Am I missing something fundamental?
Thanks, Vladan
I'm struggling with this as well.
So I've found that the the forrest.php
cache file is being weirdly ignored. If I change to a new sfdc.php
config then things work as expected.
I've recently seen this as well. @omniphx any insights?
I've not yet run into an issue with caching, but related to reusing tokens this is the solution that ended up working for my team.
We extended ForrestServiceProvider.php from this package and made our own CustomStorage class that implemented StorageInterface. This allowed us to handle saving our data to the related Eloquent model.
<?php
namespace App\Providers;
use App\Models\User;
use App\Utilities\Integrations\Salesforce\Forrest\CustomStorage;
use App\Utilities\Integrations\Salesforce\Forrest\CustomWebServer;
use Omniphx\Forrest\Formatters\JSONFormatter;
use Omniphx\Forrest\Providers\Laravel\ForrestServiceProvider;
use Omniphx\Forrest\Providers\Laravel\LaravelEncryptor;
use Omniphx\Forrest\Providers\Laravel\LaravelEvent;
use Omniphx\Forrest\Providers\Laravel\LaravelInput;
use Omniphx\Forrest\Repositories\InstanceURLRepository;
use Omniphx\Forrest\Repositories\RefreshTokenRepository;
use Omniphx\Forrest\Repositories\ResourceRepository;
use Omniphx\Forrest\Repositories\StateRepository;
use Omniphx\Forrest\Repositories\TokenRepository;
use Omniphx\Forrest\Repositories\VersionRepository;
class CustomForrestServiceProvider extends ForrestServiceProvider
{
public function register()
{
$authenticationType = config('forrest.authentication');
if ($authenticationType === 'WebServer' || empty($authenticationType)) {
$this->app->bind('forrest', function ($app, $parameters) {
$user = $parameters['user'] ?? \Auth::user();
// Config options
$settings = config('forrest');
// Dependencies
$httpClient = $this->getClient();
$input = new LaravelInput(app('request'));
$event = new LaravelEvent(app('events'));
$encryptor = new LaravelEncryptor(app('encrypter'));
$redirect = $this->getRedirect();
$settingsStorage = new CustomStorage($user->team, 'settings');
$connectionSettingStorage = new CustomStorage($user->team, 'connection_settings');
$refreshTokenRepo = new RefreshTokenRepository($encryptor, $connectionSettingStorage);
$tokenRepo = new TokenRepository($encryptor, $connectionSettingStorage);
$resourceRepo = new ResourceRepository($settingsStorage);
$versionRepo = new VersionRepository($settingsStorage);
$instanceURLRepo = new InstanceURLRepository($tokenRepo, $settings);
$stateRepo = new StateRepository($settingsStorage);
$formatter = new JSONFormatter($tokenRepo, $settings);
return new CustomWebServer($httpClient,
$encryptor,
$event,
$input,
$redirect,
$instanceURLRepo,
$refreshTokenRepo,
$resourceRepo,
$stateRepo,
$tokenRepo,
$versionRepo,
$formatter,
$settings);
});
} else {
parent::register();
}
}
}
Here's what the CustomStorage class looks like in our case:
<?php
namespace App\Utilities\Integrations\Salesforce\Forrest;
use App\Models\Team;
use Omniphx\Forrest\Exceptions\MissingKeyException;
use Omniphx\Forrest\Interfaces\StorageInterface;
class CustomStorage implements StorageInterface
{
/**
* @var Team $team
*/
protected $team;
/**
* @var string $field
*/
protected $field;
public function __construct(Team $team, string $field = 'settings')
{
$this->team = $team;
$this->field = $field;
}
public function put($key, $value)
{
$model = $this->model();
$field = $model->{$this->field};
$field[$key] = $value;
$model->{$this->field} = $field;
$model->save();
}
public function get($key)
{
if(!$this->has($key)) {
throw new MissingKeyException(sprintf('No value for requested key: %s', $key));
}
return $this->model()->{$this->field}[$key];
}
public function has($key)
{
return array_key_exists($key, $this->model()->{$this->field});
}
/**
* @return Integration
*/
private function model()
{
return Model::firstOrCreate([
....
], [
....
]);
}
}
@SimonChaw @vladan-me -- added the ability (v2.16.0) to pass in a custom storage class without needing to completely replace the service provider. To implement, just update your config:
'storage' => [
'type' => App\Storage\CustomStorage::class,
],
If the config is not being recognized, it's possible there is a conflict with other packages see: https://github.com/omniphx/forrest/issues/262