Skip to content

Commit 7b65e00

Browse files
committed
initial shipment trackers & oauth2 client integration
1 parent e03a27e commit 7b65e00

File tree

10 files changed

+1429
-0
lines changed

10 files changed

+1429
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
/**
4+
* SiteBase
5+
* PHP Version 8.3
6+
*
7+
* @category CMS / Framework
8+
* @package Degami\Sitebase
9+
* @author Mirko De Grandis <degami@github.com>
10+
* @license MIT https://opensource.org/licenses/mit-license.php
11+
* @link https://github.com/degami/sitebase
12+
*/
13+
14+
namespace App\Base\Abstracts\OAuth2;
15+
16+
use Psr\Container\ContainerInterface;
17+
use App\Base\Abstracts\ContainerAwareObject;
18+
use App\Base\Models\OAuth2AccessToken;
19+
use Degami\Basics\Exceptions\BasicException;
20+
use GuzzleHttp\Exception\GuzzleException;
21+
22+
/**
23+
* Base Class for OAuth2 Clients
24+
*/
25+
abstract class AbstractOAuth2ApiClient extends ContainerAwareObject
26+
{
27+
protected ?OAuth2AccessToken $token = null;
28+
29+
public function __construct(
30+
protected ContainerInterface $container,
31+
protected string $clientId,
32+
protected string $clientSecret,
33+
protected string $scope,
34+
protected string $authUrl,
35+
protected string $apiBaseUrl
36+
) { }
37+
38+
/**
39+
* Get the API base URL
40+
*
41+
* @return string
42+
*/
43+
protected function getApiBaseUrl(): string
44+
{
45+
return rtrim($this->apiBaseUrl, '/');
46+
}
47+
48+
/**
49+
* Get the Auth URL
50+
*
51+
* @return string
52+
*/
53+
protected function getAuthUrl(): string
54+
{
55+
return rtrim($this->authUrl, '/');
56+
}
57+
58+
/**
59+
* Get the OAuth2 token
60+
*
61+
* @return OAuth2AccessToken
62+
* @throws BasicException
63+
* @throws GuzzleException
64+
*/
65+
protected function getToken(): OAuth2AccessToken
66+
{
67+
if ($this->token && $this->token->getExpitesAt() > new \DateTime()) {
68+
return $this->token;
69+
}
70+
71+
$response = $this->getUtils()->httpRequest($this->getAuthUrl(), 'POST', [
72+
'form_params' => [
73+
'grant_type' => 'client_credentials',
74+
'client_id' => $this->clientId,
75+
'client_secret' => $this->clientSecret,
76+
'scope' => $this->scope,
77+
],
78+
]);
79+
80+
if (!isset($response->access_token)) {
81+
throw new BasicException("Can't gain OAuth2 Token from {$this->getAuthUrl()}");
82+
}
83+
84+
return $this->token = OAuth2AccessToken::createFromResponse($this->getApiBaseUrl(), (array)$response);
85+
}
86+
87+
/**
88+
* Refresh the OAuth2 token
89+
*
90+
* @return OAuth2AccessToken
91+
* @throws BasicException
92+
* @throws GuzzleException
93+
*/
94+
protected function refreshToken() : OAuth2AccessToken
95+
{
96+
if (!$this->token || !$this->token->getRefreshToken()) {
97+
throw new BasicException('No refresh token available');
98+
}
99+
100+
$response = $this->getUtils()->httpRequest($this->getAuthUrl(), 'POST', [
101+
'form_params' => [
102+
'grant_type' => 'refresh_token',
103+
'refresh_token' => $this->token->getRefreshToken(),
104+
'client_id' => $this->clientId,
105+
'client_secret' => $this->clientSecret,
106+
'scope' => $this->scope,
107+
],
108+
]);
109+
110+
return $this->token = OAuth2AccessToken::createFromResponse($this->getApiBaseUrl(), (array)$response);
111+
}
112+
113+
/**
114+
* Make an API request
115+
*
116+
* @param string $endpoint
117+
* @param string $method
118+
* @param array $options
119+
* @return mixed
120+
* @throws BasicException
121+
* @throws GuzzleException
122+
*/
123+
protected function apiRequest(string $endpoint, string $method = 'GET', array $options = []): mixed
124+
{
125+
$token = $this->getToken();
126+
$options['headers']['Authorization'] = "{$token->getType()} {$token->getAccessToken()}";
127+
$options['headers']['Accept'] = 'application/json';
128+
129+
return $this->getUtils()->httpRequest($this->getApiBaseUrl() . '/' . ltrim($endpoint, '/'), $method, $options);
130+
}
131+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace App\Base\Abstracts\OAuth2;
4+
5+
use Psr\Container\ContainerInterface;
6+
use App\Base\Models\OAuth2AccessToken;
7+
use Degami\Basics\Exceptions\BasicException;
8+
use GuzzleHttp\Exception\GuzzleException;
9+
10+
abstract class AuthorizationCodeOAuth2Client extends AbstractOAuth2ApiClient
11+
{
12+
public function __construct(
13+
protected ContainerInterface $container,
14+
protected string $clientId,
15+
protected string $clientSecret,
16+
protected string $scope,
17+
protected string $authUrl,
18+
protected string $apiBaseUrl,
19+
protected string $redirectUri
20+
) {
21+
parent::__construct($container, $clientId, $clientSecret, $scope, $authUrl, $apiBaseUrl);
22+
}
23+
24+
/**
25+
* Get the authorization URL
26+
*
27+
* @return string
28+
*/
29+
public function getAuthorizationUrl(): string
30+
{
31+
$params = http_build_query([
32+
'response_type' => 'code',
33+
'client_id' => $this->clientId,
34+
'redirect_uri' => $this->redirectUri,
35+
'scope' => $this->scope,
36+
]);
37+
38+
return "{$this->getAuthUrl()}/authorize?{$params}";
39+
}
40+
41+
/**
42+
* Exchange authorization code for access token
43+
*
44+
* @param string $code
45+
* @return OAuth2AccessToken
46+
* @throws BasicException
47+
* @throws GuzzleException
48+
*/
49+
public function exchangeCodeForToken(string $code): OAuth2AccessToken
50+
{
51+
$response = $this->getUtils()->httpRequest($this->getAuthUrl() . '/token', 'POST', [
52+
'form_params' => [
53+
'grant_type' => 'authorization_code',
54+
'code' => $code,
55+
'client_id' => $this->clientId,
56+
'client_secret' => $this->clientSecret,
57+
'redirect_uri' => $this->redirectUri,
58+
],
59+
]);
60+
61+
if (!isset($response->access_token)) {
62+
throw new BasicException("Error exchanging code for token");
63+
}
64+
65+
$token = OAuth2AccessToken::createFromResponse($this->getApiBaseUrl(), (array)$response);
66+
$token->persist();
67+
$this->token = $token;
68+
69+
return $token;
70+
}
71+
72+
/**
73+
* Refresh the OAuth2 token
74+
*
75+
* @return OAuth2AccessToken
76+
* @throws BasicException
77+
* @throws GuzzleException
78+
*/
79+
protected function refreshToken(): OAuth2AccessToken
80+
{
81+
if (!$this->token || !$this->token->getRefreshToken()) {
82+
throw new BasicException('Cannot refresh OAuth2 token without refresh token.');
83+
}
84+
85+
$response = $this->getUtils()->httpRequest($this->getAuthUrl() . '/token', 'POST', [
86+
'form_params' => [
87+
'grant_type' => 'refresh_token',
88+
'refresh_token' => $this->token->getRefreshToken(),
89+
'client_id' => $this->clientId,
90+
'client_secret' => $this->clientSecret,
91+
],
92+
]);
93+
94+
$newToken = OAuth2AccessToken::createFromResponse($this->getApiBaseUrl(), (array)$response);
95+
$newToken->persist();
96+
97+
return $this->token = $newToken;
98+
}
99+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace App\Base\Abstracts\OAuth2;
4+
5+
use App\Base\Models\OAuth2AccessToken;
6+
use Degami\Basics\Exceptions\BasicException;
7+
use GuzzleHttp\Exception\GuzzleException;
8+
9+
abstract class ClientCredentialsOAuth2Client extends AbstractOAuth2ApiClient
10+
{
11+
/**
12+
* Get the OAuth2 token
13+
*
14+
* @return OAuth2AccessToken
15+
* @throws BasicException
16+
* @throws GuzzleException
17+
*/
18+
protected function getToken(): OAuth2AccessToken
19+
{
20+
if ($this->token && new \DateTime("".$this->token->getExpitesAt()) > new \DateTime()) {
21+
return $this->token;
22+
}
23+
24+
/** @var OAuth2AccessToken $stored */
25+
$stored = OAuth2AccessToken::getCollection()->where(['api_base_url' => $this->getApiBaseUrl()])->getFirst();
26+
27+
if ($stored && new \DateTime("".$stored->getExpitesAt()) > new \DateTime()) {
28+
return $this->token = $stored;
29+
}
30+
31+
$response = $this->getUtils()->httpRequest($this->authUrl, 'POST', [
32+
'form_params' => [
33+
'grant_type' => 'client_credentials',
34+
'client_id' => $this->clientId,
35+
'client_secret' => $this->clientSecret,
36+
'scope' => $this->scope,
37+
],
38+
]);
39+
40+
if (!isset($response->access_token)) {
41+
throw new BasicException("Can't gain OAuth2 Token from {$this->authUrl}");
42+
}
43+
44+
$token = OAuth2AccessToken::createFromResponse($this->getApiBaseUrl(), (array)$response);
45+
$token->persist();
46+
47+
return $this->token = $token;
48+
}
49+
}

0 commit comments

Comments
 (0)