vendor/league/oauth2-server/src/Exception/OAuthServerException.php line 243

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