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

    JAMES-4195 Add JMAP OIDC authentication strategy
    
    Adds JMAP OIDC configuration parsing, userinfo/introspection resolution, 
bearer authentication strategy, challenge realm override support, and focused 
unit tests.
---
 server/protocols/jmap/pom.xml                      |   4 +
 .../jmap/http/OidcAuthenticationStrategy.java      | 115 +++++++++++++
 .../james/jmap/oidc/JMAPOidcConfiguration.java     | 181 +++++++++++++++++++++
 .../james/jmap/oidc/OidcEndpointsInfoResolver.java | 103 ++++++++++++
 .../jmap/http/OidcAuthenticationStrategyTest.java  |  67 ++++++++
 .../james/jmap/oidc/JMAPOidcConfigurationTest.java |  69 ++++++++
 6 files changed, 539 insertions(+)

diff --git a/server/protocols/jmap/pom.xml b/server/protocols/jmap/pom.xml
index 32f57a72f5..c650ecd765 100644
--- a/server/protocols/jmap/pom.xml
+++ b/server/protocols/jmap/pom.xml
@@ -68,6 +68,10 @@
             <artifactId>testing-base</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.github.ben-manes.caffeine</groupId>
             <artifactId>caffeine</artifactId>
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/OidcAuthenticationStrategy.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/OidcAuthenticationStrategy.java
new file mode 100644
index 0000000000..cfb88a8747
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/http/OidcAuthenticationStrategy.java
@@ -0,0 +1,115 @@
+/****************************************************************
+ * 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.http;
+
+import static 
org.apache.james.jmap.http.JWTAuthenticationStrategy.AUTHORIZATION_HEADER_PREFIX;
+
+import java.time.Clock;
+import java.util.List;
+import java.util.Optional;
+
+import jakarta.inject.Inject;
+
+import org.apache.james.core.Username;
+import org.apache.james.jmap.exceptions.UnauthorizedException;
+import org.apache.james.jmap.oidc.Aud;
+import org.apache.james.jmap.oidc.OidcTokenCache;
+import org.apache.james.jmap.oidc.Token;
+import org.apache.james.jmap.oidc.TokenInfo;
+import org.apache.james.jwt.introspection.TokenIntrospectionException;
+import org.apache.james.jwt.userinfo.UserInfoCheckException;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.SessionProvider;
+
+import com.google.common.collect.ImmutableMap;
+
+import reactor.core.publisher.Mono;
+import reactor.netty.http.server.HttpServerRequest;
+
+public class OidcAuthenticationStrategy implements AuthenticationStrategy {
+    public static final String DEFAULT_AUTHENTICATION_CHALLENGE_REALM = 
"james";
+
+    private final SessionProvider sessionProvider;
+    private final OidcTokenCache oidcTokenCache;
+    private final Clock clock;
+    private final List<Aud> auds;
+    private final String authenticationChallengeRealm;
+
+    @Inject
+    public OidcAuthenticationStrategy(SessionProvider sessionProvider, 
OidcTokenCache oidcTokenCache, Clock clock, List<Aud> auds) {
+        this(sessionProvider, oidcTokenCache, clock, auds, 
DEFAULT_AUTHENTICATION_CHALLENGE_REALM);
+    }
+
+    public OidcAuthenticationStrategy(SessionProvider sessionProvider, 
OidcTokenCache oidcTokenCache, Clock clock, List<Aud> auds,
+                                      String authenticationChallengeRealm) {
+        this.sessionProvider = sessionProvider;
+        this.oidcTokenCache = oidcTokenCache;
+        this.clock = clock;
+        this.auds = auds;
+        this.authenticationChallengeRealm = authenticationChallengeRealm;
+    }
+
+    @Override
+    public Mono<MailboxSession> createMailboxSession(HttpServerRequest 
httpRequest) {
+        return Mono.fromCallable(() -> authHeaders(httpRequest))
+            .filter(header -> header.startsWith(AUTHORIZATION_HEADER_PREFIX))
+            .map(header -> 
header.substring(AUTHORIZATION_HEADER_PREFIX.length()))
+            .map(Token::new)
+            .flatMap(oidcTokenCache::associatedInformation)
+            .<TokenInfo>handle((tokenInfo, sink) -> {
+                if (!auds.isEmpty() && !isAudienceAccepted(tokenInfo.aud())) {
+                    sink.error(new UnauthorizedException("Wrong audience. 
Expected " + auds + " got " + tokenInfo.aud()));
+                    return;
+                }
+                if (clock.instant().isAfter(tokenInfo.exp())) {
+                    sink.error(new UnauthorizedException("Expired token"));
+                    return;
+                }
+
+                sink.next(tokenInfo);
+            })
+            .map(tokenInfo -> Username.of(tokenInfo.email()))
+            .flatMap(username -> Mono.fromCallable(() -> 
sessionProvider.authenticate(username)
+                .withoutDelegation()))
+            .onErrorMap(TokenIntrospectionException.class, e -> new 
UnauthorizedException("Invalid OIDC token when introspection check", e))
+            .onErrorMap(UserInfoCheckException.class, e -> new 
UnauthorizedException("Invalid OIDC token when user info check", e));
+    }
+
+    private boolean isAudienceAccepted(Optional<List<Aud>> 
maybeTokenAudiences) {
+        return maybeTokenAudiences.map(tokenAudiences -> {
+                for (Aud aud: auds) {
+                    if (tokenAudiences.contains(aud)) {
+                        return true;
+                    }
+                }
+                return false;
+            })
+            .orElse(true); // if no audience is present in the introspection 
response, we ignore audience validation
+    }
+
+    @Override
+    public AuthenticationChallenge correspondingChallenge() {
+        return AuthenticationChallenge.of(
+            AuthenticationScheme.of("Bearer"),
+            ImmutableMap.of("realm", authenticationChallengeRealm,
+                "error", "invalid_token",
+                "scope", "openid profile email"));
+    }
+}
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/oidc/JMAPOidcConfiguration.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/oidc/JMAPOidcConfiguration.java
new file mode 100644
index 0000000000..5e9ff5d039
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/oidc/JMAPOidcConfiguration.java
@@ -0,0 +1,181 @@
+/****************************************************************
+ * 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.oidc;
+
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.james.jmap.http.OidcAuthenticationStrategy;
+import org.apache.james.jwt.introspection.IntrospectionEndpoint;
+import org.apache.james.utils.PropertiesProvider;
+
+import com.google.common.collect.ImmutableList;
+
+public class JMAPOidcConfiguration {
+    private static final String AUTHENTICATION_STRATEGIES_PROPERTY = 
"authentication.strategy.rfc8621";
+    private static final String USERINFO_URL_PROPERTY = "oidc.userInfo.url";
+    private static final String INTROSPECT_URL_PROPERTY = 
"oidc.introspect.url";
+    private static final String INTROSPECT_CREDENTIALS_PROPERTY = 
"oidc.introspect.credentials";
+    private static final String AUDIENCE_PROPERTY = "oidc.audience";
+    private static final String CLAIM_PROPERTY = "oidc.claim";
+
+    @FunctionalInterface
+    public interface RequireOidcEnabled {
+        Builder oidcEnabled(boolean oidcEnabled);
+    }
+
+    public static class Builder {
+        private final boolean oidcEnabled;
+        private Optional<URL> oidcUserInfoUrl = Optional.empty();
+        private Optional<IntrospectionEndpoint> oidcIntrospectionEndpoint = 
Optional.empty();
+        private Optional<String> oidcIntrospectionClaim = Optional.empty();
+        private Optional<List<Aud>> oidcAudience = Optional.empty();
+
+        private Builder(boolean oidcEnabled) {
+            this.oidcEnabled = oidcEnabled;
+        }
+
+        public Builder oidcUserInfoUrl(Optional<URL> url) {
+            this.oidcUserInfoUrl = url;
+            return this;
+        }
+
+        public Builder 
oidcIntrospectionEndpoint(Optional<IntrospectionEndpoint> endpoint) {
+            this.oidcIntrospectionEndpoint = endpoint;
+            return this;
+        }
+
+        public Builder oidcClaim(Optional<String> claim) {
+            this.oidcIntrospectionClaim = claim;
+            return this;
+        }
+
+        public Builder oidcAudience(Optional<List<Aud>> aud) {
+            this.oidcAudience = aud;
+            return this;
+        }
+
+        public JMAPOidcConfiguration build() {
+            if (oidcEnabled) {
+                checkOidcFieldsPresence();
+            }
+
+            return new JMAPOidcConfiguration(
+                oidcEnabled,
+                oidcUserInfoUrl,
+                oidcIntrospectionEndpoint,
+                oidcIntrospectionClaim,
+                oidcAudience);
+        }
+
+        private void checkOidcFieldsPresence() {
+            if (oidcUserInfoUrl.isEmpty() ||
+                oidcIntrospectionEndpoint.isEmpty() ||
+                oidcIntrospectionClaim.isEmpty()) {
+                throw new IllegalStateException("All OIDC fields must be 
defined when OIDC is enabled.");
+            }
+        }
+    }
+
+    public static RequireOidcEnabled builder() {
+        return oidcEnabled -> new Builder(oidcEnabled);
+    }
+
+    public static JMAPOidcConfiguration parseConfiguration(PropertiesProvider 
propertiesProvider) throws ConfigurationException, FileNotFoundException {
+        return parseConfiguration(propertiesProvider.getConfiguration("jmap"));
+    }
+
+    public static JMAPOidcConfiguration parseConfiguration(Configuration 
configuration) {
+        List<String> authenticationStrategies = 
configuration.getList(String.class, AUTHENTICATION_STRATEGIES_PROPERTY, 
ImmutableList.of());
+        boolean oidcEnabled = authenticationStrategies.stream()
+            .anyMatch(JMAPOidcConfiguration::isOidcAuthenticationStrategy);
+
+        Optional<URL> oidcUserInfoUrl = 
Optional.ofNullable(configuration.getString(USERINFO_URL_PROPERTY, null))
+            .map(JMAPOidcConfiguration::parseUrl);
+        Optional<URL> oidcIntrospectUrl = 
Optional.ofNullable(configuration.getString(INTROSPECT_URL_PROPERTY, null))
+            .map(JMAPOidcConfiguration::parseUrl);
+        Optional<String> oidcIntrospectCreds = 
Optional.ofNullable(configuration.getString(INTROSPECT_CREDENTIALS_PROPERTY, 
null));
+        Optional<List<Aud>> oidcAudience = 
Optional.of(configuration.getList(String.class, AUDIENCE_PROPERTY, 
ImmutableList.of()).stream().map(Aud::new).toList());
+        Optional<String> oidcIntrospectionClaim = 
Optional.ofNullable(configuration.getString(CLAIM_PROPERTY, null));
+
+        Optional<IntrospectionEndpoint> introspectionEndpoint = 
oidcIntrospectUrl.map(url -> new IntrospectionEndpoint(url, 
oidcIntrospectCreds));
+
+        return JMAPOidcConfiguration.builder()
+            .oidcEnabled(oidcEnabled)
+            .oidcUserInfoUrl(oidcUserInfoUrl)
+            .oidcIntrospectionEndpoint(introspectionEndpoint)
+            .oidcAudience(oidcAudience)
+            .oidcClaim(oidcIntrospectionClaim)
+            .build();
+    }
+
+    private static boolean isOidcAuthenticationStrategy(String 
authenticationStrategy) {
+        return 
authenticationStrategy.equals(OidcAuthenticationStrategy.class.getSimpleName()) 
||
+            authenticationStrategy.endsWith("." + 
OidcAuthenticationStrategy.class.getSimpleName());
+    }
+
+    private static URL parseUrl(String rawUrl) {
+        try {
+            return new URL(rawUrl);
+        } catch (MalformedURLException e) {
+            throw new IllegalArgumentException("Invalid OIDC URL: " + rawUrl, 
e);
+        }
+    }
+
+    private final boolean oidcEnabled;
+    private final Optional<URL> oidcUserInfoUrl;
+    private final Optional<IntrospectionEndpoint> introspectionEndpoint;
+    private final Optional<String> oidcClaim;
+    private final Optional<List<Aud>> aud;
+
+    JMAPOidcConfiguration(boolean oidcEnabled, Optional<URL> oidcUserInfoUrl, 
Optional<IntrospectionEndpoint> introspectionEndpoint,
+                          Optional<String> oidcClaim, Optional<List<Aud>> aud) 
{
+        this.oidcEnabled = oidcEnabled;
+        this.oidcUserInfoUrl = oidcUserInfoUrl;
+        this.introspectionEndpoint = introspectionEndpoint;
+        this.oidcClaim = oidcClaim;
+        this.aud = aud;
+    }
+
+    public boolean getOidcEnabled() {
+        return oidcEnabled;
+    }
+
+    public URL getOidcUserInfoUrl() {
+        return oidcUserInfoUrl.get();
+    }
+
+    public String getOidcClaim() {
+        return oidcClaim.get();
+    }
+
+    public IntrospectionEndpoint getIntrospectionEndpoint() {
+        return introspectionEndpoint.get();
+    }
+
+    public List<Aud> getAud() {
+        return aud.get();
+    }
+}
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/oidc/OidcEndpointsInfoResolver.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/oidc/OidcEndpointsInfoResolver.java
new file mode 100644
index 0000000000..0ac50e101a
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/oidc/OidcEndpointsInfoResolver.java
@@ -0,0 +1,103 @@
+/****************************************************************
+ * 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.oidc;
+
+import java.net.URL;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
+
+import org.apache.james.core.Username;
+import org.apache.james.jmap.exceptions.UnauthorizedException;
+import org.apache.james.jwt.DefaultCheckTokenClient;
+import org.apache.james.jwt.introspection.IntrospectionEndpoint;
+import org.apache.james.jwt.introspection.TokenIntrospectionResponse;
+import org.apache.james.jwt.userinfo.UserinfoResponse;
+import org.apache.james.metrics.api.MetricFactory;
+import org.apache.james.util.streams.Iterators;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.publisher.Mono;
+
+public class OidcEndpointsInfoResolver implements TokenInfoResolver {
+    private static final String SID_PROPERTY = "sid";
+
+    private final DefaultCheckTokenClient checkTokenClient;
+    private final MetricFactory metricFactory;
+    private final URL userInfoURL;
+    private final IntrospectionEndpoint introspectionEndpoint;
+    private final JMAPOidcConfiguration configuration;
+
+    @Inject
+    public OidcEndpointsInfoResolver(DefaultCheckTokenClient checkTokenClient,
+                                     MetricFactory metricFactory,
+                                     @Named("userInfo") URL userInfoURL,
+                                     IntrospectionEndpoint 
introspectionEndpoint,
+                                     JMAPOidcConfiguration configuration) {
+        this.checkTokenClient = checkTokenClient;
+        this.metricFactory = metricFactory;
+        this.userInfoURL = userInfoURL;
+        this.introspectionEndpoint = introspectionEndpoint;
+        this.configuration = configuration;
+    }
+
+    @Override
+    public Mono<TokenInfo> apply(Token token) {
+        return Mono.zip(
+                
Mono.from(metricFactory.decoratePublisherWithTimerMetric("userinfo-lookup", 
checkTokenClient.userInfo(userInfoURL, token.value()))),
+                
Mono.from(metricFactory.decoratePublisherWithTimerMetric("introspection-lookup",
 checkTokenClient.introspect(introspectionEndpoint, token.value()))))
+            .flatMap(tokenInfos -> {
+                UserinfoResponse userInfo = tokenInfos.getT1();
+                TokenIntrospectionResponse introspectInfo = tokenInfos.getT2();
+
+                Username sub = 
Username.of(userInfo.claimByPropertyName(configuration.getOidcClaim())
+                    .orElseThrow(() -> new UnauthorizedException("Invalid OIDC 
token: userinfo needs to include " + configuration.getOidcClaim() + " claim")));
+
+                return Mono.just(toTokenInfo(sub, userInfo, introspectInfo));
+            });
+    }
+
+    private TokenInfo toTokenInfo(Username username, UserinfoResponse 
userinfoResponse, TokenIntrospectionResponse introspectionResponse) {
+        return new TokenInfo(
+            username.asString(),
+            userinfoResponse.claimByPropertyName(SID_PROPERTY).map(Sid::new)
+                .or(() -> 
introspectionResponse.claimByPropertyName(SID_PROPERTY).map(Sid::new)),
+            Instant.ofEpochSecond(introspectionResponse.exp().orElseThrow(() 
-> new UnauthorizedException("Expiration claim ('exp') is required in the 
token"))),
+            extractAudience(introspectionResponse));
+    }
+
+    private static Optional<List<Aud>> 
extractAudience(TokenIntrospectionResponse introspectionResponse) {
+        return Optional.ofNullable(introspectionResponse.json().get("aud"))
+            .map(audJson -> {
+                if (audJson.isArray()) {
+                    return Iterators.toStream(audJson.iterator())
+                        .map(JsonNode::asText)
+                        .map(Aud::new)
+                        .toList();
+                }
+                return ImmutableList.of(new Aud(audJson.asText()));
+            });
+    }
+}
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/http/OidcAuthenticationStrategyTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/http/OidcAuthenticationStrategyTest.java
new file mode 100644
index 0000000000..847842bb1b
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/http/OidcAuthenticationStrategyTest.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * 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.http;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import java.time.Clock;
+import java.util.List;
+
+import org.apache.james.jmap.oidc.OidcTokenCache;
+import org.apache.james.mailbox.SessionProvider;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+class OidcAuthenticationStrategyTest {
+    @Test
+    void correspondingChallengeShouldUseJamesRealmByDefault() {
+        OidcAuthenticationStrategy testee = new OidcAuthenticationStrategy(
+            mock(SessionProvider.class),
+            mock(OidcTokenCache.class),
+            Clock.systemUTC(),
+            List.of());
+
+        assertThat(testee.correspondingChallenge())
+            .isEqualTo(AuthenticationChallenge.of(
+                AuthenticationScheme.of("Bearer"),
+                ImmutableMap.of("realm", "james",
+                    "error", "invalid_token",
+                    "scope", "openid profile email")));
+    }
+
+    @Test
+    void correspondingChallengeShouldAllowRealmOverride() {
+        OidcAuthenticationStrategy testee = new OidcAuthenticationStrategy(
+            mock(SessionProvider.class),
+            mock(OidcTokenCache.class),
+            Clock.systemUTC(),
+            List.of(),
+            "twake_mail");
+
+        assertThat(testee.correspondingChallenge())
+            .isEqualTo(AuthenticationChallenge.of(
+                AuthenticationScheme.of("Bearer"),
+                ImmutableMap.of("realm", "twake_mail",
+                    "error", "invalid_token",
+                    "scope", "openid profile email")));
+    }
+}
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/oidc/JMAPOidcConfigurationTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/oidc/JMAPOidcConfigurationTest.java
new file mode 100644
index 0000000000..d29f38f594
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/oidc/JMAPOidcConfigurationTest.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.oidc;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
+import org.apache.james.jmap.http.OidcAuthenticationStrategy;
+import org.junit.jupiter.api.Test;
+
+class JMAPOidcConfigurationTest {
+    @Test
+    void parseConfigurationShouldEnableOidcWhenSimpleNameIsConfigured() {
+        BaseHierarchicalConfiguration configuration = 
oidcConfiguration(OidcAuthenticationStrategy.class.getSimpleName());
+
+        
assertThat(JMAPOidcConfiguration.parseConfiguration(configuration).getOidcEnabled())
+            .isTrue();
+    }
+
+    @Test
+    void 
parseConfigurationShouldEnableOidcWhenJamesCanonicalNameIsConfigured() {
+        BaseHierarchicalConfiguration configuration = 
oidcConfiguration(OidcAuthenticationStrategy.class.getCanonicalName());
+
+        
assertThat(JMAPOidcConfiguration.parseConfiguration(configuration).getOidcEnabled())
+            .isTrue();
+    }
+
+    @Test
+    void 
parseConfigurationShouldEnableOidcWhenTmailCanonicalNameIsConfigured() {
+        BaseHierarchicalConfiguration configuration = 
oidcConfiguration("com.linagora.tmail.james.jmap.oidc.OidcAuthenticationStrategy");
+
+        
assertThat(JMAPOidcConfiguration.parseConfiguration(configuration).getOidcEnabled())
+            .isTrue();
+    }
+
+    @Test
+    void 
parseConfigurationShouldNotEnableOidcWhenStrategyNameOnlyContainsOidcAuthenticationStrategy()
 {
+        BaseHierarchicalConfiguration configuration = 
oidcConfiguration("com.example.DisabledOidcAuthenticationStrategy");
+
+        
assertThat(JMAPOidcConfiguration.parseConfiguration(configuration).getOidcEnabled())
+            .isFalse();
+    }
+
+    private BaseHierarchicalConfiguration oidcConfiguration(String 
authenticationStrategy) {
+        BaseHierarchicalConfiguration configuration = new 
BaseHierarchicalConfiguration();
+        configuration.addProperty("authentication.strategy.rfc8621", 
authenticationStrategy);
+        configuration.addProperty("oidc.userInfo.url", 
"http://127.0.0.1/userinfo";);
+        configuration.addProperty("oidc.introspect.url", 
"http://127.0.0.1/introspect";);
+        configuration.addProperty("oidc.claim", "email");
+        return configuration;
+    }
+}


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

Reply via email to