def test_oauth2_pkce_flow_get_code_is_sent_in_authorization_header_by_default( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock ): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OktaAuthorizationCodePKCE( "testserver.okta-emea.com", "54239d18-c68c-4c47-8bdd-ce71ea1d50cd" ) tab = browser_mock.add_response( opened_url="https://testserver.okta-emea.com/oauth2/default/v1/authorize?client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b", ) httpx_mock.add_response( method="POST", url="https://testserver.okta-emea.com/oauth2/default/v1/token", json={ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, match_content=b"code_verifier=MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&code=SplxlOBeZQQYbYS6WxSbIA" ) assert ( get_header(httpx_mock, auth).get("Authorization") == "Bearer 2YotnFZFEjr1zCsicMWpAA" ) tab.assert_success( "You are now authenticated on 5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b. You may close this tab." )
def test_with_invalid_grant_request_invalid_request_error( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock ): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OktaAuthorizationCodePKCE( "testserver.okta-emea.com", "54239d18-c68c-4c47-8bdd-ce71ea1d50cd" ) tab = browser_mock.add_response( opened_url="https://testserver.okta-emea.com/oauth2/default/v1/authorize?client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b", ) httpx_mock.add_response( method="POST", url="https://testserver.okta-emea.com/oauth2/default/v1/token", json={"error": "invalid_request"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_request: The request is missing a required parameter, includes an " "unsupported parameter value (other than grant type), repeats a parameter, " "includes multiple credentials, utilizes more than one mechanism for " "authenticating the client, or is otherwise malformed." ) tab.assert_success( "You are now authenticated on 5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b. You may close this tab." )
def test_with_invalid_grant_request_invalid_client_error( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock ): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OktaAuthorizationCodePKCE( "testserver.okta-emea.com", "54239d18-c68c-4c47-8bdd-ce71ea1d50cd" ) tab = browser_mock.add_response( opened_url="https://testserver.okta-emea.com/oauth2/default/v1/authorize?client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b", ) httpx_mock.add_response( method="POST", url="https://testserver.okta-emea.com/oauth2/default/v1/token", json={"error": "invalid_client"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_client: Client authentication failed (e.g., unknown client, no " "client authentication included, or unsupported authentication method). The " "authorization server MAY return an HTTP 401 (Unauthorized) status code to " "indicate which HTTP authentication schemes are supported. 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." ) tab.assert_success( "You are now authenticated on 5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b. You may close this tab." )
def test_with_invalid_grant_request_invalid_grant_error( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock ): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OktaAuthorizationCodePKCE( "testserver.okta-emea.com", "54239d18-c68c-4c47-8bdd-ce71ea1d50cd" ) tab = browser_mock.add_response( opened_url="https://testserver.okta-emea.com/oauth2/default/v1/authorize?client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b", ) httpx_mock.add_response( method="POST", url="https://testserver.okta-emea.com/oauth2/default/v1/token", json={"error": "invalid_grant"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_grant: The provided authorization grant (e.g., authorization code, " "resource owner credentials) or refresh token is invalid, expired, revoked, " "does not match the redirection URI used in the authorization request, or was " "issued to another client." ) tab.assert_success( "You are now authenticated on 5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b. You may close this tab." )
def test_with_invalid_grant_request_invalid_request_error_and_error_description_and_uri_and_other_fields( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock ): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OktaAuthorizationCodePKCE( "testserver.okta-emea.com", "54239d18-c68c-4c47-8bdd-ce71ea1d50cd" ) tab = browser_mock.add_response( opened_url="https://testserver.okta-emea.com/oauth2/default/v1/authorize?client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b", ) httpx_mock.add_response( method="POST", url="https://testserver.okta-emea.com/oauth2/default/v1/token", json={ "error": "invalid_request", "error_description": "desc of the error", "error_uri": "http://test_url", "other": "other info", }, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == f"invalid_request: desc of the error\nMore information can be found on http://test_url\nAdditional information: {{'other': 'other info'}}" ) tab.assert_success( "You are now authenticated on 5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b. You may close this tab." )
def test_with_invalid_grant_request_invalid_client_error( token_cache, httpx_mock: HTTPXMock ): auth = httpx_auth.OAuth2ClientCredentials( "http://provide_access_token", client_id="test_user", client_secret="test_pwd" ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={"error": "invalid_client"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_client: Client authentication failed (e.g., unknown client, no " "client authentication included, or unsupported authentication method). The " "authorization server MAY return an HTTP 401 (Unauthorized) status code to " "indicate which HTTP authentication schemes are supported. 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." )
def test_empty_token_is_invalid( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock ): auth = httpx_auth.OktaAuthorizationCode( "testserver.okta-emea.com", "54239d18-c68c-4c47-8bdd-ce71ea1d50cd" ) tab = browser_mock.add_response( opened_url="https://testserver.okta-emea.com/oauth2/default/v1/authorize?client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b", ) httpx_mock.add_response( method="POST", url="https://testserver.okta-emea.com/oauth2/default/v1/token", json={ "access_token": "", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, ) with pytest.raises(httpx_auth.GrantNotProvided) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "access_token not provided within {'access_token': '', 'token_type': 'example', 'expires_in': 3600, 'refresh_token': 'tGzv3JOkF0XG5Qx2TlKWIA', 'example_parameter': 'example_value'}." ) tab.assert_success( "You are now authenticated on 5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b. You may close this tab." )
def test_query_api_key_is_sent_in_api_key_by_default(httpx_mock: HTTPXMock): auth = httpx_auth.QueryApiKey("my_provided_api_key") # Mock a dummy response httpx_mock.add_response( url="http://authorized_only?api_key=my_provided_api_key") # Send a request to this dummy URL with authentication httpx.get("http://authorized_only", auth=auth)
def test_response_type_can_be_provided_in_url(token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock): auth = httpx_auth.OAuth2AuthorizationCode( "http://provide_code?response_type=my_code", "http://provide_access_token", response_type="not_used", ) tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=my_code&state=49b67a19e70f692c3fc09dd124e5782b41a86f4f4931e1cc938ccbb466eecf1b730edb9eb01e42005de77ce3dd5a016418f8e780f30c4477d71102fe03e39e62&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=49b67a19e70f692c3fc09dd124e5782b41a86f4f4931e1cc938ccbb466eecf1b730edb9eb01e42005de77ce3dd5a016418f8e780f30c4477d71102fe03e39e62", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, match_content= b"grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code=SplxlOBeZQQYbYS6WxSbIA" ) # Mock a dummy response httpx_mock.add_response( match_headers={"Authorization": "Bearer 2YotnFZFEjr1zCsicMWpAA"}) # Send a request to this dummy URL with authentication httpx.get("http://authorized_only", auth=auth) tab.assert_success( "You are now authenticated on 49b67a19e70f692c3fc09dd124e5782b41a86f4f4931e1cc938ccbb466eecf1b730edb9eb01e42005de77ce3dd5a016418f8e780f30c4477d71102fe03e39e62. You may close this tab." )
def test_headers_matching(httpx_mock: HTTPXMock): httpx_mock.add_response( match_headers={"user-agent": "python-httpx/0.11.1"}) with httpx.Client() as client: response = client.get("http://test_url") assert response.content == b""
def test_oauth2_client_credential_and_multiple_authentication_can_be_combined( token_cache, httpx_mock: HTTPXMock ): resource_owner_password_auth = httpx_auth.OAuth2ClientCredentials( "http://provide_access_token", client_id="test_user", client_secret="test_pwd" ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, ) api_key_auth = httpx_auth.HeaderApiKey("my_provided_api_key") api_key_auth2 = httpx_auth.HeaderApiKey( "my_provided_api_key2", header_name="X-Api-Key2" ) header = get_header( httpx_mock, resource_owner_password_auth & (api_key_auth & api_key_auth2) ) assert header.get("Authorization") == "Bearer 2YotnFZFEjr1zCsicMWpAA" assert header.get("X-Api-Key") == "my_provided_api_key" assert header.get("X-Api-Key2") == "my_provided_api_key2"
def test_oauth2_authorization_code_and_api_key_authentication_can_be_combined( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock ): authorization_code_auth = httpx_auth.OAuth2AuthorizationCode( "http://provide_code", "http://provide_access_token" ) tab = browser_mock.add_response( opened_url="http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, ) api_key_auth = httpx_auth.HeaderApiKey("my_provided_api_key") header = get_header(httpx_mock, authorization_code_auth & api_key_auth) assert header.get("Authorization") == "Bearer 2YotnFZFEjr1zCsicMWpAA" assert header.get("X-Api-Key") == "my_provided_api_key" tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_with_invalid_grant_request_invalid_scope_error( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OAuth2AuthorizationCodePKCE( "http://provide_code?nonce=123456", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&nonce=%5B%27123456%27%5D&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={"error": "invalid_scope"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_scope: The requested scope is invalid, unknown, malformed, or " "exceeds the scope granted by the resource owner.") tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_oauth2_pkce_flow_get_code_is_sent_in_authorization_header_by_default( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OAuth2AuthorizationCodePKCE( "http://provide_code", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, match_content= b"code_verifier=MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&response_type=code&code=SplxlOBeZQQYbYS6WxSbIA" ) assert (get_header( httpx_mock, auth).get("Authorization") == "Bearer 2YotnFZFEjr1zCsicMWpAA") tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_with_invalid_grant_request_invalid_request_error( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OAuth2AuthorizationCodePKCE( "http://provide_code?nonce=123456", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&nonce=%5B%27123456%27%5D&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={"error": "invalid_request"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_request: The request is missing a required parameter, includes an " "unsupported parameter value (other than grant type), repeats a parameter, " "includes multiple credentials, utilizes more than one mechanism for " "authenticating the client, or is otherwise malformed.") tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_with_invalid_grant_request_invalid_request_error_and_error_description_and_uri_and_other_fields( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock): auth = httpx_auth.OAuth2AuthorizationCode("http://provide_code", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "error": "invalid_request", "error_description": "desc of the error", "error_uri": "http://test_url", "other": "other info", }, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_request: desc of the error\nMore information can be found on http://test_url\nAdditional information: {'other': 'other info'}" ) tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_fail_status_health_check(mock_http_health_datetime, httpx_mock: HTTPXMock): httpx_mock.add_response( url="http://test/health", method="GET", status_code=200, json={ "status": "fail", "version": "1", "releaseId": "1.2.3", "details": {"toto": "tata"}, }, headers={"content-type": "application/json"}, ) assert healthpy.httpx.check("tests", "http://test/health") == ( "fail", { "tests:health": { "componentType": "http://test/health", "observedValue": { "status": "fail", "version": "1", "releaseId": "1.2.3", "details": {"toto": "tata"}, }, "status": "fail", "time": "2018-10-11T15:05:05.663979", } }, )
async def test_content_matching(httpx_mock: HTTPXMock): httpx_mock.add_response(match_content=b"This is the body") async with httpx.AsyncClient() as client: response = await client.post("http://test_url", data=b"This is the body") assert response.read() == b""
def test_with_invalid_grant_request_invalid_scope_error( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock ): auth = httpx_auth.OktaAuthorizationCode( "testserver.okta-emea.com", "54239d18-c68c-4c47-8bdd-ce71ea1d50cd" ) tab = browser_mock.add_response( opened_url="https://testserver.okta-emea.com/oauth2/default/v1/authorize?client_id=54239d18-c68c-4c47-8bdd-ce71ea1d50cd&scope=openid&response_type=code&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b", ) httpx_mock.add_response( method="POST", url="https://testserver.okta-emea.com/oauth2/default/v1/token", json={"error": "invalid_scope"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_scope: The requested scope is invalid, unknown, malformed, or " "exceeds the scope granted by the resource owner." ) tab.assert_success( "You are now authenticated on 5264d11c8b268ccf911ce564ca42fd75cea68c4a3c1ec3ac1ab20243891ab7cd5250ad4c2d002017c6e8ac2ba34954293baa5e0e4fd00bb9ffd4a39c45f1960b. You may close this tab." )
def test_oauth2_authorization_code_flow_get_code_is_sent_in_authorization_header_by_default( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock): auth = httpx_auth.OAuth2AuthorizationCode("http://provide_code", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, match_content= b"grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&response_type=code&code=SplxlOBeZQQYbYS6WxSbIA" ) httpx_mock.add_response( match_headers={"Authorization": "Bearer 2YotnFZFEjr1zCsicMWpAA"}) # Send a request to this dummy URL with authentication httpx.get("http://authorized_only", auth=auth) tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_empty_token_is_invalid(token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock): auth = httpx_auth.OAuth2AuthorizationCode("http://provide_code", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "access_token": "", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, ) with pytest.raises(httpx_auth.GrantNotProvided) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "access_token not provided within {'access_token': '', 'token_type': 'example', 'expires_in': 3600, 'refresh_token': 'tGzv3JOkF0XG5Qx2TlKWIA', 'example_parameter': 'example_value'}." ) tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_with_invalid_grant_request_unsupported_grant_type_error( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock): auth = httpx_auth.OAuth2AuthorizationCode("http://provide_code", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={"error": "unsupported_grant_type"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "unsupported_grant_type: The authorization grant type is not supported by the " "authorization server.") tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_with_invalid_grant_request_invalid_grant_error( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock): auth = httpx_auth.OAuth2AuthorizationCode("http://provide_code", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={"error": "invalid_grant"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_grant: The provided authorization grant (e.g., authorization code, " "resource owner credentials) or refresh token is invalid, expired, revoked, " "does not match the redirection URI used in the authorization request, or was " "issued to another client.") tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_oauth2_pkce_and_multiple_authentication_can_be_combined( token_cache, httpx_mock: HTTPXMock, browser_mock: BrowserMock, monkeypatch ): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) pkce_auth = httpx_auth.OAuth2AuthorizationCodePKCE( "http://provide_code", "http://provide_access_token" ) tab = browser_mock.add_response( opened_url="http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url="http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value", }, ) api_key_auth = httpx_auth.HeaderApiKey("my_provided_api_key") api_key_auth2 = httpx_auth.HeaderApiKey( "my_provided_api_key2", header_name="X-Api-Key2" ) header = get_header(httpx_mock, pkce_auth & (api_key_auth & api_key_auth2)) assert header.get("Authorization") == "Bearer 2YotnFZFEjr1zCsicMWpAA" assert header.get("X-Api-Key") == "my_provided_api_key" assert header.get("X-Api-Key2") == "my_provided_api_key2" tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_with_invalid_grant_request_invalid_client_error( token_cache, httpx_mock: HTTPXMock, monkeypatch, browser_mock: BrowserMock): monkeypatch.setattr(httpx_auth.authentication.os, "urandom", lambda x: b"1" * 63) auth = httpx_auth.OAuth2AuthorizationCodePKCE( "http://provide_code?nonce=123456", "http://provide_access_token") tab = browser_mock.add_response( opened_url= "http://provide_code?response_type=code&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F&nonce=%5B%27123456%27%5D&code_challenge=5C_ph_KZ3DstYUc965SiqmKAA-ShvKF4Ut7daKd3fjc&code_challenge_method=S256", reply_url= "http://localhost:5000#code=SplxlOBeZQQYbYS6WxSbIA&state=163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de", ) httpx_mock.add_response( method="POST", url="http://provide_access_token", json={"error": "invalid_client"}, status_code=400, ) with pytest.raises(httpx_auth.InvalidGrantRequest) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "invalid_client: Client authentication failed (e.g., unknown client, no " "client authentication included, or unsupported authentication method). The " "authorization server MAY return an HTTP 401 (Unauthorized) status code to " "indicate which HTTP authentication schemes are supported. 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.") tab.assert_success( "You are now authenticated on 163f0455b3e9cad3ca04254e5a0169553100d3aa0756c7964d897da316a695ffed5b4f46ef305094fd0a88cfe4b55ff257652015e4aa8f87b97513dba440f8de. You may close this tab." )
def test_browser_error(token_cache, httpx_mock: HTTPXMock, monkeypatch): import httpx_auth.oauth2_authentication_responses_server auth = httpx_auth.OAuth2Implicit("http://provide_token", timeout=0.1) class FakeBrowser: def open(self, url, new): import webbrowser raise webbrowser.Error("Failure") monkeypatch.setattr( httpx_auth.oauth2_authentication_responses_server.webbrowser, "get", lambda *args: FakeBrowser(), ) httpx_mock.add_response( method="GET", url="http://provide_token?response_type=token&state=42a85b271b7a652ca3cc4c398cfd3f01b9ad36bf9c945ba823b023e8f8b95c4638576a0e3dcc96838b838bec33ec6c0ee2609d62ed82480b3b8114ca494c0521&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2F", ) with pytest.raises(httpx_auth.TimeoutOccurred) as exception_info: httpx.get("http://authorized_only", auth=auth) assert ( str(exception_info.value) == "User authentication was not received within 0.1 seconds." )
def test_query_api_key_can_be_sent_in_a_custom_field_name( httpx_mock: HTTPXMock): auth = httpx_auth.QueryApiKey("my_provided_api_key", "X-API-QUERY-KEY") # Mock a dummy response httpx_mock.add_response( url="http://authorized_only?X-API-QUERY-KEY=my_provided_api_key") # Send a request to this dummy URL with authentication httpx.get("http://authorized_only", auth=auth)
def test_default_request_retrieval(httpx_mock: HTTPXMock): httpx_mock.add_response() with httpx.Client() as client: client.post("http://test_url", headers={"X-TEST": "test header 1"}) request = httpx_mock.get_request() assert request.headers["x-test"] == "test header 1"
def test_request_retrieval_with_more_than_one(httpx_mock: HTTPXMock): httpx_mock.add_response() with httpx.Client() as client: client.get("http://test_url", headers={"X-TEST": "test header 1"}) client.get("http://test_url", headers={"X-TEST": "test header 2"}) httpx_mock.get_request(url=httpx.URL("http://test_url"))
async def test_headers_matching(httpx_mock: HTTPXMock): httpx_mock.add_response( match_headers={"user-agent": f"python-httpx/{httpx.__version__}"} ) async with httpx.AsyncClient() as client: response = await client.get("http://test_url") assert response.content == b""