async def test_introspect_revoked_token( server: AuthorizationServer, storage: Dict[str, List], defaults: Defaults ): client_id = defaults.client_id client_secret = defaults.client_secret request_url = "https://localhost" token = storage["tokens"][0] post = Post( grant_type=GrantType.TYPE_REFRESH_TOKEN, refresh_token=token.refresh_token, ) request = Request( url=request_url, post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.OK # Check that refreshed token was revoked post = Post(token=token.access_token) request = Request( post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) response = await server.create_token_introspection_response(request) assert not response.content.active, "The refresh_token must be revoked"
def test_is_secure_transport(): request = Request(method=RequestMethod.GET, url="https://google.com") is_secure = is_secure_transport(request=request) assert is_secure request = Request(method=RequestMethod.GET, url="http://google.com") is_secure = is_secure_transport(request=request) assert not is_secure
def test_decode_auth_headers(): request = Request(headers=CaseInsensitiveDict(), method=RequestMethod.POST) with pytest.raises(InvalidClientError): decode_auth_headers(request=request) request = Request( headers=CaseInsensitiveDict({"authorization": ""}), method=RequestMethod.POST ) with pytest.raises(InvalidClientError): decode_auth_headers(request=request)
async def test_expired_token( server: AuthorizationServer, storage: Dict[str, List], defaults: Defaults ): settings = Settings() token = Token( client_id=defaults.client_id, expires_in=settings.TOKEN_EXPIRES_IN, access_token=generate_token(42), refresh_token=generate_token(48), issued_at=int(time.time() - settings.TOKEN_EXPIRES_IN), scope=defaults.scope, ) client_id = defaults.client_id client_secret = defaults.client_secret storage["tokens"].append(token) post = Post(token=token.access_token) request = Request( post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) response = await server.create_token_introspection_response(request) assert response.status_code == HTTPStatus.OK assert not response.content.active
async def test_db(storage: Dict[str, List]): db = BaseDB() request = Request(method=RequestMethod.POST) client: Client = storage["clients"][0] token: Token = storage["tokens"][0] authorization_code: AuthorizationCode = storage["authorization_codes"][0] with pytest.raises(NotImplementedError): await db.get_token( request=request, client_id=client.client_id, access_token=token.access_token, refresh_token=token.refresh_token, ) with pytest.raises(NotImplementedError): await db.get_client( request=request, client_id=client.client_id, client_secret=client.client_secret, ) with pytest.raises(NotImplementedError): await db.authenticate(request=request) with pytest.raises(NotImplementedError): await db.get_authorization_code(request=request, client_id=client.client_id, code=authorization_code.code) with pytest.raises(NotImplementedError): await db.delete_authorization_code(request=request, client_id=client.client_id, code=authorization_code.code) with pytest.raises(NotImplementedError): await db.revoke_token(request=request, refresh_token=token.refresh_token)
async def test_expired_authorization_code(server: AuthorizationServer, defaults: Defaults, storage: Dict[str, List]): request_url = "https://localhost" settings = Settings() authorization_code = storage["authorization_codes"][0] storage["authorization_codes"][0] = set_values( authorization_code, {"auth_time": time.time() - settings.AUTHORIZATION_CODE_EXPIRES_IN}, ) post = Post( grant_type=GrantType.TYPE_AUTHORIZATION_CODE, redirect_uri=defaults.redirect_uri, code=storage["authorization_codes"][0].code, ) request = Request( url=request_url, post=post, method=RequestMethod.POST, headers=encode_auth_headers(defaults.client_id, defaults.client_secret), ) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.BAD_REQUEST assert response.content.error == ErrorType.INVALID_GRANT
async def test_implicit_flow(server: AuthorizationServer, defaults: Defaults): request_url = "https://localhost" state = generate_token(10) scope = defaults.scope user = "******" query = Query( client_id=defaults.client_id, response_type=ResponseType.TYPE_TOKEN, redirect_uri=defaults.redirect_uri, scope=scope, state=state, ) request = Request( url=request_url, query=query, method=RequestMethod.GET, user=user, ) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.FOUND location = response.headers["location"] location = urlparse(location) fragment = dict(parse_qsl(location.fragment)) assert fragment["state"] == state assert fragment["scope"] == scope
async def test_invalid_response_type(server: AuthorizationServer, defaults: Defaults, storage): code_verifier = generate_token(128) code_challenge = create_s256_code_challenge(code_verifier) request_url = "https://localhost" user = "******" client = storage["clients"][0] client = set_values(client, {"response_types": [ResponseType.TYPE_TOKEN]}) storage["clients"][0] = client query = Query( client_id=defaults.client_id, response_type=ResponseType.TYPE_CODE, redirect_uri=defaults.redirect_uri, scope=defaults.scope, state=generate_token(10), code_challenge_method=CodeChallengeMethod.S256, code_challenge=code_challenge, ) request = Request( url=request_url, query=query, method=RequestMethod.GET, user=user, ) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.BAD_REQUEST assert response.content.error == ErrorType.UNSUPPORTED_RESPONSE_TYPE
async def test_invalid_grant_type(server: AuthorizationServer, defaults: Defaults, storage): client: Client = storage["clients"][0] client = set_values(client, {"grant_types": [GrantType.TYPE_AUTHORIZATION_CODE]}) storage["clients"][0] = client client_id = defaults.client_id client_secret = defaults.client_secret request_url = "https://localhost" post = Post( grant_type=GrantType.TYPE_PASSWORD, username=defaults.username, password=defaults.password, scope="test test", ) request = Request( post=post, url=request_url, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.BAD_REQUEST assert response.content.error == ErrorType.UNAUTHORIZED_CLIENT
async def test_endpoint_availability(db_class: Type[BaseDB]): server = AuthorizationServer(db=db_class()) request = Request(method=RequestMethod.POST, settings=Settings(AVAILABLE=False)) response = await server.create_token_introspection_response(request) assert response.status_code == HTTPStatus.BAD_REQUEST assert response.content.error == ErrorType.TEMPORARILY_UNAVAILABLE
def test_is_secure_transport_insecure_transport_enabled(): request = Request( method=RequestMethod.GET, url="https://google.com", settings=Settings(INSECURE_TRANSPORT=True), ) is_secure = is_secure_transport(request=request) assert is_secure request = Request( method=RequestMethod.GET, url="https://google.com", settings=Settings(INSECURE_TRANSPORT=True), ) is_secure = is_secure_transport(request=request) assert is_secure
def test_base_error_uri(): ERROR_URI = "https://google.com" request = Request(settings=Settings(ERROR_URI=ERROR_URI), method=RequestMethod.POST) try: raise InvalidClientError(request=request) except InvalidClientError as exc: assert urljoin(ERROR_URI, exc.error) == exc.error_uri
async def test_authorization_code_flow_credentials_in_post( server: AuthorizationServer, defaults: Defaults): client_id = defaults.client_id client_secret = defaults.client_secret request_url = "https://localhost" user = "******" query = Query( client_id=defaults.client_id, response_type=ResponseType.TYPE_CODE, redirect_uri=defaults.redirect_uri, scope=defaults.scope, state=generate_token(10), ) request = Request( url=request_url, query=query, method=RequestMethod.GET, user=user, ) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.FOUND location = response.headers["location"] location = urlparse(location) query = dict(parse_qsl(location.query)) code = query["code"] post = Post( grant_type=GrantType.TYPE_AUTHORIZATION_CODE, client_id=client_id, client_secret=client_secret, redirect_uri=defaults.redirect_uri, code=code, ) request = Request( url=request_url, post=post, method=RequestMethod.POST, ) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.OK
async def test_allowed_methods(server: AuthorizationServer): request_url = "https://localhost" request = Request( url=request_url, method=RequestMethod.POST, ) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED
async def test_insecure_transport_error(server: AuthorizationServer): request_url = "http://localhost" request = Request( url=request_url, method=RequestMethod.GET, ) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.BAD_REQUEST
async def test_internal_server_error(): class EndpointClass: available: Optional[bool] = True def __init__(self, available: Optional[bool] = None): if available is not None: self.available = available @catch_errors_and_unavailability async def server(self, request): raise Exception() e = EndpointClass() response = await e.server(Request(method=RequestMethod.POST)) assert response.status_code == HTTPStatus.BAD_REQUEST
async def test_client_credentials_flow_post_data(server: AuthorizationServer, defaults: Defaults): request_url = "https://localhost" post = Post( grant_type=GrantType.TYPE_CLIENT_CREDENTIALS, client_id=defaults.client_id, client_secret=defaults.client_secret, scope=defaults.scope, ) request = Request(url=request_url, post=post, method=RequestMethod.POST) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.OK
async def test_invalid_token(server: AuthorizationServer, defaults: Defaults): client_id = defaults.client_id client_secret = defaults.client_secret request_url = "https://localhost" token = "invalid token" post = Post(token=token) request = Request( url=request_url, post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) response = await server.create_token_introspection_response(request) assert not response.content.active assert response.status_code == HTTPStatus.OK
async def test_valid_token(server: AuthorizationServer, storage: Dict[str, List], defaults: Defaults): client_id = defaults.client_id client_secret = defaults.client_secret token = storage["tokens"][0] post = Post(token=token.access_token) request = Request( post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) response = await server.create_token_introspection_response(request) assert response.status_code == HTTPStatus.OK assert response.content.active
async def test_anonymous_user(server: AuthorizationServer, defaults: Defaults, storage): code_verifier = generate_token(128) code_challenge = create_s256_code_challenge(code_verifier) request_url = "https://localhost" query = Query( client_id=defaults.client_id, response_type=ResponseType.TYPE_CODE, redirect_uri=defaults.redirect_uri, scope=defaults.scope, state=generate_token(10), code_challenge_method=CodeChallengeMethod.S256, code_challenge=code_challenge, ) request = Request(url=request_url, query=query, method=RequestMethod.GET) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.UNAUTHORIZED assert response.content.error == ErrorType.INVALID_CLIENT
async def test_invalid_client_credentials(server: AuthorizationServer, defaults: Defaults): client_id = defaults.client_id request_url = "https://localhost" post = Post( grant_type=GrantType.TYPE_PASSWORD, username=defaults.username, password=defaults.password, ) request = Request( post=post, url=request_url, method=RequestMethod.POST, headers=encode_auth_headers(client_id, "client_secret"), ) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.BAD_REQUEST assert response.content.error == ErrorType.INVALID_REQUEST
async def test_password_grant_type(server: AuthorizationServer, defaults: Defaults): client_id = defaults.client_id client_secret = defaults.client_secret request_url = "https://localhost" post = Post( grant_type=GrantType.TYPE_PASSWORD, username=defaults.username, password=defaults.password, ) request = Request( post=post, url=request_url, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) await check_request_validators(request, server.create_token_response) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.OK
async def test_expired_refresh_token(server: AuthorizationServer, defaults: Defaults, storage: Dict[str, List]): settings = Settings() token = storage["tokens"][0] refresh_token = token.refresh_token storage["tokens"][0] = set_values( token, {"issued_at": time.time() - (settings.TOKEN_EXPIRES_IN * 2)}) request_url = "https://localhost" post = Post( grant_type=GrantType.TYPE_REFRESH_TOKEN, refresh_token=refresh_token, ) request = Request( url=request_url, post=post, method=RequestMethod.POST, headers=encode_auth_headers(defaults.client_id, defaults.client_secret), ) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.BAD_REQUEST assert response.content.error == ErrorType.INVALID_GRANT
async def test_authorization_code_flow_plan_code_challenge( server: AuthorizationServer, defaults: Defaults, db: BaseDB): code_challenge = generate_token(128) client_id = defaults.client_id client_secret = defaults.client_secret scope = defaults.scope state = generate_token(10) redirect_uri = defaults.redirect_uri request_url = "https://localhost" user = "******" query = Query( client_id=defaults.client_id, response_type=ResponseType.TYPE_CODE, redirect_uri=redirect_uri, scope=scope, state=state, code_challenge_method=CodeChallengeMethod.PLAIN, code_challenge=code_challenge, ) request = Request( url=request_url, query=query, method=RequestMethod.GET, user=user, ) await check_request_validators(request, server.create_authorization_response) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.FOUND location = response.headers["location"] location = urlparse(location) query = dict(parse_qsl(location.query)) assert query["state"] == state assert query["scope"] == scope assert await db.get_authorization_code(request, client_id, query["code"]) assert "code" in query location = response.headers["location"] location = urlparse(location) query = dict(parse_qsl(location.query)) code = query["code"] post = Post( grant_type=GrantType.TYPE_AUTHORIZATION_CODE, redirect_uri=defaults.redirect_uri, code=code, code_verifier=code_challenge, scope=scope, ) request = Request( url=request_url, post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.OK assert response.headers == default_headers assert response.content.scope == scope assert response.content.token_type == "Bearer" # Check that token was created in db assert await db.get_token( request, client_id, response.content.access_token, response.content.refresh_token, ) access_token = response.content.access_token refresh_token = response.content.refresh_token post = Post( grant_type=GrantType.TYPE_REFRESH_TOKEN, refresh_token=refresh_token, ) request = Request( url=request_url, post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) await check_request_validators(request, server.create_token_response) response = await server.create_token_response(request) assert response.status_code == HTTPStatus.OK assert response.content.access_token != access_token assert response.content.refresh_token != refresh_token # Check that token was created in db assert await db.get_token( request, client_id, response.content.access_token, response.content.refresh_token, ) # Check that previous token was revoken token_in_db = await db.get_token(request, client_id, access_token, refresh_token) assert token_in_db.revoked
async def test_authorization_code_flow_pkce_code_challenge( server: AuthorizationServer, defaults: Defaults, db: BaseDB): client_id = defaults.client_id client_secret = defaults.client_secret code_verifier = generate_token(128) scope = defaults.scope code_challenge = create_s256_code_challenge(code_verifier) request_url = "https://localhost" user = "******" state = generate_token(10) query = Query( client_id=defaults.client_id, response_type=ResponseType.TYPE_CODE, redirect_uri=defaults.redirect_uri, scope=scope, state=state, code_challenge_method=CodeChallengeMethod.S256, code_challenge=code_challenge, ) request = Request( url=request_url, query=query, method=RequestMethod.GET, user=user, ) response = await server.create_authorization_response(request) assert response.status_code == HTTPStatus.FOUND location = response.headers["location"] location = urlparse(location) query = dict(parse_qsl(location.query)) assert query["state"] == state assert query["scope"] == scope assert "code" in query code = query["code"] post = Post( grant_type=GrantType.TYPE_AUTHORIZATION_CODE, redirect_uri=defaults.redirect_uri, code=code, scope=scope, code_verifier=code_verifier, ) request = Request( url=request_url, post=post, method=RequestMethod.POST, headers=encode_auth_headers(client_id, client_secret), ) await check_request_validators(request, server.create_token_response) code_record = await db.get_authorization_code(request, client_id, code) assert code_record response = await server.create_token_response(request) assert response.status_code == HTTPStatus.OK assert response.headers == default_headers assert response.content.scope == scope assert response.content.token_type == "Bearer" code_record = await db.get_authorization_code(request, client_id, code) assert not code_record