132 lines
4.1 KiB
PHP
132 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Libraries\DigitMotor;
|
|
|
|
use App\Models\MotorTokenModel;
|
|
use Config\DigitMotor as DigitMotorConfig;
|
|
|
|
/**
|
|
* Owns Digit OneAPI token lifecycle (cache + refresh).
|
|
*/
|
|
class DigitAuthClient
|
|
{
|
|
protected DigitMotorConfig $config;
|
|
protected MotorTokenModel $tokenModel;
|
|
|
|
public function __construct(?DigitMotorConfig $config = null)
|
|
{
|
|
$this->config = $config ?? config('DigitMotor');
|
|
$this->tokenModel = new MotorTokenModel();
|
|
helper('api');
|
|
}
|
|
|
|
/**
|
|
* Return a valid Bearer access token, refreshing from Digit when needed.
|
|
*
|
|
* @throws DigitApiException
|
|
*/
|
|
public function getToken(bool $forceRefresh = false): string
|
|
{
|
|
if (!$forceRefresh) {
|
|
$cached = $this->tokenModel->getByEnvironment($this->config->environment);
|
|
if ($cached && !empty($cached['access_token']) && !empty($cached['expires_at'])) {
|
|
$expiresAt = strtotime($cached['expires_at']);
|
|
if ($expiresAt !== false && ($expiresAt - $this->config->tokenLeewaySec) > time()) {
|
|
return $cached['access_token'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->fetchAndStoreToken();
|
|
}
|
|
|
|
/**
|
|
* Force refresh (e.g. after 401).
|
|
*
|
|
* @throws DigitApiException
|
|
*/
|
|
public function refreshToken(): string
|
|
{
|
|
return $this->getToken(true);
|
|
}
|
|
|
|
/**
|
|
* @throws DigitApiException
|
|
*/
|
|
protected function fetchAndStoreToken(): string
|
|
{
|
|
if ($this->config->username === '' || $this->config->password === '') {
|
|
throw new DigitApiException(
|
|
'Digit Motor credentials are not configured. Set DIGIT_MOTOR_USERNAME and DIGIT_MOTOR_PASSWORD in .env.',
|
|
'CONFIG',
|
|
0
|
|
);
|
|
}
|
|
|
|
$url = $this->config->baseUrl . $this->config->authPath;
|
|
$headers = ['Content-Type: application/json'];
|
|
$body = [
|
|
'username' => $this->config->username,
|
|
'password' => $this->config->password,
|
|
];
|
|
|
|
$response = call_third_party_api($url, 'POST', $headers, $body);
|
|
$httpCode = (int) ($response['code'] ?? 0);
|
|
$data = $response['data'] ?? [];
|
|
|
|
if (is_string($data)) {
|
|
$decoded = json_decode($data, true);
|
|
$data = is_array($decoded) ? $decoded : [];
|
|
}
|
|
|
|
$accessToken = $data['access_token']
|
|
?? $data['accessToken']
|
|
?? $data['token']
|
|
?? null;
|
|
|
|
if (empty($accessToken) || empty($response['status'])) {
|
|
$msg = null;
|
|
if (is_array($data)) {
|
|
$msg = $data['message'] ?? $data['error'] ?? $data['error_description'] ?? null;
|
|
if (is_array($msg)) {
|
|
$msg = json_encode($msg);
|
|
}
|
|
}
|
|
if (!$msg && is_string($response['data'] ?? null) && stripos((string) $response['data'], '502') !== false) {
|
|
$msg = 'Digit auth gateway is down (HTTP 502). Retry in a few minutes.';
|
|
}
|
|
if (!$msg && $httpCode >= 500) {
|
|
$msg = 'Digit auth service unavailable (HTTP ' . $httpCode . ').';
|
|
}
|
|
if (!$msg) {
|
|
$msg = 'Digit token generation failed.';
|
|
}
|
|
throw new DigitApiException(
|
|
$msg,
|
|
$data['code'] ?? (string) $httpCode,
|
|
$httpCode,
|
|
is_array($data) ? $data : ['raw' => $response['data'] ?? null],
|
|
null,
|
|
$url,
|
|
[
|
|
'username' => $this->config->username,
|
|
'password' => '***REDACTED***',
|
|
]
|
|
);
|
|
}
|
|
|
|
$expiresIn = (int) ($data['expires_in'] ?? $data['expiresIn'] ?? 900);
|
|
$refreshToken = $data['refresh_token'] ?? $data['refreshToken'] ?? null;
|
|
$expiresAt = date('Y-m-d H:i:s', time() + max(60, $expiresIn));
|
|
|
|
$this->tokenModel->upsertToken(
|
|
$this->config->environment,
|
|
$accessToken,
|
|
$refreshToken,
|
|
$expiresAt
|
|
);
|
|
|
|
return $accessToken;
|
|
}
|
|
}
|