@@ -114,6 +114,46 @@ def test_redirect_path_normalization(self, jwt_verifier):
114114 )
115115 assert proxy2 ._redirect_path == "/auth/callback"
116116
117+ async def test_authorize_url_with_ampersand_separator (self , jwt_verifier ):
118+ """Test that authorize builds URLs with & separator when upstream endpoint has existing query parameters."""
119+ # Test case: upstream endpoint with existing query parameters
120+ proxy = OAuthProxy (
121+ upstream_authorization_endpoint = "https://auth.example.com/authorize?version=2.0" ,
122+ upstream_token_endpoint = "https://auth.example.com/token" ,
123+ upstream_client_id = "client-123" ,
124+ upstream_client_secret = "secret-456" ,
125+ token_verifier = jwt_verifier ,
126+ base_url = "https://myserver.com" ,
127+ )
128+
129+ client = OAuthClientInformationFull (
130+ client_id = "test-client" ,
131+ client_secret = "test-secret" ,
132+ redirect_uris = [AnyUrl ("http://localhost:54321/callback" )],
133+ )
134+
135+ params = AuthorizationParams (
136+ redirect_uri = AnyUrl ("http://localhost:54321/callback" ),
137+ redirect_uri_provided_explicitly = True ,
138+ state = "client-state" ,
139+ code_challenge = "challenge" ,
140+ scopes = ["read" ],
141+ )
142+
143+ # Should use "&" separator
144+ redirect_url = await proxy .authorize (client , params )
145+ parsed = urlparse (redirect_url )
146+ query_params = parse_qs (parsed .query )
147+
148+ assert parsed .scheme == "https"
149+ assert parsed .netloc == "auth.example.com"
150+ assert parsed .path == "/authorize"
151+ # Params in the original url are kept
152+ assert "version" in query_params
153+ assert query_params ["version" ] == ["2.0" ]
154+ # New params added correctly
155+ assert query_params ["response_type" ] == ["code" ]
156+
117157 def test_dcr_always_enabled (self , jwt_verifier ):
118158 """Test that DCR is always enabled for OAuth Proxy."""
119159 proxy = OAuthProxy (
0 commit comments