Completed
Pull Request — master (#1006)
by Marc
03:42
created

OAuthServerException::getPayload()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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