OAuth2Middleware   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 109
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 39
c 1
b 0
f 0
dl 0
loc 109
rs 10
ccs 45
cts 45
cp 1
wmc 17

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getToken() 0 15 4
A onRejected() 0 4 1
A __construct() 0 8 1
A __invoke() 0 10 2
A signRequest() 0 5 1
A onFulfilled() 0 27 4
A requestNewToken() 0 14 4
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
0 ignored issues
show
Unused Code introduced by
The parameter $handler is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

93
    private function onRejected(RequestInterface $request, array $options, /** @scrutinizer ignore-unused */ callable $handler): \Closure

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

93
    private function onRejected(RequestInterface $request, /** @scrutinizer ignore-unused */ array $options, callable $handler): \Closure

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

93
    private function onRejected(/** @scrutinizer ignore-unused */ RequestInterface $request, array $options, callable $handler): \Closure

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $request->withHea...ation', $authorization) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\RequestInterface.
Loading history...
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