|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Bmatovu\AirtelMoney\Auth; |
|
4
|
|
|
|
|
5
|
|
|
use Bmatovu\AirtelMoney\Auth\GrantTypes\GrantTypeInterface; |
|
6
|
|
|
use Bmatovu\AirtelMoney\Auth\Repositories\TokenRepositoryInterface; |
|
7
|
|
|
use Bmatovu\AirtelMoney\Support\Util; |
|
8
|
|
|
use GuzzleHttp\Promise\RejectedPromise; |
|
9
|
|
|
use Illuminate\Database\Eloquent\Model; |
|
10
|
|
|
use Psr\Http\Message\RequestInterface; |
|
11
|
|
|
|
|
12
|
|
|
class OAuth2Middleware |
|
13
|
|
|
{ |
|
14
|
|
|
protected ?Model $token = null; |
|
15
|
|
|
|
|
16
|
|
|
protected TokenRepositoryInterface $tokenRepository; |
|
17
|
|
|
|
|
18
|
|
|
protected GrantTypeInterface $grantType; |
|
19
|
|
|
|
|
20
|
|
|
protected ?GrantTypeInterface $refreshTokenGrantType; |
|
21
|
|
|
|
|
22
|
6 |
|
public function getToken(): ?Model |
|
23
|
|
|
{ |
|
24
|
|
|
// If token is not set try to get it from the persistent storage. |
|
25
|
6 |
|
if ($this->token === null) { |
|
26
|
6 |
|
$this->token = $this->tokenRepository->retrieve(); |
|
27
|
|
|
} |
|
28
|
|
|
|
|
29
|
|
|
// If storage token is not set or expired then try to acquire a new one... |
|
30
|
6 |
|
if ($this->token === null || Util::isExpired($this->token->expires_at)) { |
|
31
|
|
|
|
|
32
|
|
|
// Hydrate `rawToken` with a new access token |
|
33
|
4 |
|
$this->token = $this->requestNewToken(); |
|
34
|
|
|
} |
|
35
|
|
|
|
|
36
|
5 |
|
return $this->token; |
|
37
|
|
|
} |
|
38
|
|
|
|
|
39
|
8 |
|
public function __construct( |
|
40
|
|
|
GrantTypeInterface $grantType, |
|
41
|
|
|
?GrantTypeInterface $refreshTokenGrantType = null, |
|
42
|
|
|
?TokenRepositoryInterface $tokenRepository = null |
|
43
|
|
|
) { |
|
44
|
8 |
|
$this->tokenRepository = $tokenRepository; |
|
45
|
8 |
|
$this->grantType = $grantType; |
|
46
|
8 |
|
$this->refreshTokenGrantType = $refreshTokenGrantType; |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
8 |
|
public function __invoke(callable $handler): \Closure |
|
50
|
|
|
{ |
|
51
|
8 |
|
return function (RequestInterface $request, array $options) use ($handler) { |
|
52
|
8 |
|
if (! $request->hasHeader('Authorization')) { |
|
53
|
6 |
|
$request = $this->signRequest($request, $this->getToken()); |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
7 |
|
return $handler($request, $options)->then( |
|
57
|
7 |
|
$this->onFulfilled($request, $options, $handler), |
|
58
|
7 |
|
$this->onRejected($request, $options, $handler) |
|
59
|
7 |
|
); |
|
60
|
8 |
|
}; |
|
61
|
|
|
} |
|
62
|
|
|
|
|
63
|
7 |
|
private function onFulfilled(RequestInterface $request, array $options, callable $handler): \Closure |
|
64
|
|
|
{ |
|
65
|
7 |
|
return function ($response) use ($request, $options, $handler) { |
|
66
|
|
|
// Only deal with Unauthorized response. |
|
67
|
6 |
|
if ($response && $response->getStatusCode() != 401) { |
|
68
|
4 |
|
return $response; |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
// If we already retried once, give up. |
|
72
|
2 |
|
if ($request->hasHeader('X-Guzzle-Retry')) { |
|
73
|
1 |
|
return $response; |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
// Delete the previous access token, if any |
|
77
|
1 |
|
$this->tokenRepository->delete($this->token->access_token); |
|
78
|
|
|
|
|
79
|
|
|
// Unset current token |
|
80
|
1 |
|
$this->token = null; |
|
81
|
|
|
|
|
82
|
|
|
// Acquire a new access token, and retry the request. |
|
83
|
1 |
|
$this->token = $this->getToken(); |
|
84
|
|
|
|
|
85
|
1 |
|
$request = $request->withHeader('X-Guzzle-Retry', '1'); |
|
86
|
|
|
|
|
87
|
1 |
|
$request = $this->signRequest($request, $this->token); |
|
88
|
|
|
|
|
89
|
1 |
|
return $handler($request, $options); |
|
90
|
7 |
|
}; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
7 |
|
private function onRejected(RequestInterface $request, array $options, callable $handler): \Closure |
|
|
|
|
|
|
94
|
|
|
{ |
|
95
|
7 |
|
return function ($reason) { |
|
96
|
1 |
|
return new RejectedPromise($reason); |
|
97
|
7 |
|
}; |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
5 |
|
protected function signRequest(RequestInterface $request, Model $token): RequestInterface |
|
101
|
|
|
{ |
|
102
|
5 |
|
$authorization = $token->token_type.' '.$token->access_token; |
|
103
|
|
|
|
|
104
|
5 |
|
return $request->withHeader('Authorization', $authorization); |
|
|
|
|
|
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
4 |
|
protected function requestNewToken(): Model |
|
108
|
|
|
{ |
|
109
|
|
|
// Refresh an existing, but expired access token. |
|
110
|
4 |
|
if ($this->refreshTokenGrantType && $this->token && $this->token->refresh_token) { |
|
111
|
|
|
// Request new access token using the existing refresh token. |
|
112
|
1 |
|
$api_token = $this->refreshTokenGrantType->getToken($this->token->refresh_token); |
|
113
|
|
|
|
|
114
|
1 |
|
return $this->tokenRepository->create($api_token); |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
// Obtain new access token using the main grant type. |
|
118
|
3 |
|
$api_token = $this->grantType->getToken(); |
|
119
|
|
|
|
|
120
|
2 |
|
return $this->tokenRepository->create($api_token); |
|
121
|
|
|
} |
|
122
|
|
|
} |
|
123
|
|
|
|
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.