Passed
Pull Request — master (#1122)
by Sebastian
02:07
created

OAuthServerException::invalidRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
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 Exception;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\ServerRequestInterface;
15
use Throwable;
16
17
class OAuthServerException extends Exception
18
{
19
    /**
20
     * @var int
21
     */
22
    private $httpStatusCode;
23
24
    /**
25
     * @var string
26
     */
27
    private $errorType;
28
29
    /**
30
     * @var null|string
31
     */
32
    private $hint;
33
34
    /**
35
     * @var null|string
36
     */
37
    private $redirectUri;
38
39
    /**
40
     * @var array
41
     */
42
    private $payload;
43
44
    /**
45
     * @var ServerRequestInterface
46
     */
47
    private $serverRequest;
48
49
    /**
50
     * Throw a new exception.
51
     *
52
     * @param string      $message        Error message
53
     * @param int         $code           Error code
54
     * @param string      $errorType      Error type
55
     * @param int         $httpStatusCode HTTP status code to send (default = 400)
56
     * @param null|string $hint           A helper hint
57
     * @param null|string $redirectUri    A HTTP URI to redirect the user back to
58
     * @param Throwable   $previous       Previous exception
59
     */
60 73
    public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, Throwable $previous = null)
61
    {
62 73
        parent::__construct($message, $code, $previous);
63 73
        $this->httpStatusCode = $httpStatusCode;
64 73
        $this->errorType = $errorType;
65 73
        $this->hint = $hint;
66 73
        $this->redirectUri = $redirectUri;
67 73
        $this->payload = [
68 73
            'error'             => $errorType,
69 73
            'error_description' => $message,
70
        ];
71 73
        if ($hint !== null) {
72 47
            $this->payload['hint'] = $hint;
73
        }
74 73
    }
75
76
    /**
77
     * Returns the current payload.
78
     *
79
     * @return array
80
     */
81 7
    public function getPayload()
82
    {
83 7
        $payload = $this->payload;
84
85
        // The "message" property is deprecated and replaced by "error_description"
86
        // TODO: remove "message" property
87 7
        if (isset($payload['error_description']) && !isset($payload['message'])) {
88 7
            $payload['message'] = $payload['error_description'];
89
        }
90
91 7
        return $payload;
92
    }
93
94
    /**
95
     * Updates the current payload.
96
     *
97
     * @param array $payload
98
     */
99
    public function setPayload(array $payload)
100
    {
101
        $this->payload = $payload;
102
    }
103
104
    /**
105
     * Set the server request that is responsible for generating the exception
106
     *
107
     * @param ServerRequestInterface $serverRequest
108
     */
109 15
    public function setServerRequest(ServerRequestInterface $serverRequest)
110
    {
111 15
        $this->serverRequest = $serverRequest;
112 15
    }
113
114
    /**
115
     * Unsupported grant type error.
116
     *
117
     * @return static
118
     */
119 2
    public static function unsupportedGrantType()
120
    {
121 2
        $errorMessage = 'The authorization grant type is not supported by the authorization server.';
122 2
        $hint = 'Check that all required parameters have been provided';
123
124 2
        return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
125
    }
126
127
    /**
128
     * Invalid request error.
129
     *
130
     * @param string      $parameter The invalid parameter
131
     * @param null|string $hint
132
     * @param Throwable   $previous  Previous exception
133
     *
134
     * @return static
135
     */
136 23
    public static function invalidRequest($parameter, $hint = null, Throwable $previous = null)
137
    {
138
        $errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
139 23
            'includes a parameter more than once, or is otherwise malformed.';
140 23
        $hint = ($hint === null) ? \sprintf('Check the `%s` parameter', $parameter) : $hint;
141
142 23
        return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
143
    }
144
145
    /**
146
     * Invalid client error.
147
     *
148
     * @param ServerRequestInterface $serverRequest
149
     *
150
     * @return static
151
     */
152 15
    public static function invalidClient(ServerRequestInterface $serverRequest)
153
    {
154 15
        $exception = new static('Client authentication failed', 4, 'invalid_client', 401);
155
156 15
        $exception->setServerRequest($serverRequest);
157
158 15
        return $exception;
159
    }
160
161
    /**
162
     * Invalid scope error.
163
     *
164
     * @param string      $scope       The bad scope
165
     * @param null|string $redirectUri A HTTP URI to redirect the user back to
166
     *
167
     * @return static
168
     */
169 4
    public static function invalidScope($scope, $redirectUri = null)
170
    {
171 4
        $errorMessage = 'The requested scope is invalid, unknown, or malformed';
172
173 4
        if (empty($scope)) {
174
            $hint = 'Specify a scope in the request or set a default scope';
175
        } else {
176 4
            $hint = \sprintf(
177 4
                'Check the `%s` scope',
178 4
                \htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
179
            );
180
        }
181
182 4
        return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri);
183
    }
184
185
    /**
186
     * Invalid credentials error.
187
     *
188
     * @return static
189
     */
190
    public static function invalidCredentials()
191
    {
192
        return new static('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
193
    }
194
195
    /**
196
     * Server error.
197
     *
198
     * @param string    $hint
199
     * @param Throwable $previous
200
     *
201
     * @return static
202
     *
203
     * @codeCoverageIgnore
204
     */
205
    public static function serverError($hint, Throwable $previous = null)
206
    {
207
        return new static(
208
            'The authorization server encountered an unexpected condition which prevented it from fulfilling'
209
            . ' the request: ' . $hint,
210
            7,
211
            'server_error',
212
            500,
213
            null,
214
            null,
215
            $previous
216
        );
217
    }
218
219
    /**
220
     * Invalid refresh token.
221
     *
222
     * @param null|string $hint
223
     * @param Throwable   $previous
224
     *
225
     * @return static
226
     */
227 4
    public static function invalidRefreshToken($hint = null, Throwable $previous = null)
228
    {
229 4
        return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint, null, $previous);
230
    }
231
232
    /**
233
     * Access denied.
234
     *
235
     * @param null|string $hint
236
     * @param null|string $redirectUri
237
     * @param Throwable   $previous
238
     *
239
     * @return static
240
     */
241 14
    public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null)
242
    {
243 14
        return new static(
244 14
            'The resource owner or authorization server denied the request.',
245 14
            9,
246 14
            'access_denied',
247 14
            401,
248
            $hint,
249
            $redirectUri,
250
            $previous
251
        );
252
    }
253
254
    /**
255
     * Invalid grant.
256
     *
257
     * @param string $hint
258
     *
259
     * @return static
260
     */
261 2
    public static function invalidGrant($hint = '')
262
    {
263 2
        return new static(
264 2
            'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token '
265
                . 'is invalid, expired, revoked, does not match the redirection URI used in the authorization request, '
266 2
                . 'or was issued to another client.',
267 2
            10,
268 2
            'invalid_grant',
269 2
            400,
270
            $hint
271
        );
272
    }
273
274
    /**
275
     * @return string
276
     */
277 2
    public function getErrorType()
278
    {
279 2
        return $this->errorType;
280
    }
281
282
    /**
283
     * Generate a HTTP response.
284
     *
285
     * @param ResponseInterface $response
286
     * @param bool              $useFragment True if errors should be in the URI fragment instead of query string
287
     * @param int               $jsonOptions options passed to json_encode
288
     *
289
     * @return ResponseInterface
290
     */
291 7
    public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
292
    {
293 7
        $headers = $this->getHttpHeaders();
294
295 7
        $payload = $this->getPayload();
296
297 7
        if ($this->redirectUri !== null) {
298 2
            if ($useFragment === true) {
299 1
                $this->redirectUri .= (\strstr($this->redirectUri, '#') === false) ? '#' : '&';
300
            } else {
301 1
                $this->redirectUri .= (\strstr($this->redirectUri, '?') === false) ? '?' : '&';
302
            }
303
304 2
            return $response->withStatus(302)->withHeader('Location', $this->redirectUri . \http_build_query($payload));
305
        }
306
307 5
        foreach ($headers as $header => $content) {
308 5
            $response = $response->withHeader($header, $content);
309
        }
310
311 5
        $responseBody = \json_encode($payload, $jsonOptions) ?: 'JSON encoding of payload failed';
312
313 5
        $response->getBody()->write($responseBody);
314
315 5
        return $response->withStatus($this->getHttpStatusCode());
316
    }
317
318
    /**
319
     * Get all headers that have to be send with the error response.
320
     *
321
     * @return array Array with header values
322
     */
323 7
    public function getHttpHeaders()
324
    {
325
        $headers = [
326 7
            'Content-type' => 'application/json',
327
        ];
328
329
        // Add "WWW-Authenticate" header
330
        //
331
        // RFC 6749, section 5.2.:
332
        // "If the client attempted to authenticate via the 'Authorization'
333
        // request header field, the authorization server MUST
334
        // respond with an HTTP 401 (Unauthorized) status code and
335
        // include the "WWW-Authenticate" response header field
336
        // matching the authentication scheme used by the client.
337
        // @codeCoverageIgnoreStart
338
        if ($this->errorType === 'invalid_client' && $this->serverRequest->hasHeader('Authorization') === true) {
339
            $authScheme = \strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic';
340
341
            $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
342
        }
343
        // @codeCoverageIgnoreEnd
344 7
        return $headers;
345
    }
346
347
    /**
348
     * Check if the exception has an associated redirect URI.
349
     *
350
     * Returns whether the exception includes a redirect, since
351
     * getHttpStatusCode() doesn't return a 302 when there's a
352
     * redirect enabled. This helps when you want to override local
353
     * error pages but want to let redirects through.
354
     *
355
     * @return bool
356
     */
357 2
    public function hasRedirect()
358
    {
359 2
        return $this->redirectUri !== null;
360
    }
361
362
    /**
363
     * Returns the HTTP status code to send when the exceptions is output.
364
     *
365
     * @return int
366
     */
367 7
    public function getHttpStatusCode()
368
    {
369 7
        return $this->httpStatusCode;
370
    }
371
372
    /**
373
     * @return null|string
374
     */
375 15
    public function getHint()
376
    {
377 15
        return $this->hint;
378
    }
379
}
380