diff --git a/src/OAuth2/AuthServer.php b/src/OAuth2/AuthServer.php index 54fb7811..1e45f363 100644 --- a/src/OAuth2/AuthServer.php +++ b/src/OAuth2/AuthServer.php @@ -117,6 +117,87 @@ class AuthServer 'invalid_refresh' => 'The refresh token is invalid.', ); + /** + * Exception error HTTP status codes + * @var array + * + * RFC 6749, section 4.1.2.1.: + * No 503 status code for 'temporarily_unavailable', because + * "a 503 Service Unavailable HTTP status code cannot be + * returned to the client via an HTTP redirect" + */ + protected static $exceptionHttpStatusCodes = array( + 'invalid_request' => 400, + 'unauthorized_client' => 400, + 'access_denied' => 401, + 'unsupported_response_type' => 400, + 'invalid_scope' => 400, + 'server_error' => 500, + 'temporarily_unavailable' => 400, + 'unsupported_grant_type' => 501, + 'invalid_client' => 401, + 'invalid_grant' => 400, + 'invalid_credentials' => 400, + 'invalid_refresh' => 400, + ); + + /** + * Get all headers that have to be send with the error response + * + * @param string $error The error message key + * @return array Array with header values + */ + public static function getExceptionHttpHeaders($error) + { + $headers = array(); + switch (self::$exceptionHttpStatusCodes[$error]) { + case 401: + $headers[] = 'HTTP/1.1 401 Unauthorized'; + break; + case 500: + $headers[] = 'HTTP/1.1 500 Internal Server Error'; + break; + case 501: + $headers[] = 'HTTP/1.1 501 Not Implemented'; + break; + case 400: + default: + $headers[] = 'HTTP/1.1 400 Bad Request'; + } + + // Add "WWW-Authenticate" header + // + // RFC 6749, section 5.2.: + // "If the client attempted to authenticate via the 'Authorization' + // request header field, the authorization server MUST + // respond with an HTTP 401 (Unauthorized) status code and + // include the "WWW-Authenticate" response header field + // matching the authentication scheme used by the client. + // @codeCoverageIgnoreStart + if ($error === 'invalid_client') { + $authScheme = null; + $request = new Request(); + if ($request->server('PHP_AUTH_USER') !== null) { + $authScheme = 'Basic'; + } else { + $authHeader = $request->header('Authorization'); + if ($authHeader !== null) { + if (strpos($authHeader, 'Bearer') === 0) { + $authScheme = 'Bearer'; + } elseif (strpos($authHeader, 'Basic') === 0) { + $authScheme = 'Basic'; + } + } + } + if ($authScheme !== null) { + $headers[] = 'WWW-Authenticate: '.$authScheme.' realm=""'; + } + } + // @codeCoverageIgnoreEnd + + return $headers; + } + /** * Get an exception message * diff --git a/tests/authorization/AuthServerTest.php b/tests/authorization/AuthServerTest.php index 0158b887..8b4c7bbe 100644 --- a/tests/authorization/AuthServerTest.php +++ b/tests/authorization/AuthServerTest.php @@ -50,6 +50,14 @@ class Authorization_Server_test extends PHPUnit_Framework_TestCase $this->assertEquals('access_denied', OAuth2\AuthServer::getExceptionType(2)); } + public function test_getExceptionHttpHeaders() + { + $this->assertEquals(array('HTTP/1.1 401 Unauthorized'), OAuth2\AuthServer::getExceptionHttpHeaders('access_denied')); + $this->assertEquals(array('HTTP/1.1 500 Internal Server Error'), OAuth2\AuthServer::getExceptionHttpHeaders('server_error')); + $this->assertEquals(array('HTTP/1.1 501 Not Implemented'), OAuth2\AuthServer::getExceptionHttpHeaders('unsupported_grant_type')); + $this->assertEquals(array('HTTP/1.1 400 Bad Request'), OAuth2\AuthServer::getExceptionHttpHeaders('invalid_refresh')); + } + public function test_hasGrantType() { $a = $this->returnDefault();