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

quantranhong1999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 96b619c4e0f78a5f81cff986da060a95234908df
Author: Quan Tran <[email protected]>
AuthorDate: Thu Aug 13 09:46:08 2026 +0700

    JAMES-4210 Align ManageSieve OAuth failure flow with RFC 7628
    
    Assert that invalid OAuth tokens produce a JSON SASL challenge, require the 
client acknowledgement prescribed by RFC 7628, and then terminate 
authentication with NO.
---
 .../apache/james/managesieveserver/OIDCTest.java   | 66 +++++++++++++---------
 1 file changed, 38 insertions(+), 28 deletions(-)

diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/OIDCTest.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/OIDCTest.java
index fd2cb0b282..1985303b3e 100644
--- 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/OIDCTest.java
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/OIDCTest.java
@@ -20,6 +20,7 @@
 package org.apache.james.managesieveserver;
 
 import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.tree.ImmutableNode;
@@ -122,7 +123,7 @@ public class OIDCTest {
         @Test
         void oauthbearerLoginWithValidTokenAndContinuationShouldSucceed() 
throws Exception {
             this.client.sendCommand("AUTHENTICATE \"OAUTHBEARER\"");
-            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readSaslChallenge();
             
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.CONTINUATION);
             
Assertions.assertThat(continuationResponse.explanation().get()).isEqualTo("");
 
@@ -134,7 +135,7 @@ public class OIDCTest {
         @Test
         void oauthbearerLoginWithValidTokenAndContinuationCanBeAborted() 
throws Exception {
             this.client.sendCommand("AUTHENTICATE \"OAUTHBEARER\"");
-            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readSaslChallenge();
             
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.CONTINUATION);
             
Assertions.assertThat(continuationResponse.explanation().get()).isEqualTo("");
 
@@ -147,8 +148,7 @@ public class OIDCTest {
         @Test
         void oauthbearerLoginWithInvalidTokenShouldNotSucceed() throws 
Exception {
             this.client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
INVALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertOauthFailure(this.client);
         }
 
         @Test
@@ -161,7 +161,7 @@ public class OIDCTest {
         @Test
         void xoauth2LoginWithValidTokenAndContinuationShouldSucceed() throws 
Exception {
             this.client.sendCommand("AUTHENTICATE \"XOAUTH2\"");
-            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readSaslChallenge();
             
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.CONTINUATION);
             
Assertions.assertThat(continuationResponse.explanation().get()).isEqualTo("");
 
@@ -173,7 +173,7 @@ public class OIDCTest {
         @Test
         void xoauth2LoginWithValidTokenAndContinuationCanBeAborted() throws 
Exception {
             this.client.sendCommand("AUTHENTICATE \"XOAUTH2\"");
-            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readSaslChallenge();
             
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.CONTINUATION);
             
Assertions.assertThat(continuationResponse.explanation().get()).isEqualTo("");
 
@@ -186,8 +186,7 @@ public class OIDCTest {
         @Test
         void xoauth2LoginWithInvalidTokenShouldNotSucceed() throws Exception {
             this.client.sendCommand("AUTHENTICATE \"XOAUTH2\" \"" + 
INVALID_XOAUTH2_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertOauthFailure(this.client);
         }
     }
 
@@ -264,8 +263,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertOauthFailure(client);
         }
 
         @Test
@@ -295,8 +293,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertOauthFailure(client);
         }
 
         @Test
@@ -326,8 +323,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertImmediateOauthFailure(client);
         }
 
         @Test
@@ -357,8 +353,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertOauthFailure(client);
         }
 
         @Test
@@ -386,8 +381,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertImmediateOauthFailure(client);
         }
 
         @Test
@@ -415,8 +409,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertImmediateOauthFailure(client);
         }
     }
 
@@ -503,8 +496,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertOauthFailure(client);
         }
 
         @Test
@@ -533,8 +525,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertOauthFailure(client);
         }
 
         @Test
@@ -561,8 +552,7 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertImmediateOauthFailure(client);
         }
 
         @Test
@@ -589,8 +579,28 @@ public class OIDCTest {
             client.readResponse();
 
             client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
-            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
-            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            assertImmediateOauthFailure(client);
         }
     }
+
+    private static void assertOauthFailure(ManageSieveClient client) throws 
Exception {
+        ManageSieveClient.ServerResponse errorChallenge = 
client.readSaslChallenge();
+        
Assertions.assertThat(errorChallenge.responseType()).isEqualTo(ManageSieveClient.ResponseType.CONTINUATION);
+        String errorPayload = errorChallenge.explanation()
+            .map(Base64.getDecoder()::decode)
+            .map(bytes -> new String(bytes, StandardCharsets.UTF_8))
+            .orElseThrow();
+        Assertions.assertThat(errorPayload)
+            .contains("\"status\":\"invalid_token\"");
+
+        // RFC 7628 section 3.2.3 requires an acknowledgement before the 
terminal failure.
+        client.sendCommand("\"AQ==\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    private static void assertImmediateOauthFailure(ManageSieveClient client) 
throws Exception {
+        ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to