Completed
Pull Request — master (#965)
by Marc
01:37
created

OAuthServerException   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 332
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Test Coverage

Coverage 94.94%

Importance

Changes 0
Metric Value
wmc 29
lcom 2
cbo 0
dl 0
loc 332
ccs 75
cts 79
cp 0.9494
rs 10
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 2
A getPayload() 0 4 1
A setPayload() 0 4 1
A unsupportedGrantType() 0 7 1
A invalidRequest() 0 8 2
A invalidClient() 0 6 1
A invalidScope() 0 15 2
A invalidCredentials() 0 4 1
A serverError() 0 11 1
A invalidRefreshToken() 0 4 1
A accessDenied() 0 12 1
A invalidGrant() 0 12 1
A getErrorType() 0 4 1
B generateHttpResponse() 0 24 6
A getHttpHeaders() 0 23 4
A hasRedirect() 0 4 1
A getHttpStatusCode() 0 4 1
A getHint() 0 4 1
1
<?php
2
/**
3
 * @author      Alex Bilbie <[email protected]>
4
 * @copyright   Copyright (c) Alex Bilbie
5
 * @license     http://mit-license.org/
6
 *
7
 * @link        https://github.com/thephpleague/oauth2-server
8
 */
9
10
namespace League\OAuth2\Server\Exception;
11
12
use Psr\Http\Message\ResponseInterface;
13
14
class OAuthServerException extends \Exception
15
{
16
    /**
17
     * @var int
18
     */
19
    private $httpStatusCode;
20
21
    /**
22
     * @var string
23
     */
24
    private $errorType;
25
26
    /**
27
     * @var null|string
28
     */
29
    private $hint;
30
31
    /**
32
     * @var null|string
33
     */
34
    private $redirectUri;
35
36
    /**
37
     * @var array
38
     */
39
    private $payload;
40
41
    /**
42
     * Throw a new exception.
43
     *
44
     * @param string      $message        Error message
45
     * @param int         $code           Error code
46
     * @param string      $errorType      Error type
47
     * @param int         $httpStatusCode HTTP status code to send (default = 400)
48
     * @param null|string $hint           A helper hint
49
     * @param null|string $redirectUri    A HTTP URI to redirect the user back to
50
     * @param \Throwable  $previous       Previous exception
51
     */
52 68
    public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, \Throwable $previous = null)
53
    {
54 68
        parent::__construct($message, $code, $previous);
55 68
        $this->httpStatusCode = $httpStatusCode;
56 68
        $this->errorType = $errorType;
57 68
        $this->hint = $hint;
58 68
        $this->redirectUri = $redirectUri;
59 68
        $this->payload = [
60 68
            'error'   => $errorType,
61 68
            'message' => $message,
62
        ];
63 68
        if ($hint !== null) {
64 45
            $this->payload['hint'] = $hint;
65
        }
66 68
    }
67
68
    /**
69
     * Returns the current payload.
70
     *
71
     * @return array
72
     */
73 5
    public function getPayload()
74
    {
75 5
        return $this->payload;
76
    }
77
78
    /**
79
     * Updates the current payload.
80
     *
81
     * @param array $payload
82
     */
83
    public function setPayload(array $payload)
84
    {
85
        $this->payload = $payload;
86
    }
87
88
    /**
89
     * Unsupported grant type error.
90
     *
91
     * @return static
92
     */
93 2
    public static function unsupportedGrantType()
94
    {
95 2
        $errorMessage = 'The authorization grant type is not supported by the authorization server.';
96 2
        $hint = 'Check that all required parameters have been provided';
97
98 2
        return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
99
    }
100
101
    /**
102
     * Invalid request error.
103
     *
104
     * @param string      $parameter The invalid parameter
105
     * @param null|string $hint
106
     * @param \Throwable  $previous Previous exception
107
     *
108
     * @return static
109
     */
110 22
    public static function invalidRequest($parameter, $hint = null, \Throwable $previous = null)
111
    {
112
        $errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
113 22
            'includes a parameter more than once, or is otherwise malformed.';
114 22
        $hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
115
116 22
        return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
117
    }
118
119
    /**
120
     * Invalid client error.
121
     *
122
     * @return static
123
     */
124 13
    public static function invalidClient()
125
    {
126 13
        $errorMessage = 'Client authentication failed';
127
128 13
        return new static($errorMessage, 4, 'invalid_client', 401);
129
    }
130
131
    /**
132
     * Invalid scope error.
133
     *
134
     * @param string      $scope       The bad scope
135
     * @param null|string $redirectUri A HTTP URI to redirect the user back to
136
     *
137
     * @return static
138
     */
139 4
    public static function invalidScope($scope, $redirectUri = null)
140
    {
141 4
        $errorMessage = 'The requested scope is invalid, unknown, or malformed';
142
143 4
        if (empty($scope)) {
144
            $hint = 'Specify a scope in the request or set a default scope';
145
        } else {
146 4
            $hint = sprintf(
147 4
                'Check the `%s` scope',
148 4
                htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
149
            );
150
        }
151
152 4
        return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri);
153
    }
154
155
    /**
156
     * Invalid credentials error.
157
     *
158
     * @return static
159
     */
160 1
    public static function invalidCredentials()
161
    {
162 1
        return new static('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
163
    }
164
165
    /**
166
     * Server error.
167
     *
168
     * @param string     $hint
169
     * @param \Throwable $previous
170
     *
171
     * @return static
172
     *
173
     * @codeCoverageIgnore
174
     */
175
    public static function serverError($hint, \Throwable $previous = null)
176
    {
177
        return new static(
178
            'The authorization server encountered an unexpected condition which prevented it from fulfilling'
179
            . ' the request: ' . $hint,
180
            7,
181
            'server_error',
182
            500,
183
            $previous
0 ignored issues
show
Bug introduced by
It seems like $previous defined by parameter $previous on line 175 can also be of type object<Throwable>; however, League\OAuth2\Server\Exc...xception::__construct() does only seem to accept null|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
184
        );
185
    }
186
187
    /**
188
     * Invalid refresh token.
189
     *
190
     * @param null|string $hint
191
     * @param \Throwable  $previous
192
     *
193
     * @return static
194
     */
195 4
    public static function invalidRefreshToken($hint = null, \Throwable $previous = null)
196
    {
197 4
        return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint, null, $previous);
198
    }
199
200
    /**
201
     * Access denied.
202
     *
203
     * @param null|string $hint
204
     * @param null|string $redirectUri
205
     * @param \Throwable  $previous
206
     *
207
     * @return static
208
     */
209 12
    public static function accessDenied($hint = null, $redirectUri = null, \Throwable $previous = null)
210
    {
211 12
        return new static(
212 12
            'The resource owner or authorization server denied the request.',
213 12
            9,
214 12
            'access_denied',
215 12
            401,
216 12
            $hint,
217 12
            $redirectUri,
218 12
            $previous
219
        );
220
    }
221
222
    /**
223
     * Invalid grant.
224
     *
225
     * @param string $hint
226
     *
227
     * @return static
228
     */
229 1
    public static function invalidGrant($hint = '')
230
    {
231 1
        return new static(
232
            'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token '
233
                . 'is invalid, expired, revoked, does not match the redirection URI used in the authorization request, '
234 1
                . 'or was issued to another client.',
235 1
            10,
236 1
            'invalid_grant',
237 1
            400,
238 1
            $hint
239
        );
240
    }
241
242
    /**
243
     * @return string
244
     */
245 2
    public function getErrorType()
246
    {
247 2
        return $this->errorType;
248
    }
249
250
    /**
251
     * Generate a HTTP response.
252
     *
253
     * @param ResponseInterface $response
254
     * @param bool              $useFragment True if errors should be in the URI fragment instead of query string
255
     * @param int               $jsonOptions options passed to json_encode
256
     *
257
     * @return ResponseInterface
258
     */
259 5
    public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
260
    {
261 5
        $headers = $this->getHttpHeaders();
262
263 5
        $payload = $this->getPayload();
264
265 5
        if ($this->redirectUri !== null) {
266 2
            if ($useFragment === true) {
267 1
                $this->redirectUri .= (strstr($this->redirectUri, '#') === false) ? '#' : '&';
268
            } else {
269 1
                $this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&';
270
            }
271
272 2
            return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload));
273
        }
274
275 3
        foreach ($headers as $header => $content) {
276 3
            $response = $response->withHeader($header, $content);
277
        }
278
279 3
        $response->getBody()->write(json_encode($payload, $jsonOptions));
280
281 3
        return $response->withStatus($this->getHttpStatusCode());
282
    }
283
284
    /**
285
     * Get all headers that have to be send with the error response.
286
     *
287
     * @return array Array with header values
288
     */
289 5
    public function getHttpHeaders()
290
    {
291
        $headers = [
292 5
            'Content-type' => 'application/json',
293
        ];
294
295
        // Add "WWW-Authenticate" header
296
        //
297
        // RFC 6749, section 5.2.:
298
        // "If the client attempted to authenticate via the 'Authorization'
299
        // request header field, the authorization server MUST
300
        // respond with an HTTP 401 (Unauthorized) status code and
301
        // include the "WWW-Authenticate" response header field
302
        // matching the authentication scheme used by the client.
303
        // @codeCoverageIgnoreStart
304
        if ($this->errorType === 'invalid_client' && array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false) {
305
            $authScheme = strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0 ? 'Bearer' : 'Basic';
306
307
            $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
308
        }
309
        // @codeCoverageIgnoreEnd
310 5
        return $headers;
311
    }
312
313
    /**
314
     * Check if the exception has an associated redirect URI.
315
     *
316
     * Returns whether the exception includes a redirect, since
317
     * getHttpStatusCode() doesn't return a 302 when there's a
318
     * redirect enabled. This helps when you want to override local
319
     * error pages but want to let redirects through.
320
     *
321
     * @return bool
322
     */
323 2
    public function hasRedirect()
324
    {
325 2
        return $this->redirectUri !== null;
326
    }
327
328
    /**
329
     * Returns the HTTP status code to send when the exceptions is output.
330
     *
331
     * @return int
332
     */
333 5
    public function getHttpStatusCode()
334
    {
335 5
        return $this->httpStatusCode;
336
    }
337
338
    /**
339
     * @return null|string
340
     */
341 14
    public function getHint()
342
    {
343 14
        return $this->hint;
344
    }
345
}
346