Passed
Push — master ( 8278f2...2a258c )
by Alexandre
02:55
created

ResourceServer::verifyRequest()   F

Complexity

Conditions 19
Paths 822

Size

Total Lines 100
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 58
nc 822
nop 3
dl 0
loc 100
rs 2.3386
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: Alexandre
5
 * Date: 27/05/2018
6
 * Time: 17:46
7
 */
8
9
namespace OAuth2\Roles\ResourceServer;
10
11
12
use GuzzleHttp\Psr7\Response;
13
use OAuth2\Exceptions\OAuthException;
14
use OAuth2\Roles\ResourceServer\BearerAuthenticationMethods\AuthorizationRequestHeaderField;
15
use OAuth2\Roles\ResourceServer\BearerAuthenticationMethods\BearerAuthenticationMethodInterface;
16
use OAuth2\Roles\ResourceServerInterface;
17
use OAuth2\Storages\AccessTokenStorageInterface;
18
use OAuth2\Storages\ClientStorageInterface;
19
use OAuth2\Storages\ResourceOwnerStorageInterface;
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
23
class ResourceServer implements ResourceServerInterface
24
{
25
    /**
26
     * @var BearerAuthenticationMethodInterface[]
27
     */
28
    private $bearerAuthenticationMethods = [];
29
    /**
30
     * @var AccessTokenStorageInterface
31
     */
32
    private $accessTokenStorage;
33
    /**
34
     * @var ClientStorageInterface
35
     */
36
    private $clientStorage;
37
    /**
38
     * @var ResourceOwnerStorageInterface
39
     */
40
    private $resourceOwnerStorage;
41
42
    /**
43
     * @var AuthenticatedRequest|null
44
     */
45
    private $authenticatedRequest;
46
47
    public function __construct(AccessTokenStorageInterface $accessTokenStorage,
48
                                ClientStorageInterface $clientStorage,
49
                                ResourceOwnerStorageInterface $resourceOwnerStorage)
50
    {
51
        $this->bearerAuthenticationMethods = [
52
            /**
53
             * Resource servers MUST support this method
54
             * @see https://tools.ietf.org/html/rfc6750#section-2.1
55
             */
56
            new AuthorizationRequestHeaderField()
57
        ];
58
        $this->accessTokenStorage = $accessTokenStorage;
59
        $this->clientStorage = $clientStorage;
60
        $this->resourceOwnerStorage = $resourceOwnerStorage;
61
    }
62
63
    /**
64
     * @param ServerRequestInterface $request
65
     * @return null|ResponseInterface
66
     * @throws OAuthException
67
     */
68
    public function verifyRequest(ServerRequestInterface $request, array $requiredScopes, ?string $realm = null): ?ResponseInterface
69
    {
70
        try {
71
            $bearerAuthenticationMethodUsed = null;
72
            foreach ($this->bearerAuthenticationMethods as $bearerAuthenticationMethod) {
73
                if ($bearerAuthenticationMethod->support($request)) {
74
                    if ($bearerAuthenticationMethodUsed) {
75
                        throw new OAuthException('invalid_request',
76
                            'The request utilizes more than one mechanism for authenticating the client.',
77
                            'https://tools.ietf.org/html/rfc6749#section-3.2.1');
78
                    }
79
80
                    $bearerAuthenticationMethodUsed = $bearerAuthenticationMethod;
81
                }
82
            }
83
84
            /**
85
             * @see https://tools.ietf.org/html/rfc6750#section-3.1
86
             * If the request lacks any authentication information (e.g., the client
87
             * was unaware that authentication is necessary or attempted using an
88
             * unsupported authentication method), the resource server SHOULD NOT
89
             * include an error code or other error information.
90
             *
91
             * For example:
92
             *
93
             * HTTP/1.1 401 Unauthorized
94
             * WWW-Authenticate: Bearer realm="example"
95
             */
96
            if (!$bearerAuthenticationMethodUsed) {
97
                return new Response(401, ['WWW-Authenticate' => 'Bearer' . ($realm ? ' realm="example"' : '')]);
98
            }
99
100
            $token = $bearerAuthenticationMethodUsed->authenticate($request);
101
102
            if (!$token) {
103
                throw new OAuthException('invalid_request',
104
                    'The request is missing a required parameter, includes an unsupported parameter or parameter value',
105
                    'https://tools.ietf.org/html/rfc6750#section-3.1');
106
            }
107
108
            if (!$accessToken = $this->accessTokenStorage->get($token)) {
109
                throw new OAuthException('invalid_token',
110
                    'The access token provided is invalid.',
111
                    'https://tools.ietf.org/html/rfc6750#section-3.1');
112
            }
113
114
            if ($this->accessTokenStorage->hasExpired($accessToken)) {
115
                throw new OAuthException('invalid_token',
116
                    'The access token provided is expired.',
117
                    'https://tools.ietf.org/html/rfc6750#section-3.1');
118
            }
119
120
            if (!$client = $this->clientStorage->get($accessToken->getClientIdentifier())) {
121
                throw new OAuthException('invalid_token',
122
                    'The access token provided is invalid. Client not found.',
123
                    'https://tools.ietf.org/html/rfc6750#section-3.1');
124
            }
125
126
            $resourceOwner = null;
127
            if ($accessToken->getResourceOwnerIdentifier()) {
128
                if (!$resourceOwner = $this->resourceOwnerStorage->get($accessToken->getResourceOwnerIdentifier())) {
129
                    throw new OAuthException('invalid_token',
130
                        'The access token provided is invalid. Resource owner not found.',
131
                        'https://tools.ietf.org/html/rfc6750#section-3.1');
132
                }
133
            }
134
135
            if (!empty(array_diff($requiredScopes, $accessToken->getScopes()))) {
136
                throw new OAuthException('insufficient_scope',
137
                    'The request requires higher privileges than provided by the access token.',
138
                    'https://tools.ietf.org/html/rfc6750#section-3.1');
139
            }
140
        } catch (OAuthException $e) {
141
            switch ($e->getError()) {
142
                case 'invalid_token':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
143
                    $statusCode = 401;
144
                    break;
145
                case 'insufficient_scope':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
146
                    $statusCode = 403;
0 ignored issues
show
Unused Code introduced by
The assignment to $statusCode is dead and can be removed.
Loading history...
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
147
                default:
148
                    $statusCode = 400;
149
            }
150
151
            $header = 'Bearer';
152
            if ($realm) {
153
                $header .= ' realm="' . $realm . '"';
154
            }
155
            $header .= ' error="'.$e->getError().'"';
156
            if($e->getErrorDescription()) {
157
                $header .= ' error_description="'.$e->getErrorDescription().'"';
158
            }
159
            if($e->getErrorUri()) {
160
                $header .= ' error_uri="'.$e->getErrorUri().'"';
161
            }
162
163
            return new Response($statusCode, ['WWW-Authenticate' => $header]);
164
        }
165
166
        $this->authenticatedRequest = new AuthenticatedRequest($request, $client, $resourceOwner, $accessToken->getScopes());
167
        return null;
168
    }
169
170
    public function addBearerAuthenticationMethod(BearerAuthenticationMethodInterface $method): self
171
    {
172
        $this->bearerAuthenticationMethods[] = $method;
173
174
        return $this;
175
    }
176
177
    /**
178
     * @return AuthenticatedRequest|null
179
     */
180
    public function getAuthenticatedRequest(): ?AuthenticatedRequest
181
    {
182
        return $this->authenticatedRequest;
183
    }
184
}