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

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

commit 0c8b5d20d40405a93621d25e3c70a7180c8b488f
Author: Felix Auringer <[email protected]>
AuthorDate: Fri Aug 15 08:05:13 2025 +0200

    test(managesieve): test authentication of managesieve server
---
 .../org/apache/james/jwt/OidcTokenFixture.java     |   2 +-
 server/protocols/protocols-managesieve/pom.xml     |  53 ++
 .../james/managesieveserver/AuthenticateTest.java  | 220 ++++++++
 .../james/managesieveserver/CapabilityTest.java    |  74 +++
 .../james/managesieveserver/ManageSieveClient.java | 104 ++++
 .../ManageSieveServerTestSystem.java               |  93 ++++
 .../apache/james/managesieveserver/OIDCTest.java   | 568 +++++++++++++++++++++
 .../src/test/resources/managesieveserver-oidc.xml  |  16 +
 .../src/test/resources/managesieveserver.xml       |   9 +
 9 files changed, 1138 insertions(+), 1 deletion(-)

diff --git 
a/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcTokenFixture.java 
b/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcTokenFixture.java
index d07646e5c4..dcf09137ba 100644
--- 
a/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcTokenFixture.java
+++ 
b/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcTokenFixture.java
@@ -107,7 +107,7 @@ public class OidcTokenFixture {
         "}";
 
     public static final String CLAIM = "email_address";
-    //  "email_address": "[email protected]"
+    public static final String USER_EMAIL_ADDRESS = "[email protected]";
     public static final String VALID_TOKEN = 
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Inc4MFBzNUlhc24tYUdXbXcyVHJ4RGlOY2FocEgyc1h6NXBxZGhBbDlIWGMifQ.eyJleHAiOjM5Mzk1MDYxNjcsImlhdCI6MTYzOTUwNTg2NywiYXV0aF90aW1lIjozNjM5NTA1ODQxLCJqdGkiOiJjMjQ5ZTBkNi1jY2JiLTRmZDAtODI5Yi04OTM1MjczN2YzZGIiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvcmVhbG0xIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjIwNDUyNzFiLWMxYmItNDJiOC1hMTkwLThlYWI1MmYzYmEwOSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFjY291bnQtY29uc29sZSIsIm5
 [...]
     public static final String VALID_TOKEN_HAS_NOT_KID = 
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjM5Mzk1MDYxNjcsImlhdCI6MTYzOTUwNTg2NywiYXV0aF90aW1lIjozNjM5NTA1ODQxLCJqdGkiOiJjMjQ5ZTBkNi1jY2JiLTRmZDAtODI5Yi04OTM1MjczN2YzZGIiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvcmVhbG0xIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjIwNDUyNzFiLWMxYmItNDJiOC1hMTkwLThlYWI1MmYzYmEwOSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFjY291bnQtY29uc29sZSIsIm5vbmNlIjoiNWUyOGJjNTAtODE5NS00NjM3LThmMWEtYWUzNWFlYTk0NTc1I
 [...]
     public static final String VALID_TOKEN_HAS_NOT_FOUND_KID = 
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im5vdEZvdW5kIn0.eyJleHAiOjM5Mzk1MDYxNjcsImlhdCI6MTYzOTUwNTg2NywiYXV0aF90aW1lIjozNjM5NTA1ODQxLCJqdGkiOiJjMjQ5ZTBkNi1jY2JiLTRmZDAtODI5Yi04OTM1MjczN2YzZGIiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvcmVhbG0xIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjIwNDUyNzFiLWMxYmItNDJiOC1hMTkwLThlYWI1MmYzYmEwOSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFjY291bnQtY29uc29sZSIsIm5vbmNlIjoiNWUyOGJjNTAtODE5NS00
 [...]
diff --git a/server/protocols/protocols-managesieve/pom.xml 
b/server/protocols/protocols-managesieve/pom.xml
index 3a96075ebc..0c7f1e5b1d 100644
--- a/server/protocols/protocols-managesieve/pom.xml
+++ b/server/protocols/protocols-managesieve/pom.xml
@@ -13,14 +13,42 @@
     <name>Apache James :: Server :: ManageSieve</name>
 
     <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-file</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-filesystem-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-filesystem-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-jwt</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-protocols-library</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-protocols-library</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-util</artifactId>
@@ -30,6 +58,16 @@
             <artifactId>testing-base</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.protocols.groupId}</groupId>
+            <artifactId>protocols-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.protocols.groupId}</groupId>
+            <artifactId>protocols-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${james.protocols.groupId}</groupId>
             <artifactId>protocols-managesieve</artifactId>
@@ -38,6 +76,11 @@
             <groupId>${james.protocols.groupId}</groupId>
             <artifactId>protocols-netty</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-handler</artifactId>
@@ -54,6 +97,16 @@
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-configuration2</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mock-server</groupId>
+            <artifactId>mockserver-netty</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/AuthenticateTest.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/AuthenticateTest.java
new file mode 100644
index 0000000000..b9d0829aa3
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/AuthenticateTest.java
@@ -0,0 +1,220 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+public class AuthenticateTest {
+    private ManageSieveClient client;
+    private final ManageSieveServerTestSystem testSystem;
+
+    public AuthenticateTest() throws Exception {
+        this.testSystem = new ManageSieveServerTestSystem();
+    }
+
+    @BeforeEach
+    void setUp() throws Exception {
+        this.testSystem.setUp();
+        this.client = new ManageSieveClient();
+        this.client.connect(this.testSystem.getBindedIP(), 
this.testSystem.getBindedPort());
+        this.client.readResponse();
+    }
+
+    @AfterEach
+    void tearDown() {
+        this.testSystem.manageSieveServer.destroy();
+    }
+
+    @Test
+    void plainLoginWithCorrectCredentialsShouldSucceed() throws IOException {
+        this.authenticatePlain();
+    }
+
+    @Test
+    void plainLoginWithWrongPasswordShouldNotSucceed() throws IOException {
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "\0" + 
ManageSieveServerTestSystem.PASSWORD + "wrong");
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\" \"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    @Test
+    void plainLoginWithNotExistingUserShouldNotSucceed() throws IOException {
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "not-existing" + "\0" + 
"pwd");
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\" \"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    @Test
+    void plainLoginWithoutPasswordShouldNotSucceed() throws IOException {
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "\0");
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\" \"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    // The SASL PLAIN standard (https://datatracker.ietf.org/doc/html/rfc4616) 
defines the following message:
+    // message = [authzid] UTF8NUL authcid UTF8NUL passwd
+    // The current code is more lenient.
+    @Disabled
+    @Test
+    void plainLoginWithMalformedMessageShouldNotSucceed() throws IOException {
+        String initialClientResponse = 
(ManageSieveServerTestSystem.USERNAME.asString() + "\0" + 
ManageSieveServerTestSystem.PASSWORD);
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\" \"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    @Test
+    void plainLoginWithoutMechanismQuotesShouldNotSucceed() throws IOException 
{
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "\0" + 
ManageSieveServerTestSystem.PASSWORD);
+        this.client.sendCommand("AUTHENTICATE PLAIN \"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    @Test
+    void plainLoginWithoutInitialResponseQuotesShouldNotSucceed() throws 
IOException {
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "\0" + 
ManageSieveServerTestSystem.PASSWORD);
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\" " + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8)));
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    @Test
+    void plainLoginWithContinuationShouldSucceed() throws IOException {
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\"");
+        ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        
Assertions.assertThat(continuationResponse.responseLines()).containsExactly("\"\"");
+
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "\0" + 
ManageSieveServerTestSystem.PASSWORD);
+        this.client.sendCommand("\"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+    }
+
+    @Test
+    void plainLoginWithContinuationCanBeAborted() throws IOException {
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\"");
+        ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        
Assertions.assertThat(continuationResponse.responseLines()).containsExactly("\"\"");
+
+        this.client.sendCommand("\"*\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        
Assertions.assertThat(authenticationResponse.explanation()).get().isEqualTo("authentication
 aborted");
+    }
+
+    @Test
+    void doubleAuthenticationShouldFail() throws IOException {
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "\0" + 
ManageSieveServerTestSystem.PASSWORD);
+        String command = "AUTHENTICATE \"PLAIN\" \"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"";
+
+        this.client.sendCommand(command);
+        ManageSieveClient.ServerResponse firstAuthenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(firstAuthenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+
+        this.client.sendCommand(command);
+        ManageSieveClient.ServerResponse secondAuthenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(secondAuthenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        
Assertions.assertThat(secondAuthenticationResponse.explanation()).get().isEqualTo("already
 authenticated");
+    }
+
+    @Test
+    void unauthenticateInUnauthenticatedStateShouldFail() throws IOException {
+        this.client.sendCommand("UNAUTHENTICATE");
+        ManageSieveClient.ServerResponse response = this.client.readResponse();
+        
Assertions.assertThat(response.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    @Test
+    void unauthenticateInAuthenticatedStateShouldSucceed() throws IOException {
+        this.authenticatePlain();
+
+        this.client.sendCommand("UNAUTHENTICATE");
+        ManageSieveClient.ServerResponse response = this.client.readResponse();
+        
Assertions.assertThat(response.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+    }
+
+    @Test
+    void authenticatedStateUnlocksNewCommands() throws IOException {
+        this.client.sendCommand("LISTSCRIPTS");
+        ManageSieveClient.ServerResponse unauthenticatedResponse = 
this.client.readResponse();
+        
Assertions.assertThat(unauthenticatedResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+
+        this.authenticatePlain();
+
+        this.client.sendCommand("LISTSCRIPTS");
+        ManageSieveClient.ServerResponse authenticatedResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticatedResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+
+        this.client.sendCommand("UNAUTHENTICATE");
+        ManageSieveClient.ServerResponse response = this.client.readResponse();
+        
Assertions.assertThat(response.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+
+        this.client.sendCommand("LISTSCRIPTS");
+        ManageSieveClient.ServerResponse loggedOutResponse = 
this.client.readResponse();
+        
Assertions.assertThat(loggedOutResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+    }
+
+    // The server actually disconnects but isConnected still returns True.
+    // Even when adding a delay, it still returns True.
+    // There is probably something else broken with this test.
+    @Disabled
+    @Test
+    void logoutShouldWorkInUnauthenticatedState() throws IOException, 
InterruptedException {
+        this.client.sendCommand("LOGOUT");
+        ManageSieveClient.ServerResponse response = this.client.readResponse();
+        
Assertions.assertThat(response.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        Assertions.assertThat(this.client.isConnected()).isFalse();
+    }
+
+    // The server actually disconnects but isConnected still returns True.
+    // Even when adding a delay, it still returns True.
+    // There is probably something else broken with this test.
+    @Disabled
+    @Test
+    void logoutShouldWorkInAuthenticatedState() throws IOException, 
InterruptedException {
+        this.authenticatePlain();
+
+        this.client.sendCommand("LOGOUT");
+        ManageSieveClient.ServerResponse response = this.client.readResponse();
+        
Assertions.assertThat(response.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        Assertions.assertThat(this.client.isConnected()).isFalse();
+    }
+
+    void authenticatePlain() throws IOException {
+        String initialClientResponse = ("\0" + 
ManageSieveServerTestSystem.USERNAME.asString() + "\0" + 
ManageSieveServerTestSystem.PASSWORD);
+        this.client.sendCommand("AUTHENTICATE \"PLAIN\" \"" + 
Base64.getEncoder().encodeToString(initialClientResponse.getBytes(StandardCharsets.UTF_8))
 + "\"");
+        ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+        
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+    }
+}
diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/CapabilityTest.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/CapabilityTest.java
new file mode 100644
index 0000000000..f13ffe3d8f
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/CapabilityTest.java
@@ -0,0 +1,74 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+class CapabilityTest {
+    private final ManageSieveServerTestSystem testSystem;
+
+    public CapabilityTest() throws Exception {
+        this.testSystem = new ManageSieveServerTestSystem();
+    }
+
+    @AfterEach
+    void tearDown() {
+        this.testSystem.manageSieveServer.destroy();
+    }
+
+    @Test
+    void shouldAnnounceOnlyPlainAuthenticationWithDefaultConfig() throws 
Exception {
+        this.testSystem.setUp();
+
+        ManageSieveClient client = new ManageSieveClient();
+        client.connect(this.testSystem.getBindedIP(), 
this.testSystem.getBindedPort());
+        ManageSieveClient.ServerResponse initialGreeting = 
client.readResponse();
+        
Assertions.assertThat(getSASLMechanisms(initialGreeting)).containsExactlyInAnyOrder("PLAIN");
+
+        client.sendCommand("CAPABILITY");
+        ManageSieveClient.ServerResponse capabilityResponse = 
client.readResponse();
+        
Assertions.assertThat(getSASLMechanisms(capabilityResponse)).containsExactlyInAnyOrder("PLAIN");
+    }
+
+    @Test
+    void shouldAnnouncePlainAndOauthWhenConfigured() throws Exception {
+        this.testSystem.setUp("managesieveserver-oidc.xml");
+
+        ManageSieveClient client = new ManageSieveClient();
+        client.connect(this.testSystem.getBindedIP(), 
this.testSystem.getBindedPort());
+        ManageSieveClient.ServerResponse initialGreeting = 
client.readResponse();
+        
Assertions.assertThat(getSASLMechanisms(initialGreeting)).containsExactlyInAnyOrder("PLAIN",
 "XOAUTH2", "OAUTHBEARER");
+
+        client.sendCommand("CAPABILITY");
+        ManageSieveClient.ServerResponse capabilityResponse = 
client.readResponse();
+        
Assertions.assertThat(getSASLMechanisms(capabilityResponse)).containsExactlyInAnyOrder("PLAIN",
 "XOAUTH2", "OAUTHBEARER");
+    }
+
+    private String[] getSASLMechanisms(ManageSieveClient.ServerResponse 
response) {
+        String saslLine = Assertions.assertThat(response.responseLines())
+            .filteredOn(line -> line.startsWith("\"SASL\""))
+            .hasSize(1)
+            .first()
+            .actual();
+        return saslLine.substring("\"SASL\" \"".length(), saslLine.length() - 
1).split(" ");
+    }
+}
diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveClient.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveClient.java
new file mode 100644
index 0000000000..e712489b70
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveClient.java
@@ -0,0 +1,104 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Optional;
+
+import org.apache.commons.lang3.EnumUtils;
+import org.apache.commons.net.SocketClient;
+import org.apache.commons.net.io.CRLFLineReader;
+
+public class ManageSieveClient extends SocketClient {
+    private static final String ENCODING = StandardCharsets.UTF_8.name();
+
+    enum ResponseType {
+        BYE,
+        NO,
+        OK;
+    }
+
+    record ServerResponse(
+        ResponseType responseType,
+        Optional<String> responseCode,
+        Optional<String> explanation,
+        ArrayList<String> responseLines
+    ) {}
+
+    private BufferedReader reader;
+    private BufferedWriter writer;
+
+    @Override
+    protected void _connectAction_() throws IOException {
+        super._connectAction_();
+        this.reader = new CRLFLineReader(new InputStreamReader(_input_, 
ENCODING));
+        this.writer = new BufferedWriter(new OutputStreamWriter(_output_, 
ENCODING));
+    }
+
+    @Override
+    public void disconnect() throws IOException {
+        super.disconnect();
+        this.reader = null;
+        this.writer = null;
+    }
+
+    public ServerResponse readResponse() throws IOException {
+        ServerResponse response = null;
+        ArrayList<String> lines = new ArrayList<>();
+        while (response == null) {
+            String line = this.reader.readLine();
+            String[] tokens = line.split(" ", 3);
+            if (EnumUtils.isValidEnumIgnoreCase(ResponseType.class, 
tokens[0])) {
+                ResponseType responseType = 
EnumUtils.getEnumIgnoreCase(ResponseType.class, tokens[0]);
+                Optional<String> responseCode = Optional.empty();
+                Optional<String> explanation = Optional.empty();
+                if (tokens.length == 2 && tokens[1].startsWith("(")) {
+                    responseCode = Optional.of(tokens[1].substring(1, 
tokens[1].length() - 1));
+                } else if (tokens.length == 2 && !tokens[1].startsWith("(")) {
+                    explanation = Optional.of(tokens[1]);
+                } else if (tokens.length == 3 && tokens[1].startsWith("(")) {
+                    responseCode = Optional.of(tokens[1].substring(1, 
tokens[1].length() - 1));
+                    explanation = Optional.of(tokens[2]);
+                } else if (tokens.length == 3 && !tokens[1].startsWith("(")) {
+                    explanation = Optional.of(tokens[1] + " " + tokens[2]);
+                }
+                if (explanation.isPresent() && explanation.get().charAt(0) == 
'"' && explanation.get().charAt(explanation.get().length() - 1) == '"') {
+                    explanation = Optional.of(explanation.get().substring(1, 
explanation.get().length() - 1));
+                }
+
+                response = new ServerResponse(responseType, responseCode, 
explanation, lines);
+            } else {
+                lines.addLast(line);
+            }
+        }
+        return response;
+    }
+
+    public void sendCommand(String command) throws IOException {
+        this.writer.write(command + "\r\n");
+        this.writer.flush();
+    }
+}
diff --git 
a/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveServerTestSystem.java
 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveServerTestSystem.java
new file mode 100644
index 0000000000..185c40e48e
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/ManageSieveServerTestSystem.java
@@ -0,0 +1,93 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver;
+
+import java.net.InetAddress;
+
+import org.apache.commons.configuration2.HierarchicalConfiguration;
+import org.apache.commons.configuration2.tree.ImmutableNode;
+import org.apache.james.core.Username;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.filesystem.api.mock.MockFileSystem;
+import org.apache.james.managesieve.core.CoreProcessor;
+import org.apache.james.managesieve.jsieve.Parser;
+import org.apache.james.managesieve.transcode.ArgumentParser;
+import org.apache.james.managesieve.transcode.ManageSieveProcessor;
+import org.apache.james.managesieveserver.netty.ManageSieveServer;
+import org.apache.james.protocols.api.utils.ProtocolServerUtils;
+import org.apache.james.server.core.configuration.FileConfigurationProvider;
+import org.apache.james.sieverepository.file.SieveFileRepository;
+import org.apache.james.user.memory.MemoryUsersRepository;
+
+class ManageSieveServerTestSystem {
+    private static final int MAX_LINE_LENGTH = 8000;
+    private static final DomainList NO_DOMAIN_LIST = null;
+    public static final String PASSWORD = "bobpwd";
+    public static final Username USERNAME = Username.of("bob");
+
+
+    private ManageSieveProcessor manageSieveProcessor;
+    public ManageSieveServer manageSieveServer;
+    private MemoryUsersRepository usersRepository;
+    private MockFileSystem fileSystem;
+
+    public ManageSieveServerTestSystem() throws Exception {
+        this.usersRepository = 
MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST);
+        this.usersRepository.addUser(USERNAME, PASSWORD);
+        this.fileSystem = new MockFileSystem();
+        this.manageSieveProcessor = new ManageSieveProcessor(
+            new ArgumentParser(
+                new CoreProcessor(
+                    new SieveFileRepository(this.fileSystem),
+                    this.usersRepository,
+                    new Parser()
+                )
+            )
+        );
+    }
+
+    public void setUp(HierarchicalConfiguration<ImmutableNode> configuration) 
throws Exception {
+        this.fileSystem.clear();
+        this.manageSieveServer = new ManageSieveServer(
+            MAX_LINE_LENGTH,
+            this.manageSieveProcessor
+        );
+        this.manageSieveServer.setFileSystem(this.fileSystem);
+        this.manageSieveServer.configure(configuration);
+        this.manageSieveServer.init();
+    }
+
+    public void setUp(String configFilePath) throws Exception {
+        HierarchicalConfiguration<ImmutableNode> configuration = 
FileConfigurationProvider.getConfig(ClassLoader.getSystemResourceAsStream(configFilePath));
+        setUp(configuration);
+    }
+
+    public void setUp() throws Exception {
+        setUp("managesieveserver.xml");
+    }
+
+    public InetAddress getBindedIP() {
+        return new 
ProtocolServerUtils(this.manageSieveServer).retrieveBindedAddress().getAddress();
+    }
+
+    public int getBindedPort() {
+        return new 
ProtocolServerUtils(this.manageSieveServer).retrieveBindedAddress().getPort();
+    }
+}
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
new file mode 100644
index 0000000000..d331c24f71
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/java/org/apache/james/managesieveserver/OIDCTest.java
@@ -0,0 +1,568 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.configuration2.HierarchicalConfiguration;
+import org.apache.commons.configuration2.tree.ImmutableNode;
+import org.apache.james.jwt.OidcTokenFixture;
+import org.apache.james.protocols.api.OIDCSASLHelper;
+import org.apache.james.protocols.lib.mock.ConfigLoader;
+import org.apache.james.util.ClassLoaderUtils;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.mockserver.integration.ClientAndServer;
+import org.mockserver.model.HttpRequest;
+import org.mockserver.model.HttpResponse;
+
+public class OIDCTest {
+    private static final String DISCOVERY_URI_PATH = 
"/oidc/.well-known/openid-configuration";
+    private static final String JWKS_URI_PATH = "/oidc/jwks";
+    private static final String INTROSPECTION_URI_PATH = "/oidc/introspect";
+    private static final String SCOPE = "scope";
+    private static final String USERINFO_URI_PATH = "/oidc/userinfo";
+    public static final String VALID_XOAUTH2_INITIAL_CLIENT_RESPONSE = 
OIDCSASLHelper.generateEncodedXOauth2InitialClientResponse(
+        OidcTokenFixture.USER_EMAIL_ADDRESS,
+        OidcTokenFixture.VALID_TOKEN
+    );
+    public static final String VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE = 
OIDCSASLHelper.generateEncodedXOauth2InitialClientResponse(
+        OidcTokenFixture.USER_EMAIL_ADDRESS,
+        OidcTokenFixture.VALID_TOKEN
+    );
+    public static final String INVALID_XOAUTH2_INITIAL_CLIENT_RESPONSE = 
OIDCSASLHelper.generateEncodedXOauth2InitialClientResponse(
+        OidcTokenFixture.USER_EMAIL_ADDRESS,
+        OidcTokenFixture.INVALID_TOKEN
+    );
+    public static final String INVALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE = 
OIDCSASLHelper.generateEncodedXOauth2InitialClientResponse(
+        OidcTokenFixture.USER_EMAIL_ADDRESS,
+        OidcTokenFixture.INVALID_TOKEN
+    );
+
+    @Nested
+    @TestInstance(Lifecycle.PER_CLASS)
+    public class LocalValidation {
+        private ClientAndServer authServer;
+        private ManageSieveClient client;
+        private final ManageSieveServerTestSystem testSystem;
+        private final HierarchicalConfiguration<ImmutableNode> configuration;
+
+        public LocalValidation() throws Exception {
+            this.testSystem = new ManageSieveServerTestSystem();
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            this.configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            this.configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            this.configuration.addProperty("oidc.claim", 
OidcTokenFixture.CLAIM);
+            this.configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            this.configuration.addProperty("oidc.scope", SCOPE);
+        }
+
+        @BeforeEach
+        void setUp() throws Exception {
+            this.testSystem.setUp(this.configuration);
+            this.client = new ManageSieveClient();
+            this.client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            this.client.readResponse();
+        }
+
+        @AfterEach
+        void tearDown() {
+            this.testSystem.manageSieveServer.destroy();
+        }
+
+        @AfterAll
+        void finalTearDown() {
+            this.authServer.stop();
+        }
+
+        @Test
+        void oauthbearerLoginWithValidTokenShouldSucceed() throws Exception {
+            this.client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        }
+
+        @Test
+        void oauthbearerLoginWithValidTokenAndContinuationShouldSucceed() 
throws Exception {
+            this.client.sendCommand("AUTHENTICATE \"OAUTHBEARER\"");
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+            
Assertions.assertThat(continuationResponse.responseLines()).containsExactly("\"\"");
+
+            this.client.sendCommand("\"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        }
+
+        @Test
+        void oauthbearerLoginWithValidTokenAndContinuationCanBeAborted() 
throws Exception {
+            this.client.sendCommand("AUTHENTICATE \"OAUTHBEARER\"");
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+            
Assertions.assertThat(continuationResponse.responseLines()).containsExactly("\"\"");
+
+            this.client.sendCommand("\"*\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            
Assertions.assertThat(authenticationResponse.explanation()).get().isEqualTo("authentication
 aborted");
+        }
+
+        @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);
+        }
+
+        @Test
+        void xoauth2LoginWithValidTokenShouldSucceed() throws Exception {
+            this.client.sendCommand("AUTHENTICATE \"XOAUTH2\" \"" + 
VALID_XOAUTH2_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        }
+
+        @Test
+        void xoauth2LoginWithValidTokenAndContinuationShouldSucceed() throws 
Exception {
+            this.client.sendCommand("AUTHENTICATE \"XOAUTH2\"");
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+            
Assertions.assertThat(continuationResponse.responseLines()).containsExactly("\"\"");
+
+            this.client.sendCommand("\"" + 
VALID_XOAUTH2_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        }
+
+        @Test
+        void xoauth2LoginWithValidTokenAndContinuationCanBeAborted() throws 
Exception {
+            this.client.sendCommand("AUTHENTICATE \"XOAUTH2\"");
+            ManageSieveClient.ServerResponse continuationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(continuationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+            
Assertions.assertThat(continuationResponse.responseLines()).containsExactly("\"\"");
+
+            this.client.sendCommand("\"*\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
this.client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+            
Assertions.assertThat(authenticationResponse.explanation()).get().isEqualTo("authentication
 aborted");
+        }
+
+        @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);
+        }
+    }
+
+    @Nested
+    public class Introspection {
+        private final ManageSieveServerTestSystem testSystem;
+        private ClientAndServer authServer;
+
+        public Introspection() throws Exception {
+            this.testSystem = new ManageSieveServerTestSystem();
+        }
+
+        @AfterEach
+        void tearDown() {
+            this.testSystem.manageSieveServer.destroy();
+            this.authServer.stop();
+        }
+
+        @Test
+        void oauthbearerShouldSucceedWhenIntrospectReturnsActiveUser() throws 
Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(INTROSPECTION_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"active\": true, \"%s\": 
\"%s\"}", OidcTokenFixture.CLAIM, OidcTokenFixture.USER_EMAIL_ADDRESS), 
StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.introspection.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
INTROSPECTION_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenIntrospectReturnsInactiveUser() throws 
Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(INTROSPECTION_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"active\": false, \"%s\": 
\"%s\"}", OidcTokenFixture.CLAIM, OidcTokenFixture.USER_EMAIL_ADDRESS), 
StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.introspection.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
INTROSPECTION_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenIntrospectReturnsWrongActiveUser() 
throws Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(INTROSPECTION_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"active\": true, \"%s\": 
\"%s-wrong\"}", OidcTokenFixture.CLAIM, OidcTokenFixture.USER_EMAIL_ADDRESS), 
StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.introspection.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
INTROSPECTION_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenIntrospectDoesNotContainActiveField() 
throws Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(INTROSPECTION_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"%s\": \"%s\"}", 
OidcTokenFixture.CLAIM, OidcTokenFixture.USER_EMAIL_ADDRESS), 
StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.introspection.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
INTROSPECTION_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenIntrospectDoesNotContainUserField() 
throws Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(INTROSPECTION_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody("{\"active\": true}", StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.introspection.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
INTROSPECTION_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenIntrospectEndpointErrors() throws 
Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(INTROSPECTION_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(500));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.introspection.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
INTROSPECTION_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void 
oauthbearerIntrospectionValidationShouldFailWhenLocalValidationFails() throws 
Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(INTROSPECTION_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"active\": true, \"%s\": 
\"%s\"}", OidcTokenFixture.CLAIM, OidcTokenFixture.USER_EMAIL_ADDRESS), 
StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(500));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.introspection.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
INTROSPECTION_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+    }
+
+    @Nested
+    public class Userinfo {
+        private final ManageSieveServerTestSystem testSystem;
+        private ClientAndServer authServer;
+
+        public Userinfo() throws Exception {
+            this.testSystem = new ManageSieveServerTestSystem();
+        }
+
+        @AfterEach
+        void tearDown() {
+            this.testSystem.manageSieveServer.destroy();
+            this.authServer.stop();
+        }
+
+        @Test
+        void oauthbearerShouldSucceedWhenUserinfoClaimMatches() throws 
Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(USERINFO_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"%s\": \"%s\"}", 
OidcTokenFixture.CLAIM, OidcTokenFixture.USER_EMAIL_ADDRESS), 
StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.userinfo.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
USERINFO_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.OK);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenUserinfoClaimDiffers() throws Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(USERINFO_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"%s\": \"test\"}", 
OidcTokenFixture.CLAIM), StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.userinfo.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
USERINFO_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenUserinfoClaimIsMissing() throws 
Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(USERINFO_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{}"), StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.userinfo.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
USERINFO_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void oauthbearerShouldFailWhenUserinfoErrors() throws Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(USERINFO_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(500));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(OidcTokenFixture.JWKS_RESPONSE, 
StandardCharsets.UTF_8));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.userinfo.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
USERINFO_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+
+        @Test
+        void oauthbearerUserinfoValidationShouldFailWhenLocalValidationFails() 
throws Exception {
+            this.authServer = ClientAndServer.startClientAndServer(0);
+            this.authServer
+                .when(HttpRequest.request().withPath(USERINFO_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(200)
+                    .withHeader("Content-Type", "application/json")
+                    .withBody(String.format("{\"%s\": \"%s\"}", 
OidcTokenFixture.CLAIM, OidcTokenFixture.USER_EMAIL_ADDRESS), 
StandardCharsets.UTF_8));
+            this.authServer
+                .when(HttpRequest.request().withPath(JWKS_URI_PATH))
+                .respond(HttpResponse.response().withStatusCode(500));
+            HierarchicalConfiguration<ImmutableNode> configuration = 
ConfigLoader.getConfig(ClassLoaderUtils.getSystemResourceAsSharedStream("managesieveserver.xml"));
+            configuration.addProperty("oidc.jwksURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
JWKS_URI_PATH));
+            configuration.addProperty("oidc.claim", OidcTokenFixture.CLAIM);
+            configuration.addProperty("oidc.oidcConfigurationURL", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
DISCOVERY_URI_PATH));
+            configuration.addProperty("oidc.scope", SCOPE);
+            configuration.addProperty("oidc.userinfo.url", 
String.format("http://127.0.0.1:%s%s";, this.authServer.getLocalPort(), 
USERINFO_URI_PATH));
+            testSystem.setUp(configuration);
+
+            ManageSieveClient client = new ManageSieveClient();
+            client.connect(testSystem.getBindedIP(), 
testSystem.getBindedPort());
+            client.readResponse();
+
+            client.sendCommand("AUTHENTICATE \"OAUTHBEARER\" \"" + 
VALID_OAUTHBEARER_INITIAL_CLIENT_RESPONSE + "\"");
+            ManageSieveClient.ServerResponse authenticationResponse = 
client.readResponse();
+            
Assertions.assertThat(authenticationResponse.responseType()).isEqualTo(ManageSieveClient.ResponseType.NO);
+        }
+    }
+}
diff --git 
a/server/protocols/protocols-managesieve/src/test/resources/managesieveserver-oidc.xml
 
b/server/protocols/protocols-managesieve/src/test/resources/managesieveserver-oidc.xml
new file mode 100644
index 0000000000..9125d16891
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/resources/managesieveserver-oidc.xml
@@ -0,0 +1,16 @@
+<managesieveserver enabled="true">
+    <jmxName>managesieveserver</jmxName>
+    <bind>0.0.0.0:4190</bind>
+
+    <connectionBacklog>200</connectionBacklog>
+    <connectiontimeout>360</connectiontimeout>
+    <connectionLimit>0</connectionLimit>
+    <connectionLimitPerIP>0</connectionLimitPerIP>
+
+    <oidc>
+        
<jwksURL>http://127.0.0.1/realms/test/protocol/openid-connect/certs</jwksURL>
+        <claim>sub</claim>
+        
<oidcConfigurationURL>https://127.0.0.1/realms/test/.well-known/openid-configuration</oidcConfigurationURL>
+        <scope>email</scope>
+    </oidc>
+</managesieveserver>
diff --git 
a/server/protocols/protocols-managesieve/src/test/resources/managesieveserver.xml
 
b/server/protocols/protocols-managesieve/src/test/resources/managesieveserver.xml
new file mode 100644
index 0000000000..77cc6f3a2a
--- /dev/null
+++ 
b/server/protocols/protocols-managesieve/src/test/resources/managesieveserver.xml
@@ -0,0 +1,9 @@
+<managesieveserver enabled="true">
+    <jmxName>managesieveserver</jmxName>
+    <bind>0.0.0.0:4190</bind>
+
+    <connectionBacklog>200</connectionBacklog>
+    <connectiontimeout>360</connectiontimeout>
+    <connectionLimit>0</connectionLimit>
+    <connectionLimitPerIP>0</connectionLimitPerIP>
+</managesieveserver>


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

Reply via email to