async def mock_req(method: str, uri: str, data=None, headers=None): (expected_uri, resp_obj) = expected_requests.pop(0) assert uri == expected_uri resp = FakeResponse( code=200, phrase=b"OK", body=json.dumps(resp_obj).encode("utf-8"), ) return resp
async def mock_req( method: str, uri: str, data: Optional[dict] = None, headers: Optional[Iterable[Tuple[AnyStr, AnyStr]]] = None, ): (expected_uri, resp_obj) = expected_requests.pop(0) assert uri == expected_uri resp = FakeResponse( code=HTTPStatus.OK, phrase=b"OK", body=json.dumps(resp_obj).encode("utf-8"), ) return resp
def test_exchange_code_jwt_key(self): """Test that code exchange works with a JWK client secret.""" from authlib.jose import jwt token = {"type": "bearer"} self.http_client.request = simple_async_mock( return_value=FakeResponse( code=200, phrase=b"OK", body=json.dumps(token).encode("utf-8") ) ) code = "code" # advance the clock a bit before we start, so we aren't working with zero # timestamps. self.reactor.advance(1000) start_time = self.reactor.seconds() ret = self.get_success(self.provider._exchange_code(code)) self.assertEqual(ret, token) # the request should have hit the token endpoint kwargs = self.http_client.request.call_args[1] self.assertEqual(kwargs["method"], "POST") self.assertEqual(kwargs["uri"], TOKEN_ENDPOINT) # the client secret provided to the should be a jwt which can be checked with # the public key args = parse_qs(kwargs["data"].decode("utf-8")) secret = args["client_secret"][0] with open(_public_key_file_path()) as f: key = f.read() claims = jwt.decode(secret, key) self.assertEqual(claims.header["kid"], "ABC789") self.assertEqual(claims["aud"], ISSUER) self.assertEqual(claims["iss"], "DEFGHI") self.assertEqual(claims["sub"], CLIENT_ID) self.assertEqual(claims["iat"], start_time) self.assertGreater(claims["exp"], start_time) # check the rest of the POSTed data self.assertEqual(args["grant_type"], ["authorization_code"]) self.assertEqual(args["code"], [code]) self.assertEqual(args["client_id"], [CLIENT_ID]) self.assertEqual(args["redirect_uri"], [CALLBACK_URL])
def test_exchange_code_no_auth(self): """Test that code exchange works with no client secret.""" token = {"type": "bearer"} self.http_client.request = simple_async_mock(return_value=FakeResponse( code=200, phrase=b"OK", body=json.dumps(token).encode("utf-8"))) code = "code" ret = self.get_success(self.provider._exchange_code(code)) self.assertEqual(ret, token) # the request should have hit the token endpoint kwargs = self.http_client.request.call_args[1] self.assertEqual(kwargs["method"], "POST") self.assertEqual(kwargs["uri"], TOKEN_ENDPOINT) # check the POSTed data args = parse_qs(kwargs["data"].decode("utf-8")) self.assertEqual(args["grant_type"], ["authorization_code"]) self.assertEqual(args["code"], [code]) self.assertEqual(args["client_id"], [CLIENT_ID]) self.assertEqual(args["redirect_uri"], [CALLBACK_URL])
def test_exchange_code(self): """Code exchange behaves correctly and handles various error scenarios.""" token = {"type": "bearer"} token_json = json.dumps(token).encode("utf-8") self.http_client.request = simple_async_mock( return_value=FakeResponse(code=200, phrase=b"OK", body=token_json)) code = "code" ret = self.get_success(self.provider._exchange_code(code)) kwargs = self.http_client.request.call_args[1] self.assertEqual(ret, token) self.assertEqual(kwargs["method"], "POST") self.assertEqual(kwargs["uri"], TOKEN_ENDPOINT) args = parse_qs(kwargs["data"].decode("utf-8")) self.assertEqual(args["grant_type"], ["authorization_code"]) self.assertEqual(args["code"], [code]) self.assertEqual(args["client_id"], [CLIENT_ID]) self.assertEqual(args["client_secret"], [CLIENT_SECRET]) self.assertEqual(args["redirect_uri"], [CALLBACK_URL]) # Test error handling self.http_client.request = simple_async_mock(return_value=FakeResponse( code=400, phrase=b"Bad Request", body=b'{"error": "foo", "error_description": "bar"}', )) from synapse.handlers.oidc import OidcError exc = self.get_failure(self.provider._exchange_code(code), OidcError) self.assertEqual(exc.value.error, "foo") self.assertEqual(exc.value.error_description, "bar") # Internal server error with no JSON body self.http_client.request = simple_async_mock(return_value=FakeResponse( code=500, phrase=b"Internal Server Error", body=b"Not JSON", )) exc = self.get_failure(self.provider._exchange_code(code), OidcError) self.assertEqual(exc.value.error, "server_error") # Internal server error with JSON body self.http_client.request = simple_async_mock(return_value=FakeResponse( code=500, phrase=b"Internal Server Error", body=b'{"error": "internal_server_error"}', )) exc = self.get_failure(self.provider._exchange_code(code), OidcError) self.assertEqual(exc.value.error, "internal_server_error") # 4xx error without "error" field self.http_client.request = simple_async_mock(return_value=FakeResponse( code=400, phrase=b"Bad request", body=b"{}", )) exc = self.get_failure(self.provider._exchange_code(code), OidcError) self.assertEqual(exc.value.error, "server_error") # 2xx error with "error" field self.http_client.request = simple_async_mock(return_value=FakeResponse( code=200, phrase=b"OK", body=b'{"error": "some_error"}', )) exc = self.get_failure(self.provider._exchange_code(code), OidcError) self.assertEqual(exc.value.error, "some_error")