This is an automated email from the ASF dual-hosted git repository.

smolnar82 pushed a commit to branch knox_idf
in repository https://gitbox.apache.org/repos/asf/knox.git

commit c8af244e46cadf2e1caddeb4989d92677a1dc41d
Author: Sandor Molnar <[email protected]>
AuthorDate: Tue Aug 11 14:53:40 2026 +0200

    KNOX-3414: E2E tests now adjusted to cover the previous Tier 1/2/3 bug 
fixes and changes
---
 .../build/conf/topologies/knoxidf-ldap.xml         |  8 +++
 .../build/conf/topologies/knoxidf-token.xml        |  7 ++
 .github/workflows/tests/test_knoxidf.py            | 75 ++++++++++------------
 3 files changed, 49 insertions(+), 41 deletions(-)

diff --git a/.github/workflows/build/conf/topologies/knoxidf-ldap.xml 
b/.github/workflows/build/conf/topologies/knoxidf-ldap.xml
index f3f795467..e7c3cd45d 100644
--- a/.github/workflows/build/conf/topologies/knoxidf-ldap.xml
+++ b/.github/workflows/build/conf/topologies/knoxidf-ldap.xml
@@ -66,6 +66,14 @@
             <name>knoxidf.client.registration.anonymous.allowed</name>
             <value>true</value>
         </param>
+        <param>
+            <!-- Skipping the user-consent screen is a server-side deployment 
decision, never a
+                 client-supplied request parameter (a client must not be able 
to bypass consent by
+                 sending auto_consent=true). This sample/CI topology opts in 
so the automated
+                 authorization-code flow tests can complete without driving 
the consent UI. -->
+            <name>knoxidf.auto.consent.enabled</name>
+            <value>true</value>
+        </param>
         <param>
             <name>token.exchange.topology.name</name>
             <value>knoxidf-token</value>
diff --git a/.github/workflows/build/conf/topologies/knoxidf-token.xml 
b/.github/workflows/build/conf/topologies/knoxidf-token.xml
index fdc11d62b..a1858b509 100644
--- a/.github/workflows/build/conf/topologies/knoxidf-token.xml
+++ b/.github/workflows/build/conf/topologies/knoxidf-token.xml
@@ -27,6 +27,13 @@
             <name>knoxidf.knox.token.limit.per.user</name>
             <value>-1</value>
         </param>
+        <param>
+            <!-- Skipping the user-consent screen is a server-side deployment 
decision, never a
+                 client-supplied request parameter. Kept in sync with 
knoxidf-ldap.xml so the
+                 KNOXIDF service consents consistently across both topologies. 
-->
+            <name>knoxidf.auto.consent.enabled</name>
+            <value>true</value>
+        </param>
     </service>
     <service>
         <role>KNOXTOKEN</role>
diff --git a/.github/workflows/tests/test_knoxidf.py 
b/.github/workflows/tests/test_knoxidf.py
index cbbbacaf2..56df6f312 100644
--- a/.github/workflows/tests/test_knoxidf.py
+++ b/.github/workflows/tests/test_knoxidf.py
@@ -26,7 +26,6 @@ from common_utils import (
     knox_get,
     knox_post,
     get_token_claim,
-    get_token_id_display_text,
 )
 
 
@@ -70,9 +69,10 @@ class TestKnoxIDF(unittest.TestCase):
             ["authorization_code", "refresh_token"],
         )
         self.assertEqual(config.get("id_token_signing_alg_values_supported"), 
["RS256"])
+        # DEFAULT_SCOPES is an ImmutableSet, so discovery emits it in 
insertion order.
         self.assertEqual(
             config.get("scopes_supported"),
-            ["openid", "email", "profile", "offline_access"],
+            ["openid", "profile", "email", "offline_access"],
         )
 
     def test_client_credentials_flow(self):
@@ -107,14 +107,14 @@ class TestKnoxIDF(unittest.TestCase):
         # 1. Register client
         client_id, client_secret = self._register_test_client()
 
-        # 2. Authorize (with Basic Auth for the user 'guest')
+        # 2. Authorize (with Basic Auth for the user 'guest'). Consent is 
auto-granted by the
+        # server (knoxidf.auto.consent.enabled=true in the topology), not by 
any client parameter.
         params = {
             "response_type": "code",
             "client_id": client_id,
             "redirect_uri": "http://localhost/callback";,
             "scope": "openid offline_access",
             "state": "test_state",
-            "auto_consent": "true"
         }
         code = self._authorize_get_code(params, expect_state="test_state")
 
@@ -139,8 +139,7 @@ class TestKnoxIDF(unittest.TestCase):
 
         refresh_token = tokens["refresh_token"]
         print(f"Refresh token: {refresh_token}")
-        refresh_token_id = get_token_claim(refresh_token, 'knox.id')
-        print(f"Refresh token knox.id: {refresh_token_id}")
+        print(f"Refresh token knox.id: {get_token_claim(refresh_token, 
'knox.id')}")
 
         # 4. Refresh the token (rotation)
         print("Refreshing token...")
@@ -174,11 +173,12 @@ class TestKnoxIDF(unittest.TestCase):
             verify=False,
             headers={"Accept": "application/json"},
         )
+        # On the refresh_token grant, JWTFederationFilter pulls the 
refresh_token from the
+        # request body and validates it as an auth credential before 
TokenResource runs.
+        # Rotation revoked this token, so that filter-level check fails and 
returns 401
+        # Unauthorized (a plain sendError HTML page, not the JSON 
invalid_grant body the
+        # resource would emit) -- the request never reaches handleRefreshToken.
         self.assertEqual(response.status_code, 401)
-        error_info = response.json()
-        display_id = get_token_id_display_text(refresh_token_id)
-        self.assertEqual(error_info["status"], "401")
-        self.assertIn(f"Unknown token: {display_id}", error_info["message"])
 
     def test_authorization_code_flow_pkce_s256(self):
         """
@@ -198,7 +198,6 @@ class TestKnoxIDF(unittest.TestCase):
             "redirect_uri": "http://localhost/callback";,
             "scope": "openid",
             "state": "pkce_state",
-            "auto_consent": "true",
             "code_challenge": code_challenge,
             "code_challenge_method": "S256"
         }
@@ -219,44 +218,39 @@ class TestKnoxIDF(unittest.TestCase):
         tokens = response.json()
         self.assertIn("access_token", tokens)
 
-    def test_authorization_code_flow_pkce_plain(self):
+    def test_authorization_code_flow_pkce_plain_rejected(self):
         """
-        Test OIDC Authorization Code Flow with PKCE (plain).
+        The 'plain' PKCE code_challenge_method offers no protection and is 
rejected: the authorize
+        endpoint must refuse to issue a code, returning invalid_request rather 
than redirecting.
         """
         # 1. Register client
-        client_id, client_secret = self._register_test_client()
+        client_id, _ = self._register_test_client()
 
-        # 2. PKCE Setup
-        code_verifier = "some-plain-verifier"
-        code_challenge = code_verifier
+        # 2. PKCE Setup ('plain': challenge == verifier)
+        code_challenge = "some-plain-verifier"
 
-        # 3. Authorize
+        # 3. Authorize with the unsupported method -> 400 invalid_request, no 
redirect
+        auth_url = f"{self.knoxidf_ldap_url}knoxidf/api/v1/authorize"
         params = {
             "response_type": "code",
             "client_id": client_id,
             "redirect_uri": "http://localhost/callback";,
             "scope": "openid",
             "state": "pkce_plain_state",
-            "auto_consent": "true",
             "code_challenge": code_challenge,
             "code_challenge_method": "plain"
         }
-        code = self._authorize_get_code(params)
-
-        # 4. Token Exchange
-        token_url = f"{self.knoxidf_token_url}knoxidf/api/v1/token"
-        data = {
-            "grant_type": "authorization_code",
-            "code": code,
-            "redirect_uri": "http://localhost/callback";,
-            "client_id": client_id,
-            "client_secret": client_secret,
-            "code_verifier": code_verifier
-        }
-        response = knox_post(token_url, data=data)
-        self.assertEqual(response.status_code, 200)
-        tokens = response.json()
-        self.assertIn("access_token", tokens)
+        response = knox_get(
+            auth_url,
+            params=params,
+            auth=(self.username, self.password),
+            verify=False,
+            allow_redirects=False,
+        )
+        self.assertEqual(response.status_code, 400)
+        error_info = response.json()
+        self.assertEqual(error_info["error"], "invalid_request")
+        self.assertIn("S256", error_info["error_description"])
 
     def test_authorization_code_flow_pkce_failure(self):
         """
@@ -273,7 +267,6 @@ class TestKnoxIDF(unittest.TestCase):
             "redirect_uri": "http://localhost/callback";,
             "scope": "openid",
             "state": "pkce_fail",
-            "auto_consent": "true",
             "code_challenge": code_challenge,
             "code_challenge_method": "S256"
         }
@@ -281,7 +274,7 @@ class TestKnoxIDF(unittest.TestCase):
 
         token_url = f"{self.knoxidf_token_url}knoxidf/api/v1/token"
 
-        # 1. Invalid verifier
+        # 1. Invalid verifier -> invalid_grant (HTTP 400)
         data = {
             "grant_type": "authorization_code",
             "code": code,
@@ -291,13 +284,13 @@ class TestKnoxIDF(unittest.TestCase):
             "code_verifier": "wrong-verifier"
         }
         response = knox_post(token_url, data=data)
-        self.assertEqual(response.status_code, 401)
+        self.assertEqual(response.status_code, 400)
         self.assertIn("Invalid code_verifier", 
response.json()["error_description"])
 
-        # Note: the code is revoked after first use, so we need a new one for 
the next test
+        # A code that fails validation is not consumed, but fetch a fresh one 
for a clean scenario.
         code = self._authorize_get_code(params)
 
-        # 2. Missing verifier
+        # 2. Missing verifier -> invalid_grant (HTTP 400)
         data = {
             "grant_type": "authorization_code",
             "code": code,
@@ -306,7 +299,7 @@ class TestKnoxIDF(unittest.TestCase):
             "client_secret": client_secret
         }
         response = knox_post(token_url, data=data)
-        self.assertEqual(response.status_code, 401)
+        self.assertEqual(response.status_code, 400)
         self.assertIn("Missing code_verifier", 
response.json()["error_description"])
 
     def _register_test_client(self):

Reply via email to