From 1073707f9cdca25209a20853f2d5aa9f139c310c Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sun, 8 Dec 2019 19:07:59 -0500 Subject: [PATCH] First set of tests for token issuance --- lib/REST/Microsub/Auth.php | 8 ++-- tests/cases/REST/Microsub/TestAuth.php | 53 +++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/REST/Microsub/Auth.php b/lib/REST/Microsub/Auth.php index 61a0d2b..25bb083 100644 --- a/lib/REST/Microsub/Auth.php +++ b/lib/REST/Microsub/Auth.php @@ -250,14 +250,14 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler { protected function opIssueAccessToken(ServerRequestInterface $req): ResponseInterface { $post = $req->getParsedBody(); // revocation is a special case of POSTing to the token URL - if ($post['action'] ?? "" === "revoke") { + if (($post['action'] ?? "") === "revoke") { return $this->opRevokeToken($req); } if (($post['grant_type'] ?? "") !== "authorization_code") { throw new ExceptionAuth("unsupported_grant_type"); } $tr = Arsse::$db->begin(); - list($user, $type) = $this->validateAuthCode($post['code'] ?? "", $post['client_id'] ?? "", $post['redirect_url'] ?? "", $post['me'] ?? ""); + list($user, $type) = $this->validateAuthCode($post['code'] ?? "", $post['client_id'] ?? "", $post['redirect_uri'] ?? "", $post['me'] ?? ""); if ($type !== "code") { throw new ExceptionAuth("invalid_grant"); } @@ -285,7 +285,7 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler { * It is the responsibility of the calling function to revoke the auth code if the code is ultimately accepted */ protected function validateAuthCode(string $code, string $clientId, string $redirUrl, string $me = null): array { - if (!strlen($code) || !strlen($clientId) || !strlen($redirUrl)) { + if (!strlen($code) || !strlen($clientId) || !strlen($redirUrl) || (isset($me) && !strlen($me))) { throw new ExceptionAuth("invalid_request"); } // check that the auth code exists @@ -296,7 +296,7 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler { } $data = @json_decode((string) $token['data'], true); // validate the auth code - if (!is_array($data) || !isset($data['redirect_uri']) || !isset($data['client_id'])) { + if (!is_array($data) || !isset($data['redirect_uri']) || !isset($data['client_id']) || (isset($me) && !isset($data['me']))) { throw new ExceptionAuth("invalid_grant"); } elseif ($data['client_id'] !== $clientId || $data['redirect_uri'] !== $redirUrl) { throw new ExceptionAuth("invalid_client"); diff --git a/tests/cases/REST/Microsub/TestAuth.php b/tests/cases/REST/Microsub/TestAuth.php index 7b93002..6ac0f60 100644 --- a/tests/cases/REST/Microsub/TestAuth.php +++ b/tests/cases/REST/Microsub/TestAuth.php @@ -9,7 +9,7 @@ namespace JKingWeb\Arsse\TestCase\REST\Microsub; use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Db\ExceptionInput; -use JKingWeb\Arsse\Misc\Date; +use JKingWeb\Arsse\REST\Microsub\Auth; use Psr\Http\Message\ResponseInterface; use Zend\Diactoros\Response\JsonResponse as Response; use Zend\Diactoros\Response\EmptyResponse; @@ -17,7 +17,7 @@ use Zend\Diactoros\Response\HtmlResponse; /** @covers \JKingWeb\Arsse\REST\Microsub\Auth */ class TestAuth extends \JKingWeb\Arsse\Test\AbstractTest { - public function setUp() { + public function setUp(): void { self::clearData(); Arsse::$db = \Phake::mock(Database::class); } @@ -161,4 +161,53 @@ class TestAuth extends \JKingWeb\Arsse\Test\AbstractTest { 'Success 2' => [['code' => "good-code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/"], "somehow", '{"redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"id"}', new Response(['me' => "http://example.com/u/somehow"], 200)], ]; } + + /** @dataProvider provideTokenRequests */ + public function testIssueAnAccessToken(array $params, string $user, $data, ResponseInterface $exp) { + if ($data instanceof \Exception) { + \Phake::when(Arsse::$db)->tokenLookup("microsub.auth", $params['code'] ?? "")->thenThrow($data); + } else { + \Phake::when(Arsse::$db)->tokenLookup("microsub.auth", $params['code'] ?? "")->thenReturn(['user' => $user, 'data' => $data]); + } + \Phake::when(Arsse::$db)->tokenCreate->thenReturn("TOKEN"); + $act = $this->req("http://example.com/u/?f=token", "POST", [], [], $params); + $this->assertMessage($exp, $act); + if ($act->getStatusCode() == 200) { + $input = '{"me":"'.($params['me'] ?? "").'","client_id":"'.($params['client_id'] ?? "").'"}'; + \Phake::verify(Arsse::$db, \Phake::times(1))->tokenCreate($user, "microsub.access", null, null, $input); + \Phake::verify(Arsse::$db, \Phake::times(1))->tokenRevoke($user, "microsub.auth", $params['code'] ?? ""); + } else { + \Phake::verify(Arsse::$db, \Phake::times(0))->tokenCreate; + \Phake::verify(Arsse::$db, \Phake::times(0))->tokenRevoke; + } + } + + public function provideTokenRequests() { + $scopes = implode(" ", Auth::SCOPES); + return [ + 'Missing code 1' => [[ 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Missing code 2' => [['code' => "", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Missing URL 1' => [['code' => "code", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Missing URL 2' => [['code' => "code", 'redirect_uri' => "", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Missing ID 1' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Missing ID 2' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Missing grant 1' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "unsupported_grant_type"], 400)], + 'Missing grant 2' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "unsupported_grant_type"], 400)], + 'Missing me 1' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "" ], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Missing me 2' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", ], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_request" ], 400)], + 'Mismatched URL' => [['code' => "code", 'redirect_uri' => "https://example.net/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_client" ], 400)], + 'Mismatched ID' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.org/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_client" ], 400)], + 'Mismatched grant' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "mismatch", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "unsupported_grant_type"], 400)], + 'Mismatched me' => [['code' => "code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/" ], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_grant" ], 400)], + 'Bad data 1' => [['code' => "bad-data1", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", null, new Response(['error' => "invalid_grant" ], 400)], + 'Bad data 2' => [['code' => "bad-data2", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{ "redirect_uri":"https://example.org/", client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_grant" ], 400)], + 'Bad data 3' => [['code' => "bad-data3", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone", client_id":"https://example.net/","response_type":"code"}', new Response(['error' => "invalid_grant" ], 400)], + 'Bad data 4' => [['code' => "bad-data4", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/", "response_type":"code"}', new Response(['error' => "invalid_grant" ], 400)], + 'Bad data 5' => [['code' => "bad-data5", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/", }', new Response(['error' => "invalid_grant" ], 400)], + 'Bad data 6' => [['code' => "bad-data6", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"id" }', new Response(['error' => "invalid_grant" ], 400)], + 'Bad user' => [['code' => "bad-user", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", new ExceptionInput("subjectMissing"), new Response(['error' => "invalid_grant" ], 400)], + 'Success 1' => [['code' => "valid-code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/someone"], "someone", '{"me":"https://example.com/u/someone","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['me' => "http://example.com/u/someone", 'token_type' => "Bearer", 'access_token' => "TOKEN", 'scope' => $scopes], 200)], + 'Success 2' => [['code' => "good-code", 'redirect_uri' => "https://example.org/", 'client_id' => "https://example.net/", 'grant_type' => "authorization_code", 'me' => "https://example.com/u/somehow"], "somehow", '{"me":"https://example.com/u/somehow","redirect_uri":"https://example.org/","client_id":"https://example.net/","response_type":"code"}', new Response(['me' => "http://example.com/u/somehow", 'token_type' => "Bearer", 'access_token' => "TOKEN", 'scope' => $scopes], 200)], + ]; + } }