Completed
Push — master ( 340d1a...50770c )
by Andrew
24s queued 10s
created

OAuthServerException::getRedirectUri()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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 75
    public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, Throwable $previous = null)
61
    {
62 75
        parent::__construct($message, $code, $previous);
63 75
        $this->httpStatusCode = $httpStatusCode;
64 75
        $this->errorType = $errorType;
65 75
        $this->hint = $hint;
66 75
        $this->redirectUri = $redirectUri;
67 75
        $this->payload = [
68 75
            'error'             => $errorType,
69 75
            'error_description' => $message,
70
        ];
71 75
        if ($hint !== null) {
72 49
            $this->payload['hint'] = $hint;
73
        }
74 75
    }
75
76
    /**
77
     * Returns the current payload.
78
     *
79
     * @return array
80
     */
81 8
    public function getPayload()
82
    {
83 8
        $payload = $this->payload;
84
85
        // The "message" property is deprecated and replaced by "error_description"
86
        // TODO: remove "message" property
87 8
        if (isset($payload['error_description']) && !isset($payload['message'])) {
88 8
            $payload['message'] = $payload['error_description'];
89
        }
90
91 8
        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 5
    public static function invalidScope($scope, $redirectUri = null)
170
    {
171 5
        $errorMessage = 'The requested scope is invalid, unknown, or malformed';
172
173 5
        if (empty($scope)) {
174
            $hint = 'Specify a scope in the request or set a default scope';
175
        } else {
176 5
            $hint = \sprintf(
177 5
                'Check the `%s` scope',
178 5
                \htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
179
            );
180
        }
181
182 5
        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 15
    public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null)
242
    {
243 15
        return new static(
244 15
            'The resource owner or authorization server denied the request.',
245 15
            9,
246 15
            'access_denied',
247 15
            401,
248 15
            $hint,
249 15
            $redirectUri,
250 15
            $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
            '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 2
            $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 8
    public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
292
    {
293 8
        $headers = $this->getHttpHeaders();
294
295 8
        $payload = $this->getPayload();
296
297 8
        if ($this->redirectUri !== null) {
298 3
            if ($useFragment === true) {
299 1
                $this->redirectUri .= (\strstr($this->redirectUri, '#') === false) ? '#' : '&';
300
            } else {
301 2
                $this->redirectUri .= (\strstr($this->redirectUri, '?') === false) ? '?' : '&';
302
            }
303
304 3
            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 8
    public function getHttpHeaders()
324
    {
325
        $headers = [
326 8
            '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 8
        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 Redirect URI used for redirecting.
364
     *
365
     * @return string|null
366
     */
367 1
    public function getRedirectUri()
368
    {
369 1
        return $this->redirectUri;
370
    }
371
372
    /**
373
     * Returns the HTTP status code to send when the exceptions is output.
374
     *
375
     * @return int
376
     */
377 7
    public function getHttpStatusCode()
378
    {
379 7
        return $this->httpStatusCode;
380
    }
381
382
    /**
383
     * @return null|string
384
     */
385 15
    public function getHint()
386
    {
387 15
        return $this->hint;
388
    }
389
}
390