Skip to content

Commit 0cf2026

Browse files
authored
Fix Keycloak double-slash URL bug (#61121) (#61305)
* Fix Keycloak double-slash URL bug (#61121) - Normalize server_url in _get_token_url to prevent double-slashes - Add .rstrip('/') to handle trailing slashes in server_url configuration - Add comprehensive tests for URL normalization scenarios - Resolves compatibility issue with Keycloak 26.4+ strict path validation When server_url has a trailing slash (e.g., 'https://host/auth/'), the previous implementation would create invalid URLs with double-slashes (e.g., 'https://host/auth//realms/...'), which Keycloak 26.4+ rejects with HTTP 400 'missingNormalization' error. This fix allows users to configure server_url with or without trailing slashes while ensuring properly normalized URLs are always generated. Fixes #61121 * Refactor URL normalization tests to use parametrize - Consolidate 4 separate test methods into a single parametrized test - Improves maintainability and reduces code duplication - Covers same scenarios: no trailing slash, single slash, multiple slashes, root path
1 parent 784e1a9 commit 0cf2026

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ def _is_batch_authorized(
412412

413413
@staticmethod
414414
def _get_token_url(server_url, realm):
415-
return f"{server_url}/realms/{realm}/protocol/openid-connect/token"
415+
# Normalize server_url to avoid double slashes (required for Keycloak 26.4+ strict path validation).
416+
return f"{server_url.rstrip('/')}/realms/{realm}/protocol/openid-connect/token"
416417

417418
@staticmethod
418419
def _get_payload(client_id: str, permission: str, attributes: dict[str, str] | None = None):

providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,29 @@ def test_get_keycloak_client_with_no_credentials(self, mock_keycloak_openid, aut
559559
client_secret_key="client_secret",
560560
)
561561
assert client == mock_keycloak_openid.return_value
562+
563+
@pytest.mark.parametrize(
564+
("server_url", "expected_url"),
565+
[
566+
(
567+
"https://keycloak.example.com/auth",
568+
"https://keycloak.example.com/auth/realms/myrealm/protocol/openid-connect/token",
569+
),
570+
(
571+
"https://keycloak.example.com/auth/",
572+
"https://keycloak.example.com/auth/realms/myrealm/protocol/openid-connect/token",
573+
),
574+
(
575+
"https://keycloak.example.com/auth///",
576+
"https://keycloak.example.com/auth/realms/myrealm/protocol/openid-connect/token",
577+
),
578+
(
579+
"https://keycloak.example.com/",
580+
"https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token",
581+
),
582+
],
583+
)
584+
def test_get_token_url_normalization(self, auth_manager, server_url, expected_url):
585+
"""Test that _get_token_url normalizes server_url by stripping trailing slashes."""
586+
token_url = auth_manager._get_token_url(server_url, "myrealm")
587+
assert token_url == expected_url

0 commit comments

Comments
 (0)