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

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

commit b9b56d726c02c36c4e266a2b469c32846d2a2d03
Author: Benoit TELLIER <[email protected]>
AuthorDate: Mon Dec 8 14:11:29 2025 +0100

    [ENHANCEMENT] OIDC SASL code refactoring
    
     - Mutualize code for choosing how code shall be validated between IMAP and 
SMTP
     - Turn OidcJwtTokenVerifier as a class
     - Have SASL configuration as a member in order to limit count of arguments
---
 protocols/api/pom.xml                              |  4 +
 .../apache/james/imap/api/process/ImapSession.java |  2 +-
 .../apache/james/imap/encode/FakeImapSession.java  |  2 +-
 .../imap/processor/AuthenticateProcessor.java      | 37 +--------
 .../protocols/lmtp/LMTPConfigurationImpl.java      |  2 +-
 .../james/protocols/smtp/SMTPConfiguration.java    |  2 +-
 .../protocols/smtp/SMTPConfigurationImpl.java      |  2 +-
 .../protocols/smtp/core/esmtp/AuthCmdHandler.java  |  2 +-
 .../apache/james/protocols/smtp/hook/AuthHook.java |  2 +-
 server/protocols/jwt/pom.xml                       |  4 +
 .../org/apache/james/jwt/OidcJwtTokenVerifier.java | 55 +++++++++++--
 .../apache/james/jwt}/OidcSASLConfiguration.java   | 96 +++++++++++++++++++++-
 .../apache/james/jwt/OidcJwtTokenVerifierTest.java | 79 +++++++++++-------
 server/protocols/protocols-imap4/pom.xml           |  2 +-
 .../apache/james/imapserver/netty/IMAPServer.java  |  2 +-
 .../james/imapserver/netty/NettyImapSession.java   |  2 +-
 .../apache/james/lmtpserver/netty/LMTPServer.java  |  2 +-
 server/protocols/protocols-smtp/pom.xml            |  2 +-
 .../james/smtpserver/ConfigurationAuthHook.java    |  2 +-
 .../james/smtpserver/UsersRepositoryAuthHook.java  | 38 +--------
 .../apache/james/smtpserver/netty/SMTPServer.java  |  2 +-
 21 files changed, 216 insertions(+), 125 deletions(-)

diff --git a/protocols/api/pom.xml b/protocols/api/pom.xml
index e64f7264c3..3922f4bac7 100644
--- a/protocols/api/pom.xml
+++ b/protocols/api/pom.xml
@@ -37,6 +37,10 @@
             <groupId>${james.groupId}</groupId>
             <artifactId>james-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-jwt</artifactId>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>testing-base</artifactId>
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java
index e55c595301..ebdce2fdba 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java
@@ -31,9 +31,9 @@ import javax.net.ssl.SSLSession;
 import org.apache.commons.text.RandomStringGenerator;
 import org.apache.james.core.Username;
 import org.apache.james.imap.api.ImapSessionState;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.protocols.api.CommandDetectionSession;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
 import org.apache.james.util.MDCBuilder;
 
 import reactor.core.publisher.Mono;
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java
index ed312cf463..d73af074e1 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java
@@ -34,7 +34,7 @@ import org.apache.james.imap.api.ImapSessionState;
 import org.apache.james.imap.api.process.ImapLineHandler;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.api.process.SelectedMailbox;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.util.concurrent.NamedThreadFactory;
 
 import reactor.core.publisher.Mono;
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
index 89eaa5877c..7db61a86f0 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.List;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
 import jakarta.inject.Inject;
@@ -41,11 +40,10 @@ import 
org.apache.james.imap.message.request.AuthenticateRequest;
 import org.apache.james.imap.message.request.IRAuthenticateRequest;
 import org.apache.james.imap.message.response.AuthenticateResponse;
 import org.apache.james.jwt.OidcJwtTokenVerifier;
-import org.apache.james.jwt.introspection.IntrospectionEndpoint;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.protocols.api.OIDCSASLParser;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
 import org.apache.james.util.MDCBuilder;
 import org.apache.james.util.ReactorUtils;
 import org.slf4j.Logger;
@@ -212,7 +210,7 @@ public class AuthenticateProcessor extends 
AbstractAuthProcessor<AuthenticateReq
 
     private void doOAuth(OIDCSASLParser.OIDCInitialResponse 
oidcInitialResponse, OidcSASLConfiguration oidcSASLConfiguration,
                          ImapSession session, ImapRequest request, Responder 
responder) {
-        validateToken(oidcSASLConfiguration, oidcInitialResponse.getToken())
+        new 
OidcJwtTokenVerifier(oidcSASLConfiguration).validateToken(oidcInitialResponse.getToken())
             .ifPresentOrElse(authenticatedUser -> {
                 Username associatedUser = 
Username.of(oidcInitialResponse.getAssociatedUser());
                 if (!associatedUser.equals(authenticatedUser)) {
@@ -226,37 +224,6 @@ public class AuthenticateProcessor extends 
AbstractAuthProcessor<AuthenticateReq
             }, () -> manageFailureCount(session, request, responder));
     }
 
-    private Optional<Username> validateToken(OidcSASLConfiguration 
oidcSASLConfiguration, String token) {
-        if (oidcSASLConfiguration.isCheckTokenByIntrospectionEndpoint()) {
-            return validTokenWithIntrospection(oidcSASLConfiguration, token);
-        } else if (oidcSASLConfiguration.isCheckTokenByUserinfoEndpoint()) {
-            return validTokenWithUserInfo(oidcSASLConfiguration, token);
-        } else {
-            return OidcJwtTokenVerifier.verifySignatureAndExtractClaim(token, 
oidcSASLConfiguration.getJwksURL(), oidcSASLConfiguration.getClaim())
-                .map(Username::of);
-        }
-    }
-
-    private static Optional<Username> 
validTokenWithUserInfo(OidcSASLConfiguration oidcSASLConfiguration, String 
token) {
-        return Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(token,
-                oidcSASLConfiguration.getJwksURL(),
-                oidcSASLConfiguration.getClaim(),
-                oidcSASLConfiguration.getUserInfoEndpoint().orElseThrow()))
-            .blockOptional()
-            .map(Username::of);
-    }
-
-    private static Optional<Username> 
validTokenWithIntrospection(OidcSASLConfiguration oidcSASLConfiguration, String 
token) {
-        return Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(token,
-                oidcSASLConfiguration.getJwksURL(),
-                oidcSASLConfiguration.getClaim(),
-                oidcSASLConfiguration.getIntrospectionEndpoint()
-                    .map(endpoint -> new IntrospectionEndpoint(endpoint, 
oidcSASLConfiguration.getIntrospectionEndpointAuthorization()))
-                    .orElseThrow()))
-            .blockOptional()
-            .map(Username::of);
-    }
-
     private static String extractInitialClientResponse(byte[] data) {
         // cut of the CRLF
         return new String(data, 0, data.length - 2, StandardCharsets.US_ASCII);
diff --git 
a/protocols/lmtp/src/test/java/org/apache/james/protocols/lmtp/LMTPConfigurationImpl.java
 
b/protocols/lmtp/src/test/java/org/apache/james/protocols/lmtp/LMTPConfigurationImpl.java
index 0839079148..4f874e011a 100644
--- 
a/protocols/lmtp/src/test/java/org/apache/james/protocols/lmtp/LMTPConfigurationImpl.java
+++ 
b/protocols/lmtp/src/test/java/org/apache/james/protocols/lmtp/LMTPConfigurationImpl.java
@@ -20,7 +20,7 @@ package org.apache.james.protocols.lmtp;
 
 import java.util.Optional;
 
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 
 public class LMTPConfigurationImpl extends LMTPConfiguration {
 
diff --git 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfiguration.java
 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfiguration.java
index ef93169705..10bffd7fea 100644
--- 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfiguration.java
+++ 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfiguration.java
@@ -25,7 +25,7 @@ import java.util.Locale;
 import java.util.Optional;
 import java.util.Set;
 
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.protocols.api.ProtocolConfiguration;
 
 import com.google.common.collect.ImmutableSet;
diff --git 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfigurationImpl.java
 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfigurationImpl.java
index 0d135d24b2..8da9de699d 100644
--- 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfigurationImpl.java
+++ 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/SMTPConfigurationImpl.java
@@ -22,7 +22,7 @@ package org.apache.james.protocols.smtp;
 
 import java.util.Optional;
 
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.protocols.api.ProtocolConfigurationImpl;
 
 /**
diff --git 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java
 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java
index 8d4436fbfc..7cf0f14abc 100644
--- 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java
+++ 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/esmtp/AuthCmdHandler.java
@@ -35,7 +35,7 @@ import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.james.core.Username;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.protocols.api.Request;
 import org.apache.james.protocols.api.Response;
 import org.apache.james.protocols.api.handler.CommandHandler;
diff --git 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/AuthHook.java
 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/AuthHook.java
index 8351eb2367..3a7234356c 100644
--- 
a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/AuthHook.java
+++ 
b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/AuthHook.java
@@ -19,7 +19,7 @@
 package org.apache.james.protocols.smtp.hook;
 
 import org.apache.james.core.Username;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.protocols.smtp.SMTPSession;
 
 /**
diff --git a/server/protocols/jwt/pom.xml b/server/protocols/jwt/pom.xml
index 8470cb2efe..420364ac5b 100644
--- a/server/protocols/jwt/pom.xml
+++ b/server/protocols/jwt/pom.xml
@@ -32,6 +32,10 @@
     <name>Apache James :: Server :: JWT</name>
 
     <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-core</artifactId>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>testing-base</artifactId>
diff --git 
a/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java
 
b/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java
index 63124ca253..637f57de5f 100644
--- 
a/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java
+++ 
b/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java
@@ -22,36 +22,73 @@ package org.apache.james.jwt;
 import java.net.URL;
 import java.util.Optional;
 
+import org.apache.james.core.Username;
 import org.apache.james.jwt.introspection.IntrospectionEndpoint;
 import org.apache.james.jwt.introspection.TokenIntrospectionResponse;
 import org.reactivestreams.Publisher;
 
+import com.google.common.annotations.VisibleForTesting;
 import reactor.core.publisher.Mono;
 
 public class OidcJwtTokenVerifier {
     public static final CheckTokenClient CHECK_TOKEN_CLIENT = new 
DefaultCheckTokenClient();
 
-    public static Optional<String> verifySignatureAndExtractClaim(String 
jwtToken, URL jwksURL, String claimName) {
-        return new JwtTokenVerifier(JwksPublicKeyProvider.of(jwksURL))
-            .verifyAndExtractClaim(jwtToken, claimName, String.class);
+    private final OidcSASLConfiguration oidcSASLConfiguration;
+
+    public OidcJwtTokenVerifier(OidcSASLConfiguration oidcSASLConfiguration) {
+        this.oidcSASLConfiguration = oidcSASLConfiguration;
+    }
+
+    public Optional<Username> validateToken(String token) {
+        if (oidcSASLConfiguration.isCheckTokenByIntrospectionEndpoint()) {
+            return validTokenWithIntrospection(token);
+        } else if (oidcSASLConfiguration.isCheckTokenByUserinfoEndpoint()) {
+            return validTokenWithUserInfo(token);
+        } else {
+            return verifySignatureAndExtractClaim(token)
+                .map(Username::of);
+        }
+    }
+
+    private Optional<Username> validTokenWithUserInfo(String token) {
+        return Mono.from(verifyWithUserinfo(token, 
oidcSASLConfiguration.getUserInfoEndpoint().orElseThrow()))
+            .blockOptional()
+            .map(Username::of);
+    }
+
+    private Optional<Username> validTokenWithIntrospection(String token) {
+        return Mono.from(verifyWithIntrospection(token,
+                oidcSASLConfiguration.getIntrospectionEndpoint()
+                    .map(endpoint -> new IntrospectionEndpoint(endpoint, 
oidcSASLConfiguration.getIntrospectionEndpointAuthorization()))
+                    .orElseThrow()))
+            .blockOptional()
+            .map(Username::of);
+    }
+
+    @VisibleForTesting
+    Optional<String> verifySignatureAndExtractClaim(String jwtToken) {
+        return new 
JwtTokenVerifier(JwksPublicKeyProvider.of(oidcSASLConfiguration.getJwksURL()))
+            .verifyAndExtractClaim(jwtToken, oidcSASLConfiguration.getClaim(), 
String.class);
     }
 
-    public static Publisher<String> verifyWithIntrospection(String jwtToken, 
URL jwksURL, String claimName, IntrospectionEndpoint introspectionEndpoint) {
-        return Mono.fromCallable(() -> 
verifySignatureAndExtractClaim(jwtToken, jwksURL, claimName))
+    @VisibleForTesting
+    Publisher<String> verifyWithIntrospection(String jwtToken, 
IntrospectionEndpoint introspectionEndpoint) {
+        return Mono.fromCallable(() -> 
verifySignatureAndExtractClaim(jwtToken))
             .flatMap(optional -> 
optional.map(Mono::just).orElseGet(Mono::empty))
             .flatMap(claimResult -> 
Mono.from(CHECK_TOKEN_CLIENT.introspect(introspectionEndpoint, jwtToken))
                 .filter(TokenIntrospectionResponse::active)
-                .filter(tokenIntrospectionResponse -> 
tokenIntrospectionResponse.claimByPropertyName(claimName)
+                .filter(tokenIntrospectionResponse -> 
tokenIntrospectionResponse.claimByPropertyName(oidcSASLConfiguration.getClaim())
                     .map(claim -> claim.equals(claimResult))
                     .orElse(false))
                 .map(activeResponse -> claimResult));
     }
 
-    public static Publisher<String> verifyWithUserinfo(String jwtToken, URL 
jwksURL, String claimName, URL userinfoEndpoint) {
-        return Mono.fromCallable(() -> 
verifySignatureAndExtractClaim(jwtToken, jwksURL, claimName))
+    @VisibleForTesting
+   Publisher<String> verifyWithUserinfo(String jwtToken, URL userinfoEndpoint) 
{
+        return Mono.fromCallable(() -> 
verifySignatureAndExtractClaim(jwtToken))
             .flatMap(optional -> 
optional.map(Mono::just).orElseGet(Mono::empty))
             .flatMap(claimResult -> 
Mono.from(CHECK_TOKEN_CLIENT.userInfo(userinfoEndpoint, jwtToken))
-                .filter(userinfoResponse -> 
userinfoResponse.claimByPropertyName(claimName)
+                .filter(userinfoResponse -> 
userinfoResponse.claimByPropertyName(oidcSASLConfiguration.getClaim())
                     .map(claim -> claim.equals(claimResult))
                     .orElse(false))
                 .map(userinfoResponse -> claimResult));
diff --git 
a/protocols/api/src/main/java/org/apache/james/protocols/api/OidcSASLConfiguration.java
 
b/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcSASLConfiguration.java
similarity index 57%
rename from 
protocols/api/src/main/java/org/apache/james/protocols/api/OidcSASLConfiguration.java
rename to 
server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcSASLConfiguration.java
index 92f0053fd9..c7db3d1e99 100644
--- 
a/protocols/api/src/main/java/org/apache/james/protocols/api/OidcSASLConfiguration.java
+++ 
b/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcSASLConfiguration.java
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.protocols.api;
+package org.apache.james.jwt;
 
 import java.net.MalformedURLException;
 import java.net.URI;
@@ -27,11 +27,95 @@ import java.util.Optional;
 
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.tree.ImmutableNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.github.fge.lambdas.Throwing;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 public class OidcSASLConfiguration {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(OidcSASLConfiguration.class);
+
+    private static final boolean FORCE_INTROSPECT = 
Boolean.parseBoolean(System.getProperty("james.sasl.oidc.force.introspect", 
"true"));
+
+    @VisibleForTesting
+    static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+        private URL jwksURL;
+        private String claim;
+        private URL oidcConfigurationURL;
+        private String scope;
+        private Optional<URL> introspectionEndpoint = Optional.empty();
+        private Optional<String> introspectionEndpointAuthorization = 
Optional.empty();
+        private Optional<URL> userInfoEndpoint = Optional.empty();
+
+        private Builder() {
+        }
+
+        public Builder jwksURL(URL jwksURL) {
+            this.jwksURL = jwksURL;
+            return this;
+        }
+
+        public Builder claim(String claim) {
+            this.claim = claim;
+            return this;
+        }
+
+        public Builder oidcConfigurationURL(URL oidcConfigurationURL) {
+            this.oidcConfigurationURL = oidcConfigurationURL;
+            return this;
+        }
+
+        public Builder scope(String scope) {
+            this.scope = scope;
+            return this;
+        }
+
+        public Builder introspectionEndpoint(Optional<URL> 
introspectionEndpoint) {
+            this.introspectionEndpoint = introspectionEndpoint;
+            return this;
+        }
+
+        public Builder introspectionEndpoint(URL introspectionEndpoint) {
+            this.introspectionEndpoint = 
Optional.ofNullable(introspectionEndpoint);
+            return this;
+        }
+
+        public Builder introspectionEndpointAuthorization(Optional<String> 
introspectionEndpointAuthorization) {
+            this.introspectionEndpointAuthorization = 
introspectionEndpointAuthorization;
+            return this;
+        }
+
+        public Builder introspectionEndpointAuthorization(String 
introspectionEndpointAuthorization) {
+            this.introspectionEndpointAuthorization = 
Optional.ofNullable(introspectionEndpointAuthorization);
+            return this;
+        }
+
+        public Builder userInfoEndpoint(Optional<URL> userInfoEndpoint) {
+            this.userInfoEndpoint = userInfoEndpoint;
+            return this;
+        }
+
+        public Builder userInfoEndpoint(URL userInfoEndpoint) {
+            this.userInfoEndpoint = Optional.ofNullable(userInfoEndpoint);
+            return this;
+        }
+
+        public OidcSASLConfiguration build() {
+            Preconditions.checkNotNull(jwksURL, "jwksURL is mandatory");
+            Preconditions.checkNotNull(claim, "claim is mandatory");
+            Preconditions.checkNotNull(oidcConfigurationURL, 
"oidcConfigurationURL is mandatory");
+            Preconditions.checkNotNull(scope, "scope is mandatory");
+
+            return new OidcSASLConfiguration(jwksURL, claim, 
oidcConfigurationURL, scope,
+                introspectionEndpoint, introspectionEndpointAuthorization, 
userInfoEndpoint);
+        }
+    }
 
     public static OidcSASLConfiguration 
parse(HierarchicalConfiguration<ImmutableNode> configuration) throws 
MalformedURLException, URISyntaxException {
         String jwksURL = configuration.getString("jwksURL", null);
@@ -47,6 +131,14 @@ public class OidcSASLConfiguration {
         String introspectionUrl = configuration.getString("introspection.url", 
null);
         String userInfoUrl = configuration.getString("userinfo.url", null);
 
+        if (introspectionUrl == null) {
+            if (FORCE_INTROSPECT) {
+                throw new IllegalArgumentException("'introspection.url' is 
mandatory for secure set up. Disable this check with 
-Djames.sasl.oidc.force.introspect=false.");
+            } else {
+                LOGGER.warn("'introspection.url' is mandatory for secure set 
up. This check was disabled with -Djames.sasl.oidc.force.introspect=false.");
+            }
+        }
+
         return new OidcSASLConfiguration(new URI(jwksURL).toURL(), claim, new 
URI(oidcConfigurationURL).toURL(), scope, Optional.ofNullable(introspectionUrl)
             .map(Throwing.function(value -> new URI(value).toURL())), 
Optional.ofNullable(configuration.getString("introspection.auth", null)),
             Optional.ofNullable(userInfoUrl).map(Throwing.function(value -> 
new URI(value).toURL())));
@@ -60,7 +152,7 @@ public class OidcSASLConfiguration {
     private final Optional<String> introspectionEndpointAuthorization;
     private final Optional<URL> userInfoEndpoint;
 
-    public OidcSASLConfiguration(URL jwksURL,
+    private OidcSASLConfiguration(URL jwksURL,
                                  String claim,
                                  URL oidcConfigurationURL,
                                  String scope,
diff --git 
a/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcJwtTokenVerifierTest.java
 
b/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcJwtTokenVerifierTest.java
index 0c9c1f597a..8fe910dfba 100644
--- 
a/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcJwtTokenVerifierTest.java
+++ 
b/server/protocols/jwt/src/test/java/org/apache/james/jwt/OidcJwtTokenVerifierTest.java
@@ -80,7 +80,7 @@ class OidcJwtTokenVerifierTest {
 
     @Test
     void verifyAndClaimShouldReturnClaimValueWhenValidTokenHasKid() {
-        Optional<String> emailAddress = 
OidcJwtTokenVerifier.verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "email_address");
+        Optional<String> emailAddress = new 
OidcJwtTokenVerifier(configForClaim("email_address")).verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN);
         SoftAssertions.assertSoftly(softly -> {
             softly.assertThat(emailAddress.isPresent()).isTrue();
             softly.assertThat(emailAddress.get()).isEqualTo("[email protected]");
@@ -89,7 +89,7 @@ class OidcJwtTokenVerifierTest {
 
     @Test
     void verifyAndClaimShouldReturnClaimValueWhenValidTokenHasNotKid() {
-        Optional<String> emailAddress = 
OidcJwtTokenVerifier.verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN_HAS_NOT_KID,
 getJwksURL(), "email_address");
+        Optional<String> emailAddress = new 
OidcJwtTokenVerifier(configForClaim("email_address")).verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN_HAS_NOT_KID);
         SoftAssertions.assertSoftly(softly -> {
             softly.assertThat(emailAddress.isPresent()).isTrue();
             softly.assertThat(emailAddress.get()).isEqualTo("[email protected]");
@@ -110,7 +110,7 @@ class OidcJwtTokenVerifierTest {
             .signWith(toPrivateKey(OidcTokenFixture.PRIVATE_KEY_BASE64), 
Jwts.SIG.RS256)
             .compact();
 
-        assertThatThrownBy(() -> 
OidcJwtTokenVerifier.verifySignatureAndExtractClaim(jws, getJwksURL(), "kid"))
+        assertThatThrownBy(() -> new 
OidcJwtTokenVerifier(configForClaim("kid")).verifySignatureAndExtractClaim(jws))
             .isInstanceOf(RuntimeException.class)
             .hasMessageContaining("Rejecting a ZIP JWT");
     }
@@ -126,7 +126,7 @@ class OidcJwtTokenVerifierTest {
         boolean prev = JwtTokenVerifier.allowZipJWT;
         JwtTokenVerifier.allowZipJWT = true;
         try {
-            assertThatCode(() -> 
OidcJwtTokenVerifier.verifySignatureAndExtractClaim(jws, getJwksURL(), "kid"))
+            assertThatCode(() -> new 
OidcJwtTokenVerifier(configForClaim("kid")).verifySignatureAndExtractClaim(jws))
                 .doesNotThrowAnyException();
         } finally {
             JwtTokenVerifier.allowZipJWT = prev;
@@ -135,19 +135,22 @@ class OidcJwtTokenVerifierTest {
 
     @Test
     void verifyAndClaimShouldReturnEmptyWhenValidTokenHasNotFoundKid() {
-        
assertThat(OidcJwtTokenVerifier.verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN_HAS_NOT_FOUND_KID,
 getJwksURL(), "email_address"))
+        assertThat(new OidcJwtTokenVerifier(configForClaim("email_address"))
+            
.verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN_HAS_NOT_FOUND_KID))
             .isEmpty();
     }
 
     @Test
     void verifyAndClaimShouldReturnEmptyWhenClaimNameNotFound() {
-        
assertThat(OidcJwtTokenVerifier.verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "not_found"))
+        assertThat(new OidcJwtTokenVerifier(configForClaim("not_found"))
+            .verifySignatureAndExtractClaim(OidcTokenFixture.VALID_TOKEN))
             .isEmpty();
     }
 
     @Test
     void verifyAndClaimShouldReturnEmptyWhenInvalidToken() {
-        
assertThat(OidcJwtTokenVerifier.verifySignatureAndExtractClaim(OidcTokenFixture.INVALID_TOKEN,
 getJwksURL(), "email_address"))
+        assertThat(new OidcJwtTokenVerifier(configForClaim("email_address"))
+            .verifySignatureAndExtractClaim(OidcTokenFixture.INVALID_TOKEN))
             .isEmpty();
     }
 
@@ -157,7 +160,8 @@ class OidcJwtTokenVerifierTest {
             .when(HttpRequest.request().withPath(USERINFO_PATH))
             .respond(HttpResponse.response().withStatusCode(201));
 
-        assertThatThrownBy(() -> 
Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN, 
getJwksURL(), "email_address", getUserInfoEndpoint()))
+        assertThatThrownBy(() -> Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN, 
getUserInfoEndpoint()))
             .block())
             .isInstanceOf(UserInfoCheckException.class)
             .hasMessageContaining("Error when check token by userInfo");
@@ -171,7 +175,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody("badResponse1", StandardCharsets.UTF_8));
 
-        assertThatThrownBy(() -> 
Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN, 
getJwksURL(), "email_address", getUserInfoEndpoint()))
+        assertThatThrownBy(() -> Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN, 
getUserInfoEndpoint()))
             .block())
             .isInstanceOf(UserInfoCheckException.class)
             .hasMessageContaining("Error when check token by userInfo");
@@ -192,11 +197,25 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(userInfoResponse, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "preferred_username", getUserInfoEndpoint()))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("preferred_username"))
+                .verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN, 
getUserInfoEndpoint()))
             .block())
             .isNull();
     }
 
+    private OidcSASLConfiguration configForClaim(String claim) {
+        try {
+            return OidcSASLConfiguration.builder()
+                .jwksURL(getJwksURL())
+                .scope("email")
+                .oidcConfigurationURL(new URL("https://whatever.nte";))
+                .claim(claim)
+                .build();
+        } catch (MalformedURLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Test
     void verifyWithUserinfoShouldReturnClaimValueWhenPassCheckToken() {
         mockServer
@@ -205,7 +224,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(USERINFO_RESPONSE, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "email_address", getUserInfoEndpoint()))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithUserinfo(OidcTokenFixture.VALID_TOKEN, 
getUserInfoEndpoint()))
             .block())
             .isEqualTo("[email protected]");
     }
@@ -218,7 +238,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(USERINFO_RESPONSE, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(OidcTokenFixture.INVALID_TOKEN,
 getJwksURL(), "email_address", getUserInfoEndpoint()))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithUserinfo(OidcTokenFixture.INVALID_TOKEN, 
getUserInfoEndpoint()))
             .block())
             .isNull();
     }
@@ -239,7 +260,7 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(userInfoResponse, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(OidcTokenFixture.INVALID_TOKEN,
 getJwksURL(), "preferred_username", getUserInfoEndpoint()))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("preferred_username")).verifyWithUserinfo(OidcTokenFixture.INVALID_TOKEN,
 getUserInfoEndpoint()))
             .block())
             .isNull();
     }
@@ -250,8 +271,8 @@ class OidcJwtTokenVerifierTest {
             .when(HttpRequest.request().withPath(INTROSPECTION_PATH))
             .respond(HttpResponse.response().withStatusCode(201));
 
-        assertThatThrownBy(() -> 
Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "email_address",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThatThrownBy(() -> Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                    .verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isInstanceOf(TokenIntrospectionException.class)
             .hasMessageContaining("Error when introspecting token");
@@ -265,8 +286,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody("badResponse1", StandardCharsets.UTF_8));
 
-        assertThatThrownBy(() -> 
Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "email_address",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThatThrownBy(() -> Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isInstanceOf(TokenIntrospectionException.class)
             .hasMessageContaining("Error when introspecting token");
@@ -295,8 +316,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(introspectionResponse, StandardCharsets.UTF_8));
 
-        assertThatThrownBy(() -> 
Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "email_address",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThatThrownBy(() -> Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isInstanceOf(TokenIntrospectionException.class)
             .hasMessageContaining("Error when introspecting token");
@@ -326,8 +347,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(introspectionResponse, StandardCharsets.UTF_8));
 
-        assertThatThrownBy(() -> 
Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "email_address",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThatThrownBy(() -> Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isInstanceOf(TokenIntrospectionException.class)
             .hasMessageContaining("Error when introspecting token");
@@ -341,8 +362,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(INTROSPECTION_RESPONSE, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "email_address",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isEqualTo("[email protected]");
     }
@@ -363,8 +384,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(introspectionResponse, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "preferred_username",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("preferred_username"))
+                .verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isNull();
     }
@@ -385,8 +406,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(introspectionResponse, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN,
 getJwksURL(), "preferred_username",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("preferred_username"))
+                .verifyWithIntrospection(OidcTokenFixture.VALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isNull();
     }
@@ -399,8 +420,8 @@ class OidcJwtTokenVerifierTest {
                 .withHeader("Content-Type", "application/json")
                 .withBody(INTROSPECTION_RESPONSE, StandardCharsets.UTF_8));
 
-        
assertThat(Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(OidcTokenFixture.INVALID_TOKEN,
 getJwksURL(), "email_address",
-                new IntrospectionEndpoint(getIntrospectionEndpoint(), 
Optional.empty())))
+        assertThat(Mono.from(new 
OidcJwtTokenVerifier(configForClaim("email_address"))
+                .verifyWithIntrospection(OidcTokenFixture.INVALID_TOKEN, new 
IntrospectionEndpoint(getIntrospectionEndpoint(), Optional.empty())))
             .block())
             .isNull();
     }
diff --git a/server/protocols/protocols-imap4/pom.xml 
b/server/protocols/protocols-imap4/pom.xml
index 9e66ff25c5..e31afd3564 100644
--- a/server/protocols/protocols-imap4/pom.xml
+++ b/server/protocols/protocols-imap4/pom.xml
@@ -188,7 +188,7 @@
                     </systemPropertyVariables>
                     <argLine>-Djava.library.path=
                         
-javaagent:"${settings.localRepository}"/org/jacoco/org.jacoco.agent/${jacoco-maven-plugin.version}/org.jacoco.agent-${jacoco-maven-plugin.version}-runtime.jar=destfile=${basedir}/target/jacoco.exec
-                        -Xms1024m -Xmx2048m</argLine>
+                        -Xms1024m -Xmx2048m 
-Djames.sasl.oidc.force.introspect=false</argLine>
                     <reuseForks>true</reuseForks>
                     <!-- Fail tests longer than 30 minutes, prevent form 
random locking tests -->
                     
<forkedProcessTimeoutInSeconds>1800</forkedProcessTimeoutInSeconds>
diff --git 
a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java
 
b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java
index b66129f04c..e0295b4e09 100644
--- 
a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java
+++ 
b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java
@@ -50,10 +50,10 @@ import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.api.process.SelectedMailbox;
 import org.apache.james.imap.decode.ImapDecoder;
 import org.apache.james.imap.encode.ImapEncoder;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.metrics.api.GaugeRegistry;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
 import org.apache.james.protocols.lib.netty.AbstractConfigurableAsyncServer;
 import org.apache.james.protocols.netty.AbstractChannelPipelineFactory;
 import org.apache.james.protocols.netty.ChannelHandlerFactory;
diff --git 
a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java
 
b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java
index 7ec85cdcab..32f663d6d3 100644
--- 
a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java
+++ 
b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java
@@ -39,8 +39,8 @@ import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.api.process.SelectedMailbox;
 import org.apache.james.imap.encode.ImapResponseWriter;
 import org.apache.james.imap.message.Literal;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
 import org.apache.james.protocols.netty.Encryption;
 import org.apache.james.protocols.netty.LineHandlerAware;
 import org.apache.james.util.MDCBuilder;
diff --git 
a/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/netty/LMTPServer.java
 
b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/netty/LMTPServer.java
index 9eb8f12264..f367ea7b9d 100644
--- 
a/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/netty/LMTPServer.java
+++ 
b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/netty/LMTPServer.java
@@ -23,9 +23,9 @@ import java.util.Optional;
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.commons.configuration2.tree.ImmutableNode;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.lmtpserver.CoreCmdHandlerLoader;
 import org.apache.james.lmtpserver.jmx.JMXHandlersLoader;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
 import org.apache.james.protocols.api.ProtocolSession;
 import org.apache.james.protocols.api.ProtocolTransport;
 import org.apache.james.protocols.lib.handler.HandlersPackage;
diff --git a/server/protocols/protocols-smtp/pom.xml 
b/server/protocols/protocols-smtp/pom.xml
index a723de12cc..e31dd5bb8d 100644
--- a/server/protocols/protocols-smtp/pom.xml
+++ b/server/protocols/protocols-smtp/pom.xml
@@ -226,7 +226,7 @@
                     </systemPropertyVariables>
                     <argLine>-Djava.library.path=
                         
-javaagent:"${settings.localRepository}"/org/jacoco/org.jacoco.agent/${jacoco-maven-plugin.version}/org.jacoco.agent-${jacoco-maven-plugin.version}-runtime.jar=destfile=${basedir}/target/jacoco.exec
-                        -Xms512m -Xmx1024m</argLine>
+                        -Xms512m -Xmx1024m 
-Djames.sasl.oidc.force.introspect=false</argLine>
                     <reuseForks>true</reuseForks>
                     <!-- Fail tests longer than 30 minutes, prevent form 
random locking tests -->
                     
<forkedProcessTimeoutInSeconds>1800</forkedProcessTimeoutInSeconds>
diff --git 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/ConfigurationAuthHook.java
 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/ConfigurationAuthHook.java
index 735ff574ff..f68527afc1 100644
--- 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/ConfigurationAuthHook.java
+++ 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/ConfigurationAuthHook.java
@@ -29,7 +29,7 @@ import 
org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.commons.configuration2.tree.ImmutableNode;
 import org.apache.commons.lang3.NotImplementedException;
 import org.apache.james.core.Username;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.protocols.smtp.SMTPSession;
 import org.apache.james.protocols.smtp.hook.AuthHook;
 import org.apache.james.protocols.smtp.hook.HookResult;
diff --git 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/UsersRepositoryAuthHook.java
 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/UsersRepositoryAuthHook.java
index 91eda43e86..d60b0dafcc 100644
--- 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/UsersRepositoryAuthHook.java
+++ 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/UsersRepositoryAuthHook.java
@@ -24,11 +24,10 @@ import jakarta.inject.Inject;
 
 import org.apache.james.core.Username;
 import org.apache.james.jwt.OidcJwtTokenVerifier;
-import org.apache.james.jwt.introspection.IntrospectionEndpoint;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.mailbox.Authorizator;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.protocols.api.OIDCSASLParser;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
 import org.apache.james.protocols.smtp.SMTPSession;
 import org.apache.james.protocols.smtp.hook.AuthHook;
 import org.apache.james.protocols.smtp.hook.HookResult;
@@ -38,8 +37,6 @@ import org.apache.james.user.api.UsersRepositoryException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import reactor.core.publisher.Mono;
-
 /**
  * This Auth hook can be used to authenticate against the james user repository
  */
@@ -77,7 +74,7 @@ public class UsersRepositoryAuthHook implements AuthHook {
     @Override
     public HookResult doSasl(SMTPSession session, OidcSASLConfiguration 
configuration, String initialResponse) {
         return OIDCSASLParser.parse(initialResponse)
-            .flatMap(oidcInitialResponseValue -> validateToken(configuration, 
oidcInitialResponseValue.getToken())
+            .flatMap(oidcInitialResponseValue -> new 
OidcJwtTokenVerifier(configuration).validateToken(oidcInitialResponseValue.getToken())
                 .map(authenticatedUser -> {
                     Username associatedUser = 
Username.of(oidcInitialResponseValue.getAssociatedUser());
                     if (!associatedUser.equals(authenticatedUser)) {
@@ -115,35 +112,4 @@ public class UsersRepositoryAuthHook implements AuthHook {
             return HookResult.DECLINED;
         }
     }
-
-    private Optional<Username> validateToken(OidcSASLConfiguration 
oidcSASLConfiguration, String token) {
-        if (oidcSASLConfiguration.isCheckTokenByIntrospectionEndpoint()) {
-            return validTokenWithIntrospection(oidcSASLConfiguration, token);
-        } else if (oidcSASLConfiguration.isCheckTokenByUserinfoEndpoint()) {
-            return validTokenWithUserInfo(oidcSASLConfiguration, token);
-        } else {
-            return OidcJwtTokenVerifier.verifySignatureAndExtractClaim(token, 
oidcSASLConfiguration.getJwksURL(), oidcSASLConfiguration.getClaim())
-                .map(Username::of);
-        }
-    }
-
-    private Optional<Username> validTokenWithUserInfo(OidcSASLConfiguration 
oidcSASLConfiguration, String token) {
-        return Mono.from(OidcJwtTokenVerifier.verifyWithUserinfo(token,
-                oidcSASLConfiguration.getJwksURL(),
-                oidcSASLConfiguration.getClaim(),
-                oidcSASLConfiguration.getUserInfoEndpoint().orElseThrow()))
-            .blockOptional()
-            .map(Username::of);
-    }
-
-    private static Optional<Username> 
validTokenWithIntrospection(OidcSASLConfiguration oidcSASLConfiguration, String 
token) {
-        return Mono.from(OidcJwtTokenVerifier.verifyWithIntrospection(token,
-                oidcSASLConfiguration.getJwksURL(),
-                oidcSASLConfiguration.getClaim(),
-                oidcSASLConfiguration.getIntrospectionEndpoint()
-                    .map(endpoint -> new IntrospectionEndpoint(endpoint, 
oidcSASLConfiguration.getIntrospectionEndpointAuthorization()))
-                    .orElseThrow()))
-            .blockOptional()
-            .map(Username::of);
-    }
 }
diff --git 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
index 5229643bcb..5b2d0e3987 100644
--- 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
+++ 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
@@ -44,7 +44,7 @@ import org.apache.james.core.Disconnector;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
 import org.apache.james.dnsservice.library.netmatcher.NetMatcher;
-import org.apache.james.protocols.api.OidcSASLConfiguration;
+import org.apache.james.jwt.OidcSASLConfiguration;
 import org.apache.james.protocols.api.ProtocolSession;
 import org.apache.james.protocols.api.ProtocolTransport;
 import org.apache.james.protocols.lib.handler.HandlersPackage;


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

Reply via email to