|
1 | 1 | """Tests for refactored OAuth client authentication implementation.""" |
2 | 2 |
|
3 | 3 | import base64 |
| 4 | +import json |
4 | 5 | import time |
5 | 6 | from unittest import mock |
6 | 7 | from urllib.parse import parse_qs, quote, unquote, urlparse |
|
23 | 24 | extract_scope_from_www_auth, |
24 | 25 | get_client_metadata_scopes, |
25 | 26 | handle_registration_response, |
| 27 | + infer_application_type, |
26 | 28 | is_valid_client_metadata_url, |
27 | 29 | should_use_client_metadata_url, |
28 | 30 | ) |
@@ -1028,6 +1030,61 @@ def test_falls_back_when_metadata_has_no_registration_endpoint(self): |
1028 | 1030 | assert request.method == "POST" |
1029 | 1031 |
|
1030 | 1032 |
|
| 1033 | +@pytest.mark.parametrize( |
| 1034 | + ("redirect_uris", "expected"), |
| 1035 | + [ |
| 1036 | + (["http://localhost:3030/callback"], "native"), |
| 1037 | + (["http://127.0.0.1:3030/callback"], "native"), |
| 1038 | + (["http://[::1]:3030/callback"], "native"), |
| 1039 | + (["com.example.app:/oauth2redirect"], "native"), |
| 1040 | + (["https://app.example.com/callback"], "web"), |
| 1041 | + (["http://localhost:3030/callback", "https://app.example.com/callback"], None), |
| 1042 | + ], |
| 1043 | +) |
| 1044 | +def test_infer_application_type(redirect_uris: list[str], expected: str | None): |
| 1045 | + """SEP-837: native for loopback or private-use redirect URIs, web for remote hosts.""" |
| 1046 | + assert infer_application_type([AnyUrl(uri) for uri in redirect_uris]) == expected |
| 1047 | + |
| 1048 | + |
| 1049 | +def test_infer_application_type_without_redirect_uris(): |
| 1050 | + assert infer_application_type([]) is None |
| 1051 | + assert infer_application_type(None) is None |
| 1052 | + |
| 1053 | + |
| 1054 | +def test_create_client_registration_request_infers_native_application_type(): |
| 1055 | + """A loopback redirect URI registers the client as a native application (SEP-837).""" |
| 1056 | + client_metadata = OAuthClientMetadata(redirect_uris=[AnyUrl("http://localhost:3030/callback")]) |
| 1057 | + |
| 1058 | + request = create_client_registration_request(None, client_metadata, "https://auth.example.com") |
| 1059 | + |
| 1060 | + assert json.loads(request.content)["application_type"] == "native" |
| 1061 | + |
| 1062 | + |
| 1063 | +def test_create_client_registration_request_preserves_explicit_application_type(): |
| 1064 | + """An explicit application_type is sent as-is, without inference overriding it.""" |
| 1065 | + client_metadata = OAuthClientMetadata( |
| 1066 | + redirect_uris=[AnyUrl("http://localhost:3030/callback")], application_type="web" |
| 1067 | + ) |
| 1068 | + |
| 1069 | + request = create_client_registration_request(None, client_metadata, "https://auth.example.com") |
| 1070 | + |
| 1071 | + assert json.loads(request.content)["application_type"] == "web" |
| 1072 | + |
| 1073 | + |
| 1074 | +def test_create_client_registration_request_omits_ambiguous_application_type(): |
| 1075 | + """Redirect URIs that mix native and web styles leave application_type unset.""" |
| 1076 | + client_metadata = OAuthClientMetadata( |
| 1077 | + redirect_uris=[ |
| 1078 | + AnyUrl("http://localhost:3030/callback"), |
| 1079 | + AnyUrl("https://app.example.com/callback"), |
| 1080 | + ] |
| 1081 | + ) |
| 1082 | + |
| 1083 | + request = create_client_registration_request(None, client_metadata, "https://auth.example.com") |
| 1084 | + |
| 1085 | + assert "application_type" not in json.loads(request.content) |
| 1086 | + |
| 1087 | + |
1031 | 1088 | class TestAuthFlow: |
1032 | 1089 | """Test the auth flow in httpx.""" |
1033 | 1090 |
|
|
0 commit comments