Skip to content

Commit a1e42ed

Browse files
authored
[6.x] Credential managers (foxbytehq#116)
Refactor storage mechanism to more generic credential managers
1 parent 0a720d2 commit a1e42ed

19 files changed

+524
-753
lines changed

src/Exceptions/OAuthException.php

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,6 @@
22

33
namespace Webfox\Xero\Exceptions;
44

5-
use Exception;
6-
use Throwable;
7-
8-
class OAuthException extends Exception
5+
class OAuthException extends XeroException
96
{
10-
public function __construct($message, $code = 0, Throwable $previous = null)
11-
{
12-
parent::__construct($message, $code, $previous);
13-
}
14-
15-
public function __toString()
16-
{
17-
return __CLASS__.": [{$this->code}]: {$this->message}\n";
18-
}
197
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Webfox\Xero\Exceptions;
4+
5+
class XeroCredentialsNotFound extends XeroException
6+
{
7+
}

src/Exceptions/XeroException.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Webfox\Xero\Exceptions;
4+
5+
use Exception;
6+
use Throwable;
7+
8+
abstract class XeroException extends Exception
9+
{
10+
public function __construct($message, $code = 0, Throwable $previous = null)
11+
{
12+
parent::__construct($message, $code, $previous);
13+
}
14+
15+
public function __toString()
16+
{
17+
return __CLASS__.": [{$this->code}]: {$this->message}\n";
18+
}
19+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Webfox\Xero\Exceptions;
4+
5+
class XeroTenantNotFound extends XeroException
6+
{
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Webfox\Xero\Exceptions;
4+
5+
class XeroUserNotAuthenticated extends XeroException
6+
{
7+
}

src/Oauth2CredentialManagers/ArrayStore.php

Lines changed: 3 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2,86 +2,19 @@
22

33
namespace Webfox\Xero\Oauth2CredentialManagers;
44

5-
use Illuminate\Session\Store;
65
use League\OAuth2\Client\Token\AccessTokenInterface;
7-
use Webfox\Xero\Oauth2Provider;
6+
use Webfox\Xero\Exceptions\XeroCredentialsNotFound;
87
use Webfox\Xero\OauthCredentialManager;
98

10-
class ArrayStore implements OauthCredentialManager
9+
class ArrayStore extends BaseCredentialManager implements OauthCredentialManager
1110
{
1211
public ?array $dataStorage = null;
1312

14-
public function __construct(protected Store $session, protected Oauth2Provider $oauthProvider)
15-
{
16-
}
17-
18-
public function getAccessToken(): string
19-
{
20-
return $this->data('token');
21-
}
22-
23-
public function getRefreshToken(): string
24-
{
25-
return $this->data('refresh_token');
26-
}
27-
28-
public function getTenants(): ?array
29-
{
30-
return $this->data('tenants');
31-
}
32-
33-
public function getTenantId(int $tenant = 0): string
34-
{
35-
if (! isset($this->data('tenants')[$tenant])) {
36-
throw new \Exception('No such tenant exists');
37-
}
38-
39-
return $this->data('tenants')[$tenant]['Id'];
40-
}
41-
42-
public function getExpires(): int
43-
{
44-
return $this->data('expires');
45-
}
46-
47-
public function getState(): string
48-
{
49-
return $this->session->get('xero_oauth2_state') ?? '';
50-
}
51-
52-
public function getAuthorizationUrl(): string
53-
{
54-
$redirectUrl = $this->oauthProvider->getAuthorizationUrl(['scope' => config('xero.oauth.scopes')]);
55-
$this->session->put('xero_oauth2_state', $this->oauthProvider->getState());
56-
57-
return $redirectUrl;
58-
}
59-
60-
public function getData(): array
61-
{
62-
return $this->data();
63-
}
64-
6513
public function exists(): bool
6614
{
6715
return $this->dataStorage !== null;
6816
}
6917

70-
public function isExpired(): bool
71-
{
72-
return time() >= $this->data('expires');
73-
}
74-
75-
public function refresh(): void
76-
{
77-
$newAccessToken = $this->oauthProvider->getAccessToken('refresh_token', [
78-
'grant_type' => 'refresh_token',
79-
'refresh_token' => $this->getRefreshToken(),
80-
]);
81-
82-
$this->store($newAccessToken);
83-
}
84-
8518
public function store(AccessTokenInterface $token, array $tenants = null): void
8619
{
8720
$this->dataStorage = [
@@ -93,30 +26,10 @@ public function store(AccessTokenInterface $token, array $tenants = null): void
9326
];
9427
}
9528

96-
public function getUser(): ?array
97-
{
98-
try {
99-
$jwt = new \XeroAPI\XeroPHP\JWTClaims();
100-
$jwt->setTokenId($this->data('id_token'));
101-
$decodedToken = $jwt->decode();
102-
103-
return [
104-
'given_name' => $decodedToken->getGivenName(),
105-
'family_name' => $decodedToken->getFamilyName(),
106-
'email' => $decodedToken->getEmail(),
107-
'user_id' => $decodedToken->getXeroUserId(),
108-
'username' => $decodedToken->getPreferredUsername(),
109-
'session_id' => $decodedToken->getGlobalSessionId(),
110-
];
111-
} catch (\Throwable $e) {
112-
return null;
113-
}
114-
}
115-
11629
protected function data(string $key = null)
11730
{
11831
if (! $this->exists()) {
119-
throw new \Exception('Xero oauth credentials are missing');
32+
throw new XeroCredentialsNotFound('Xero oauth credentials are missing');
12033
}
12134

12235
return $key === null ? $this->dataStorage : $this->dataStorage[$key] ?? null;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Webfox\Xero\Oauth2CredentialManagers;
4+
5+
use Illuminate\Support\Facades\Auth;
6+
use Webfox\Xero\Exceptions\XeroUserNotAuthenticated;
7+
8+
class AuthenticatedUserStore extends ModelStore
9+
{
10+
public function __construct()
11+
{
12+
if (! Auth::check()) {
13+
throw new XeroUserNotAuthenticated('User is not authenticated');
14+
}
15+
16+
parent::__construct();
17+
18+
$this->model = Auth::user();
19+
}
20+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
namespace Webfox\Xero\Oauth2CredentialManagers;
4+
5+
use Illuminate\Session\Store;
6+
use Webfox\Xero\Exceptions\XeroTenantNotFound;
7+
use Webfox\Xero\Oauth2Provider;
8+
use XeroAPI\XeroPHP\JWTClaims;
9+
10+
abstract class BaseCredentialManager
11+
{
12+
protected Store $session;
13+
protected Oauth2Provider $oauthProvider;
14+
15+
public function __construct()
16+
{
17+
$this->session = app(Store::class);
18+
$this->oauthProvider = app(Oauth2Provider::class);
19+
}
20+
21+
abstract protected function data(string $key = null);
22+
23+
public function getAccessToken(): string
24+
{
25+
return $this->data('token');
26+
}
27+
28+
public function getRefreshToken(): string
29+
{
30+
return $this->data('refresh_token');
31+
}
32+
33+
public function getTenants(): ?array
34+
{
35+
return $this->data('tenants');
36+
}
37+
38+
public function getTenantId(int $tenant = 0): string
39+
{
40+
if (! isset($this->data('tenants')[$tenant])) {
41+
throw new XeroTenantNotFound('No such tenant exists');
42+
}
43+
44+
return $this->data('tenants')[$tenant]['Id'];
45+
}
46+
47+
public function getExpires(): int
48+
{
49+
return $this->data('expires');
50+
}
51+
52+
public function getData(): array
53+
{
54+
return $this->data();
55+
}
56+
57+
public function getUser(): ?array
58+
{
59+
try {
60+
$jwt = new JWTClaims();
61+
$jwt->setTokenId($this->data('id_token'));
62+
$decodedToken = $jwt->decode();
63+
64+
return [
65+
'given_name' => $decodedToken->getGivenName(),
66+
'family_name' => $decodedToken->getFamilyName(),
67+
'email' => $decodedToken->getEmail(),
68+
'user_id' => $decodedToken->getXeroUserId(),
69+
'username' => $decodedToken->getPreferredUsername(),
70+
'session_id' => $decodedToken->getGlobalSessionId(),
71+
];
72+
} catch (\Throwable $e) {
73+
return null;
74+
}
75+
}
76+
77+
public function isExpired(): bool
78+
{
79+
return time() >= $this->data('expires');
80+
}
81+
82+
public function refresh(): void
83+
{
84+
$newAccessToken = $this->oauthProvider->getAccessToken('refresh_token', [
85+
'grant_type' => 'refresh_token',
86+
'refresh_token' => $this->getRefreshToken(),
87+
]);
88+
89+
$this->store($newAccessToken);
90+
}
91+
92+
public function getState(): string
93+
{
94+
return $this->session->get('xero_oauth2_state') ?? '';
95+
}
96+
97+
public function getAuthorizationUrl(): string
98+
{
99+
$redirectUrl = $this->oauthProvider->getAuthorizationUrl(['scope' => config('xero.oauth.scopes')]);
100+
$this->session->put('xero_oauth2_state', $this->oauthProvider->getState());
101+
102+
return $redirectUrl;
103+
}
104+
}

0 commit comments

Comments
 (0)