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

add access token refreshing logic #410

Merged
merged 5 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/Commands/Seat/Buckets/Update.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
use Illuminate\Support\Facades\Cache;
use Seat\Eveapi\Bus\Character;
use Seat\Eveapi\Bus\Corporation;
use Seat\Eveapi\Jobs\Character\Roles;
use Seat\Eveapi\Jobs\Token\RefreshAccessToken;
use Seat\Eveapi\Models\Bucket;
use Seat\Eveapi\Models\RefreshToken;
use Seat\Eveapi\Models\RefreshTokenSchedule;
Expand Down Expand Up @@ -140,8 +140,7 @@ private function dispatchCharacterEsiUpdate(RefreshToken $token): void
*/
private function dispatchCharacterTokenKeepAlive(RefreshToken $token): void
{
// TODO: add a job that only requests a new access token instead of a random esi job. This will require some eseye rework
Roles::dispatch($token)->onQueue('characters');
RefreshAccessToken::dispatch($token)->onQueue('characters');
}

/**
Expand Down
72 changes: 72 additions & 0 deletions src/InteractsWithToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

/*
* This file is part of SeAT
*
* Copyright (C) 2015 to present Leon Jacobs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

namespace Seat\Eveapi;

use Seat\Eveapi\Models\RefreshToken;
use Seat\Services\Contracts\EsiClient;

trait InteractsWithToken
{
abstract protected function getClient(): EsiClient;

abstract protected function getToken(): ?RefreshToken;

/**
* @return void
*/
protected function configureTokenForEsiClient(): void
{
$token = $this->getToken();
if($token !== null) {
$this->getClient()->setAuthentication($token);
}
}

/**
* Update the access_token last used in the job,
* along with the expiry time.
*
* @return void
*/
public function updateRefreshToken(): void
{
$client = $this->getClient();
$token = $this->getToken();

// If it is an unauthenticated call, there is nothing to update
if (is_null($token))
return;

if (! $client->isAuthenticated())
return;

$last_auth = $client->getAuthentication();

// update the token
if (! empty($last_auth->getRefreshToken()))
$token->refresh_token = $last_auth->getRefreshToken();
$token->token = $last_auth->getAccessToken() ?? '-';
$token->expires_on = $last_auth->getExpiresOn();
$token->save();
}
}
45 changes: 10 additions & 35 deletions src/Jobs/EsiBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use Seat\Eveapi\Exception\PermanentInvalidTokenException;
use Seat\Eveapi\Exception\TemporaryEsiOutageException;
use Seat\Eveapi\Exception\UnavailableEveServersException;
use Seat\Eveapi\InteractsWithToken;
use Seat\Eveapi\Jobs\Middleware\CheckEsiRateLimit;
use Seat\Eveapi\Jobs\Middleware\CheckEsiRouteStatus;
use Seat\Eveapi\Jobs\Middleware\CheckServerStatus;
Expand All @@ -46,6 +47,8 @@
*/
abstract class EsiBase extends AbstractJob
{
use InteractsWithToken;

/**
* ANTI_RACE_DELAY prevents rapid job recycling with low queue depths.
*/
Expand Down Expand Up @@ -212,6 +215,11 @@ public function getToken(): ?RefreshToken
return $this->token;
}

protected function getClient(): EsiClient
{
return $this->esi;
}

/**
* @return string
*/
Expand Down Expand Up @@ -299,17 +307,13 @@ public function retrieve(array $path_values = []): EsiResponse
if (! is_null($this->page))
$this->esi->page($this->page);

$this->configureTokenForEsiClient();

// Generally, we want to bubble up exceptions all the way to the
// callee. However, in the case of this worker class, we need to
// try and be vigilant with tokens that may have expired. So for
// those cases we wrap in a try/catch.
try {
if ($this->token) {
$this->token = $this->token->fresh();

$this->esi->setAuthentication($this->token);
}

$result = $this->esi->invoke($this->method, $this->endpoint, $path_values);

// Update the refresh token we have stored in the database.
Expand Down Expand Up @@ -398,35 +402,6 @@ public function warning(EsiResponse $response): void
}
}

/**
* Update the access_token last used in the job,
* along with the expiry time.
*/
public function updateRefreshToken(): void
{

tap($this->token, function ($token) {

// If no API call was made, the client would have never
// been instantiated and auth information never updated.
if (is_null($token))
return;

if (! $this->esi->isAuthenticated())
return;

$last_auth = $this->esi->getAuthentication();

if (! empty($last_auth->getRefreshToken()))
$token->refresh_token = $last_auth->getRefreshToken();

$token->token = $last_auth->getAccessToken() ?? '-';
$token->expires_on = $last_auth->getExpiresOn();

$token->save();
});
}

/**
* Check if there are any pages left in a response
* based on the number of pages available and the
Expand Down
88 changes: 88 additions & 0 deletions src/Jobs/Token/RefreshAccessToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

/*
* This file is part of SeAT
*
* Copyright (C) 2015 to present Leon Jacobs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

namespace Seat\Eveapi\Jobs\Token;

use Illuminate\Contracts\Container\BindingResolutionException;
use Seat\Eseye\Exceptions\InvalidContainerDataException;
use Seat\Eveapi\InteractsWithToken;
use Seat\Eveapi\Jobs\AbstractJob;
use Seat\Eveapi\Models\RefreshToken;
use Seat\Services\Contracts\EsiClient;

class RefreshAccessToken extends AbstractJob
{
use InteractsWithToken;

/**
* @var array
*/
protected $tags = ['character', 'token'];

/**
* @var \Seat\Eveapi\Models\RefreshToken
*/
protected $token;

/**
* @var EsiClient
*/
protected EsiClient $esi;

/**
* @param RefreshToken $token
*
* @throws BindingResolutionException
*/
public function __construct(RefreshToken $token)
{
$this->token = $token;
$this->esi = app()->make(EsiClient::class);
}

/**
* @return void
*
* @throws InvalidContainerDataException
*/
public function handle(): void
{
// pass this token to the esi client
$this->configureTokenForEsiClient();

// ensure we have a valid access token
$this->esi->getValidAccessToken();

// make sure the new token is stored
$this->updateRefreshToken();
}

public function getClient(): EsiClient
{
return $this->esi;
}

public function getToken(): ?RefreshToken
{
return $this->token;
}
}
11 changes: 11 additions & 0 deletions src/Services/EseyeClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
use Seat\Eseye\Configuration;
use Seat\Eseye\Containers\EsiAuthentication;
use Seat\Eseye\Eseye;
use Seat\Eseye\Exceptions\InvalidAuthenticationException;
use Seat\Eseye\Exceptions\InvalidContainerDataException;
use Seat\Services\Contracts\EsiClient;
use Seat\Services\Contracts\EsiResponse;
use Seat\Services\Contracts\EsiToken;
Expand Down Expand Up @@ -227,4 +229,13 @@ public function getCache(): CacheInterface
{
return $this->instance->getConfiguration()->getCache();
}

/**
* @throws InvalidAuthenticationException
* @throws InvalidContainerDataException
*/
public function getValidAccessToken(): string
{
return $this->instance->getValidAccessToken()->access_token;
}
}