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 ff036b435c762311d879aa0e1c831e18d22eaffa
Author: Quan Tran <[email protected]>
AuthorDate: Thu Jul 2 16:28:53 2026 +0700

    JAMES-4195 Add JMAP OIDC integration tests
    
    Adds memory, postgres, and distributed JMAP OIDC authentication integration 
coverage, including Redis cache wiring for postgres/distributed.
---
 .../pom.xml                                        |  16 +
 .../DistributedOidcAuthenticationTest.java         |  70 ++++
 .../jmap-rfc-8621-integration-tests-common/pom.xml |   4 +
 .../contract/OidcAuthenticationContract.java       | 429 +++++++++++++++++++++
 .../memory/MemoryOidcAuthenticationTest.java       |  52 +++
 .../pom.xml                                        |  16 +
 .../postgres/PostgresOidcAuthenticationTest.java   |  69 ++++
 7 files changed, 656 insertions(+)

diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/pom.xml
 
b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/pom.xml
index ab6d6aadfb..b3599e1bd7 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/pom.xml
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/pom.xml
@@ -67,6 +67,12 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-backends-redis</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>blob-s3</artifactId>
@@ -122,6 +128,11 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-jmap-redis</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-opensearch</artifactId>
@@ -150,6 +161,11 @@
             <artifactId>pulsar</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedOidcAuthenticationTest.java
 
b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedOidcAuthenticationTest.java
new file mode 100644
index 0000000000..459c7c604e
--- /dev/null
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedOidcAuthenticationTest.java
@@ -0,0 +1,70 @@
+/****************************************************************
+ * 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.jmap.rfc8621.distributed;
+
+import static 
org.apache.james.jmap.oidc.redis.OidcTokenCacheModuleChooser.Implementation.REDIS;
+
+import org.apache.james.CassandraExtension;
+import org.apache.james.CassandraRabbitMQJamesConfiguration;
+import org.apache.james.CassandraRabbitMQJamesServerMain;
+import org.apache.james.DockerOpenSearchExtension;
+import org.apache.james.JamesServerBuilder;
+import org.apache.james.JamesServerExtension;
+import org.apache.james.SearchConfiguration;
+import org.apache.james.backends.redis.RedisExtension;
+import org.apache.james.jmap.core.JmapRfc8621Configuration;
+import org.apache.james.jmap.oidc.JMAPOidcConfiguration;
+import org.apache.james.jmap.rfc8621.contract.OidcAuthenticationContract;
+import org.apache.james.modules.AwsS3BlobStoreExtension;
+import org.apache.james.modules.RabbitMQExtension;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.apache.james.modules.blobstore.BlobStoreConfiguration;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class DistributedOidcAuthenticationTest extends OidcAuthenticationContract {
+    @RegisterExtension
+    static JamesServerExtension testExtension = new 
JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
+        CassandraRabbitMQJamesConfiguration.builder()
+            .workingDirectory(tmpDir)
+            .configurationFromClasspath()
+            .blobStore(BlobStoreConfiguration.builder()
+                .s3()
+                .disableCache()
+                .deduplication()
+                .noCryptoConfig())
+            .searchConfiguration(SearchConfiguration.openSearch())
+            .enableJMAPOidc()
+            .oidcTokenCacheImplementation(REDIS)
+            .build())
+        .extension(new DockerOpenSearchExtension())
+        .extension(new CassandraExtension())
+        .extension(new RabbitMQExtension())
+        .extension(new AwsS3BlobStoreExtension())
+        .extension(new RedisExtension())
+        .server(configuration -> 
CassandraRabbitMQJamesServerMain.createServer(configuration)
+            .overrideWith(new TestJMAPServerModule())
+            .overrideWith(binder -> binder.bind(JmapRfc8621Configuration.class)
+                .toInstance(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION()
+                    
.withAuthenticationStrategies(OIDC_AUTHENTICATION_STRATEGY)))
+            .overrideWith(binder -> binder.bind(JMAPOidcConfiguration.class)
+                .toProvider(OidcAuthenticationContract::oidcConfiguration)))
+        .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
+        .build();
+}
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
index 4bce0e801d..c0c29faac6 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
@@ -58,6 +58,10 @@
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-smtp</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-jmap</artifactId>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-testing</artifactId>
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/OidcAuthenticationContract.java
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/OidcAuthenticationContract.java
new file mode 100644
index 0000000000..61fe32883f
--- /dev/null
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/OidcAuthenticationContract.java
@@ -0,0 +1,429 @@
+/****************************************************************
+ * 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.jmap.rfc8621.contract;
+
+import static io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.requestSpecification;
+import static io.restassured.RestAssured.with;
+import static org.apache.http.HttpStatus.SC_OK;
+import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
+import static 
org.apache.james.jmap.rfc8621.contract.Fixture.AUTHORIZATION_HEADER;
+import static org.apache.james.jmap.rfc8621.contract.Fixture.BOB;
+import static org.apache.james.jmap.rfc8621.contract.Fixture.BOB_PASSWORD;
+import static org.apache.james.jmap.rfc8621.contract.Fixture.DOMAIN;
+import static 
org.apache.james.jmap.rfc8621.contract.Fixture.ECHO_REQUEST_OBJECT;
+import static 
org.apache.james.jmap.rfc8621.contract.Fixture.baseRequestSpecBuilder;
+import static org.apache.james.jmap.rfc8621.contract.Fixture.getHeadersWith;
+
+import java.net.URI;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.apache.james.GuiceJamesServer;
+import org.apache.james.jmap.http.OidcAuthenticationStrategy;
+import org.apache.james.jmap.oidc.Aud;
+import org.apache.james.jmap.oidc.JMAPOidcConfiguration;
+import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags;
+import org.apache.james.jwt.introspection.IntrospectionEndpoint;
+import org.apache.james.user.api.AlreadyExistInUsersRepositoryException;
+import org.apache.james.utils.DataProbeImpl;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.mockserver.integration.ClientAndServer;
+import org.mockserver.model.HttpRequest;
+import org.mockserver.model.HttpResponse;
+import org.mockserver.verify.VerificationTimes;
+
+import io.restassured.authentication.NoAuthScheme;
+import io.restassured.http.Header;
+
+public abstract class OidcAuthenticationContract {
+    private static final String USERINFO_TOKEN_URI_PATH = "/oauth2/userinfo";
+    private static final String INTROSPECT_TOKEN_URI_PATH = 
"/oauth2/introspect";
+    private static final String EMAIL_CLAIM = "email";
+    private static final String SID = 
"dT/8+UDx1lWp1bRZkdhbS1i6ZfYhf8+bWAZQs8p0T/c";
+    private static final long TOKEN_EXPIRATION_TIME = 
Instant.now().plus(Duration.ofHours(1)).getEpochSecond();
+
+    private static final ClientAndServer MOCK_SERVER = 
ClientAndServer.startClientAndServer(0);
+
+    protected Header authHeader;
+
+    protected static final Optional<List<String>> OIDC_AUTHENTICATION_STRATEGY 
=
+        Optional.of(List.of(OidcAuthenticationStrategy.class.getSimpleName()));
+
+    protected static JMAPOidcConfiguration oidcConfiguration() {
+        return oidcConfiguration(List.of(new Aud("james"), new 
Aud("james-admin")));
+    }
+
+    protected static JMAPOidcConfiguration oidcConfiguration(List<Aud> 
audiences) {
+        return JMAPOidcConfiguration.builder()
+            .oidcEnabled(true)
+            .oidcUserInfoUrl(Optional.of(getUserInfoTokenEndpoint()))
+            .oidcIntrospectionEndpoint(Optional.of(new 
IntrospectionEndpoint(getIntrospectTokenEndpoint(), Optional.empty())))
+            .oidcClaim(Optional.of(EMAIL_CLAIM))
+            .oidcAudience(Optional.of(audiences))
+            .build();
+    }
+
+    @BeforeEach
+    void setUp(GuiceJamesServer server) throws Exception {
+        authHeader = bearerHeader(UUID.randomUUID().toString());
+
+        server.getProbe(DataProbeImpl.class)
+            .fluent()
+            .addDomain(DOMAIN().asString());
+
+        try {
+            server.getProbe(DataProbeImpl.class)
+                .fluent()
+                .addUser(BOB().asString(), BOB_PASSWORD());
+        } catch (AlreadyExistInUsersRepositoryException e) {
+            // James integration tests reuse the server between methods.
+        }
+
+        requestSpecification = baseRequestSpecBuilder(server)
+            .setAuth(new NoAuthScheme())
+            .build();
+    }
+
+    @AfterEach
+    void tearDown() {
+        MOCK_SERVER.reset();
+    }
+
+    private static URL getUserInfoTokenEndpoint() {
+        return toUrl(USERINFO_TOKEN_URI_PATH);
+    }
+
+    private static URL getIntrospectTokenEndpoint() {
+        return toUrl(INTROSPECT_TOKEN_URI_PATH);
+    }
+
+    private static URL toUrl(String path) {
+        try {
+            return URI.create(String.format("http://127.0.0.1:%d%s";, 
MOCK_SERVER.getLocalPort(), path)).toURL();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Header bearerHeader(String token) {
+        return new Header(AUTHORIZATION_HEADER(), "Bearer " + token);
+    }
+
+    protected String primaryAudience() {
+        return "james";
+    }
+
+    protected String secondaryAudience() {
+        return "james-admin";
+    }
+
+    private void mockJsonResponse(String path, String response, int 
statusCode) {
+        MOCK_SERVER
+            .when(HttpRequest.request().withPath(path))
+            .respond(HttpResponse.response()
+                .withStatusCode(statusCode)
+                .withHeader("Content-Type", "application/json")
+                .withBody(response, StandardCharsets.UTF_8));
+    }
+
+    private void mockUserInfo(String email) {
+        mockJsonResponse(USERINFO_TOKEN_URI_PATH, """
+            {
+              "sub": "james-user",
+              "email": "%s",
+              "sid": "%s",
+              "name": "James User"
+            }""".formatted(email, SID), SC_OK);
+    }
+
+    private void mockUserInfoWithoutSid(String email) {
+        mockJsonResponse(USERINFO_TOKEN_URI_PATH, """
+            {
+              "sub": "james-user",
+              "email": "%s",
+              "name": "James User"
+            }""".formatted(email), SC_OK);
+    }
+
+    private void mockIntrospection(String audience) {
+        mockJsonResponse(INTROSPECT_TOKEN_URI_PATH, """
+            {
+              "exp": %d,
+              "scope": "openid email profile",
+              "client_id": "james",
+              "active": true,
+              "aud": "%s",
+              "sub": "james-user",
+              "sid": "%s",
+              "iss": "https://sso.example.com";
+            }""".formatted(TOKEN_EXPIRATION_TIME, audience, SID), SC_OK);
+    }
+
+    protected void mockValidToken(String email) {
+        mockUserInfo(email);
+        mockIntrospection(primaryAudience());
+    }
+
+    @Tag(CategoryTags.BASIC_FEATURE)
+    @Test
+    void shouldAuthenticateWithOidc() {
+        mockValidToken(BOB().asString());
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+    }
+
+    @Test
+    void shouldAcceptAudArray() {
+        mockUserInfo(BOB().asString());
+        mockJsonResponse(INTROSPECT_TOKEN_URI_PATH, """
+            {
+              "exp": %d,
+              "scope": "openid email profile",
+              "client_id": "james",
+              "active": true,
+              "aud": ["%s"],
+              "sub": "james-user",
+              "sid": "%s",
+              "iss": "https://sso.example.com";
+            }""".formatted(TOKEN_EXPIRATION_TIME, primaryAudience(), SID), 
SC_OK);
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+    }
+
+    @Tag(CategoryTags.BASIC_FEATURE)
+    @Test
+    void shouldRejectOutdatedToken() {
+        mockUserInfo(BOB().asString());
+        mockJsonResponse(INTROSPECT_TOKEN_URI_PATH, """
+            {
+              "exp": %d,
+              "scope": "openid email profile",
+              "client_id": "james",
+              "active": true,
+              "aud": "%s",
+              "sub": "james-user",
+              "sid": "%s",
+              "iss": "https://sso.example.com";
+            
}""".formatted(Instant.now().minus(Duration.ofHours(1)).getEpochSecond(), 
primaryAudience(), SID), SC_OK);
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_UNAUTHORIZED);
+    }
+
+    @Tag(CategoryTags.BASIC_FEATURE)
+    @Test
+    void shouldRejectBadAudience() {
+        mockUserInfo(BOB().asString());
+        mockIntrospection("bad");
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_UNAUTHORIZED);
+    }
+
+    @Test
+    void shouldAcceptOtherConfiguredAudience() {
+        mockUserInfo(BOB().asString());
+        mockIntrospection(secondaryAudience());
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+    }
+
+    @Test
+    void shouldAcceptNoSidInUserInfo() {
+        mockUserInfoWithoutSid(BOB().asString());
+        mockIntrospection(primaryAudience());
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+    }
+
+    @Test
+    void shouldAcceptNoSidInIntrospection() {
+        mockUserInfo(BOB().asString());
+        mockJsonResponse(INTROSPECT_TOKEN_URI_PATH, """
+            {
+              "exp": %d,
+              "scope": "openid email profile",
+              "client_id": "james",
+              "active": true,
+              "aud": "%s",
+              "sub": "james-user",
+              "iss": "https://sso.example.com";
+            }""".formatted(TOKEN_EXPIRATION_TIME, primaryAudience()), SC_OK);
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+    }
+
+    @Test
+    void shouldAcceptMissingAudInIntrospectionResponseAndCacheIt() {
+        mockUserInfo(BOB().asString());
+        mockJsonResponse(INTROSPECT_TOKEN_URI_PATH, """
+            {
+              "exp": %d,
+              "scope": "openid email profile",
+              "client_id": "james",
+              "active": true,
+              "sub": "james-user",
+              "sid": "%s",
+              "iss": "https://sso.example.com";
+            }""".formatted(TOKEN_EXPIRATION_TIME, SID), SC_OK);
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+    }
+
+    @Test
+    void shouldRejectWhenUserInfoFails() {
+        mockIntrospection(primaryAudience());
+        mockJsonResponse(USERINFO_TOKEN_URI_PATH, "unauthorized", 
SC_UNAUTHORIZED);
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_UNAUTHORIZED);
+    }
+
+    @Test
+    void shouldRejectWhenIntrospectionFails() {
+        mockUserInfo(BOB().asString());
+        mockJsonResponse(INTROSPECT_TOKEN_URI_PATH, "unauthorized", 
SC_UNAUTHORIZED);
+
+        given()
+            .headers(getHeadersWith(authHeader))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_UNAUTHORIZED);
+    }
+
+    @Test
+    void shouldCacheResponse() {
+        mockValidToken(BOB().asString());
+
+        for (int i = 0; i < 3; i++) {
+            with()
+                .headers(getHeadersWith(authHeader))
+                .body(ECHO_REQUEST_OBJECT())
+            .when()
+                .post()
+            .then()
+                .statusCode(SC_OK);
+        }
+
+        
MOCK_SERVER.verify(HttpRequest.request().withPath(INTROSPECT_TOKEN_URI_PATH), 
VerificationTimes.exactly(1));
+        
MOCK_SERVER.verify(HttpRequest.request().withPath(USERINFO_TOKEN_URI_PATH), 
VerificationTimes.exactly(1));
+    }
+
+    @Test
+    void shouldNotShareCacheAcrossDifferentOidcTokens() {
+        mockIntrospection(primaryAudience());
+        mockUserInfo(BOB().asString());
+
+        Header token1 = bearerHeader("token1");
+        given()
+            .headers(getHeadersWith(token1))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+
+        Header token2 = bearerHeader("token2");
+        given()
+            .headers(getHeadersWith(token2))
+            .body(ECHO_REQUEST_OBJECT())
+        .when()
+            .post()
+        .then()
+            .statusCode(SC_OK);
+
+        MOCK_SERVER.verify(HttpRequest.request()
+            .withPath(USERINFO_TOKEN_URI_PATH)
+            .withHeader(AUTHORIZATION_HEADER(), token1.getValue()), 
VerificationTimes.exactly(1));
+        MOCK_SERVER.verify(HttpRequest.request()
+            .withPath(USERINFO_TOKEN_URI_PATH)
+            .withHeader(AUTHORIZATION_HEADER(), token2.getValue()), 
VerificationTimes.exactly(1));
+    }
+}
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryOidcAuthenticationTest.java
 
b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryOidcAuthenticationTest.java
new file mode 100644
index 0000000000..951b66df6b
--- /dev/null
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryOidcAuthenticationTest.java
@@ -0,0 +1,52 @@
+/****************************************************************
+ * 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.jmap.rfc8621.memory;
+
+import static 
org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
+
+import org.apache.james.JamesServerBuilder;
+import org.apache.james.JamesServerExtension;
+import org.apache.james.MemoryJamesConfiguration;
+import org.apache.james.MemoryJamesServerMain;
+import org.apache.james.jmap.core.JmapRfc8621Configuration;
+import org.apache.james.jmap.oidc.JMAPOidcConfiguration;
+import org.apache.james.jmap.rfc8621.contract.OidcAuthenticationContract;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class MemoryOidcAuthenticationTest extends OidcAuthenticationContract {
+    @RegisterExtension
+    static JamesServerExtension testExtension = new 
JamesServerBuilder<MemoryJamesConfiguration>(tmpDir ->
+        MemoryJamesConfiguration.builder()
+            .workingDirectory(tmpDir)
+            .configurationFromClasspath()
+            .usersRepository(DEFAULT)
+            .enableJMAPOidc()
+            .build())
+        .server(configuration -> 
MemoryJamesServerMain.createServer(configuration)
+            .overrideWith(new TestJMAPServerModule())
+            .overrideWith(binder -> binder.bind(JmapRfc8621Configuration.class)
+                .toInstance(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION()
+                    
.withAuthenticationStrategies(OIDC_AUTHENTICATION_STRATEGY)))
+            .overrideWith(binder -> binder.bind(JMAPOidcConfiguration.class)
+                .toProvider(OidcAuthenticationContract::oidcConfiguration)))
+        .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
+        .build();
+}
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/pom.xml
 
b/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/pom.xml
index a5867b7c21..321aaa8661 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/pom.xml
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/pom.xml
@@ -48,6 +48,12 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-backends-redis</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-common</artifactId>
@@ -60,6 +66,11 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-jmap-redis</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-opensearch</artifactId>
@@ -93,6 +104,11 @@
             <artifactId>postgresql</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresOidcAuthenticationTest.java
 
b/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresOidcAuthenticationTest.java
new file mode 100644
index 0000000000..e5fb9f42cd
--- /dev/null
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresOidcAuthenticationTest.java
@@ -0,0 +1,69 @@
+/****************************************************************
+ * 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.jmap.rfc8621.postgres;
+
+import static 
org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
+import static 
org.apache.james.jmap.oidc.redis.OidcTokenCacheModuleChooser.Implementation.REDIS;
+
+import org.apache.james.JamesServerBuilder;
+import org.apache.james.JamesServerExtension;
+import org.apache.james.PostgresJamesConfiguration;
+import org.apache.james.PostgresJamesServerMain;
+import org.apache.james.SearchConfiguration;
+import org.apache.james.backends.postgres.PostgresExtension;
+import org.apache.james.backends.redis.RedisExtension;
+import org.apache.james.jmap.core.JmapRfc8621Configuration;
+import org.apache.james.jmap.oidc.JMAPOidcConfiguration;
+import org.apache.james.jmap.rfc8621.contract.OidcAuthenticationContract;
+import org.apache.james.modules.RabbitMQExtension;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.apache.james.modules.blobstore.BlobStoreConfiguration;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class PostgresOidcAuthenticationTest extends OidcAuthenticationContract 
{
+    @RegisterExtension
+    static JamesServerExtension testExtension = new 
JamesServerBuilder<PostgresJamesConfiguration>(tmpDir ->
+        PostgresJamesConfiguration.builder()
+            .workingDirectory(tmpDir)
+            .configurationFromClasspath()
+            .searchConfiguration(SearchConfiguration.scanning())
+            .usersRepository(DEFAULT)
+            .eventBusImpl(PostgresJamesConfiguration.EventBusImpl.RABBITMQ)
+            .blobStore(BlobStoreConfiguration.builder()
+                .postgres()
+                .disableCache()
+                .deduplication()
+                .noCryptoConfig())
+            .enableJMAPOidc()
+            .oidcTokenCacheImplementation(REDIS)
+            .build())
+        .extension(PostgresExtension.empty())
+        .extension(new RabbitMQExtension())
+        .extension(new RedisExtension())
+        .server(configuration -> 
PostgresJamesServerMain.createServer(configuration)
+            .overrideWith(new TestJMAPServerModule())
+            .overrideWith(binder -> binder.bind(JmapRfc8621Configuration.class)
+                .toInstance(JmapRfc8621Configuration.LOCALHOST_CONFIGURATION()
+                    
.withAuthenticationStrategies(OIDC_AUTHENTICATION_STRATEGY)))
+            .overrideWith(binder -> binder.bind(JMAPOidcConfiguration.class)
+                .toProvider(OidcAuthenticationContract::oidcConfiguration)))
+        .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
+        .build();
+}


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

Reply via email to