You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rc...@apache.org on 2020/06/05 03:26:01 UTC

[james-project] 04/05: JAMES-3093 Add JWT Authentication, integration test for JMAP RFC 8621

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

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

commit c3965277428a9ffdef6bdf997163a66290d7a139
Author: duc91 <du...@gmail.com>
AuthorDate: Fri May 22 11:51:44 2020 +0700

    JAMES-3093 Add JWT Authentication, integration test for JMAP RFC 8621
---
 .../james/jmap/draft/DraftMethodsModule.java       |   2 +-
 .../james/jmap/rfc8621/RFC8621MethodsModule.java   |  10 +-
 .../apache/james/jmap/http/AuthenticatorTest.java  |   2 +-
 .../jmap/http/JWTAuthenticationStrategyTest.java   |   3 +-
 .../DistributedBasicAuthenticationTest.java        |   4 +-
 .../distributed/DistributedEchoMethodTest.java     |   1 -
 ....java => DistributedJWTAuthenticationTest.java} |   6 +-
 ...act.scala => BasicAuthenticationContract.scala} |  46 ++-------
 .../james/jmap/rfc8621/contract/Fixture.scala      |  35 ++++++-
 .../contract/JWTAuthenticationContract.scala       | 107 +++++++++++++++++++++
 .../rfc8621/memory/MemoryAuthenticationTest.java   |   4 +-
 server/protocols/jmap-rfc-8621/pom.xml             |   4 +
 server/protocols/jmap/pom.xml                      |  18 +++-
 .../MailboxSessionCreationException.java           |   2 +-
 .../james/jmap/jwt}/JWTAuthenticationStrategy.java |  10 +-
 15 files changed, 192 insertions(+), 62 deletions(-)

diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/DraftMethodsModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/DraftMethodsModule.java
index 72488fd..d95d95c 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/DraftMethodsModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/draft/DraftMethodsModule.java
@@ -46,8 +46,8 @@ import org.apache.james.jmap.draft.methods.SetVacationResponseMethod;
 import org.apache.james.jmap.http.AccessTokenAuthenticationStrategy;
 import org.apache.james.jmap.http.Authenticator;
 import org.apache.james.jmap.http.InjectionKeys;
-import org.apache.james.jmap.http.JWTAuthenticationStrategy;
 import org.apache.james.jmap.http.QueryParameterAccessTokenAuthenticationStrategy;
+import org.apache.james.jmap.jwt.JWTAuthenticationStrategy;
 import org.apache.james.metrics.api.MetricFactory;
 
 import com.google.inject.AbstractModule;
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
index adffa45..f085137 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/rfc8621/RFC8621MethodsModule.java
@@ -26,6 +26,7 @@ import org.apache.james.jmap.http.Authenticator;
 import org.apache.james.jmap.http.BasicAuthenticationStrategy;
 import org.apache.james.jmap.http.rfc8621.InjectionKeys;
 import org.apache.james.jmap.json.Serializer;
+import org.apache.james.jmap.jwt.JWTAuthenticationStrategy;
 import org.apache.james.jmap.method.CoreEcho;
 import org.apache.james.jmap.method.Method;
 import org.apache.james.jmap.routes.JMAPApiRoutes;
@@ -57,7 +58,12 @@ public class RFC8621MethodsModule extends AbstractModule {
     @Provides
     @Singleton
     @Named(InjectionKeys.RFC_8621)
-    Authenticator provideAuthenticator(MetricFactory metricFactory, BasicAuthenticationStrategy basicAuthenticationStrategy) {
-        return Authenticator.of(metricFactory, basicAuthenticationStrategy);
+    Authenticator provideAuthenticator(MetricFactory metricFactory,
+                                       BasicAuthenticationStrategy basicAuthenticationStrategy,
+                                       JWTAuthenticationStrategy jwtAuthenticationStrategy) {
+        return Authenticator.of(
+            metricFactory,
+            basicAuthenticationStrategy,
+            jwtAuthenticationStrategy);
     }
 }
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AuthenticatorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AuthenticatorTest.java
index 59c3816..f009783 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AuthenticatorTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/AuthenticatorTest.java
@@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.james.core.Username;
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.jmap.api.access.AccessTokenRepository;
-import org.apache.james.jmap.draft.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
 import org.apache.james.jmap.exceptions.UnauthorizedException;
 import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
 import org.apache.james.mailbox.MailboxSession;
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JWTAuthenticationStrategyTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JWTAuthenticationStrategyTest.java
index 49d5144..ac5169d 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JWTAuthenticationStrategyTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JWTAuthenticationStrategyTest.java
@@ -26,7 +26,8 @@ import static org.mockito.Mockito.when;
 
 import org.apache.james.core.Username;
 import org.apache.james.domainlist.api.DomainList;
-import org.apache.james.jmap.draft.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.jwt.JWTAuthenticationStrategy;
 import org.apache.james.jwt.JwtTokenVerifier;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
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/DistributedBasicAuthenticationTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedBasicAuthenticationTest.java
index 1d7932b..d4f2151 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedBasicAuthenticationTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedBasicAuthenticationTest.java
@@ -26,14 +26,14 @@ import org.apache.james.DockerElasticSearchExtension;
 import org.apache.james.GuiceJamesServer;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
-import org.apache.james.jmap.rfc8621.contract.AuthenticationContract;
+import org.apache.james.jmap.rfc8621.contract.BasicAuthenticationContract;
 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;
 
-public class DistributedBasicAuthenticationTest implements AuthenticationContract {
+public class DistributedBasicAuthenticationTest implements BasicAuthenticationContract {
     @RegisterExtension
     static JamesServerExtension testExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
         CassandraRabbitMQJamesConfiguration.builder()
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/DistributedEchoMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
index c55787b..1e504db 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
@@ -23,7 +23,6 @@ import org.apache.james.CassandraExtension;
 import org.apache.james.CassandraRabbitMQJamesConfiguration;
 import org.apache.james.CassandraRabbitMQJamesServerMain;
 import org.apache.james.DockerElasticSearchExtension;
-import org.apache.james.GuiceJamesServer;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
 import org.apache.james.jmap.rfc8621.contract.EchoMethodContract;
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/DistributedEchoMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedJWTAuthenticationTest.java
similarity index 93%
copy from server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
copy to server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedJWTAuthenticationTest.java
index c55787b..0a8787b 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedJWTAuthenticationTest.java
@@ -23,18 +23,16 @@ import org.apache.james.CassandraExtension;
 import org.apache.james.CassandraRabbitMQJamesConfiguration;
 import org.apache.james.CassandraRabbitMQJamesServerMain;
 import org.apache.james.DockerElasticSearchExtension;
-import org.apache.james.GuiceJamesServer;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
-import org.apache.james.jmap.rfc8621.contract.EchoMethodContract;
+import org.apache.james.jmap.rfc8621.contract.JWTAuthenticationContract;
 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;
 
-public class DistributedEchoMethodTest implements EchoMethodContract {
-
+public class DistributedJWTAuthenticationTest implements JWTAuthenticationContract {
     @RegisterExtension
     static JamesServerExtension testExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
         CassandraRabbitMQJamesConfiguration.builder()
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/AuthenticationContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/BasicAuthenticationContract.scala
similarity index 68%
rename from server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/AuthenticationContract.scala
rename to server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/BasicAuthenticationContract.scala
index 6716c1a..f257bbf 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/AuthenticationContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/BasicAuthenticationContract.scala
@@ -1,44 +1,27 @@
-/****************************************************************
- * 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 java.nio.charset.StandardCharsets.UTF_8
 import java.util.Base64
 
 import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
-import io.restassured.RestAssured._
+import io.restassured.RestAssured.{`given`, requestSpecification}
 import io.restassured.authentication.NoAuthScheme
-import io.restassured.http.{Header, Headers}
+import io.restassured.http.Header
 import org.apache.http.HttpStatus.{SC_OK, SC_UNAUTHORIZED}
 import org.apache.james.GuiceJamesServer
-import org.apache.james.jmap.rfc8621.contract.AuthenticationContract._
+import org.apache.james.jmap.rfc8621.contract.BasicAuthenticationContract._
 import org.apache.james.jmap.rfc8621.contract.Fixture._
 import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags
 import org.apache.james.utils.DataProbeImpl
 import org.junit.jupiter.api.{BeforeEach, Tag, Test}
 
-object AuthenticationContract {
-  private val AUTHORIZATION_HEADER: String = "Authorization"
+object BasicAuthenticationContract {
+  private def toBase64(stringValue: String): String = {
+    Base64.getEncoder.encodeToString(stringValue.getBytes(UTF_8))
+  }
 }
 
-trait AuthenticationContract {
+trait BasicAuthenticationContract {
   @BeforeEach
   def setUp(server: GuiceJamesServer): Unit = {
     server.getProbe(classOf[DataProbeImpl])
@@ -55,7 +38,7 @@ trait AuthenticationContract {
 
   @Test
   def postShouldRespondUnauthorizedWhenNoAuthorizationHeader(): Unit = {
-    `given`()
+    given()
       .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
       .body(ECHO_REQUEST_OBJECT)
     .when()
@@ -137,15 +120,4 @@ trait AuthenticationContract {
     .then
       .statusCode(SC_UNAUTHORIZED)
   }
-
-  private def getHeadersWith(authHeader: Header): Headers = {
-    new Headers(
-      new Header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER),
-      authHeader
-    )
-  }
-
-  private def toBase64(stringValue: String): String = {
-    Base64.getEncoder.encodeToString(stringValue.getBytes(UTF_8))
-  }
 }
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/Fixture.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
index 56be582..be44b77 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/Fixture.scala
@@ -21,11 +21,12 @@ package org.apache.james.jmap.rfc8621.contract
 
 import java.nio.charset.StandardCharsets
 
+import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
 import io.restassured.authentication.PreemptiveBasicAuthScheme
 import io.restassured.builder.RequestSpecBuilder
 import io.restassured.config.EncoderConfig.encoderConfig
 import io.restassured.config.RestAssuredConfig.newConfig
-import io.restassured.http.ContentType
+import io.restassured.http.{ContentType, Header, Headers}
 import org.apache.james.GuiceJamesServer
 import org.apache.james.core.{Domain, Username}
 import org.apache.james.jmap.JMAPUrls.JMAP
@@ -50,6 +51,14 @@ object Fixture {
     authScheme
   }
 
+  def getHeadersWith(authHeader: Header): Headers = {
+    new Headers(
+      new Header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER),
+      authHeader
+    )
+  }
+
+  val AUTHORIZATION_HEADER: String = "Authorization"
   val DOMAIN: Domain = Domain.of("domain.tld")
   val DOMAIN_WITH_SPACE: Domain = Domain.of("dom ain.tld")
   val _2_DOT_DOMAIN: Domain = Domain.of("do.main.tld")
@@ -91,4 +100,28 @@ object Fixture {
       |}""".stripMargin
 
   val ACCEPT_RFC8621_VERSION_HEADER: String = "application/json; jmapVersion=rfc-8621"
+
+  val USER: Username = Username.fromLocalPartWithDomain("user", DOMAIN)
+  val USER_PASSWORD: String = "user"
+
+  // These tokens copied from class JwtTokenVerifierTest
+  val USER_TOKEN: String =
+    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGRvbWFpbi50bGQifQ.U-dUPv6OU6KO5N7CooHUfMkCd" +
+      "FJHx2F3H4fm7Q79g1BPfBSkifPj5xyVlZ0JwEGXypC4zBw9ay3l4DxzX7D_6p1Hx_ihXsoLx1Ca-WUo44x-XRSpPfgxiZjHCJkGBLMV3RZlA" +
+      "jip-d18mxkcX3JGplX_sCQkFisduAOAHuKSUg9wI6VBgUQi_0B35FYv6tP_bD6eFtvaAUN9QyXXh8UQjEp8CO12lRz6enfLx_V6BG_fEMkee" +
+      "6vRqdEqx_F9OF3eWTe1giMp_JhQ7_l1OXXtbd4TndVvTeuVy4irPbsRc-M8x_-qTDpFp6saRRsyOcFspxPp5n3yIhEK7B3UZiseXw"
+
+  val UNKNOWN_USER_TOKEN: String =
+    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
+      "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZD" +
+      "N_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf49" +
+      "t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2qN" +
+      "OR8Q31ydinyqzXvCSzVJOf6T60-w"
+
+  val INVALID_JWT_TOKEN: String =
+    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbkBvcGVu" +
+      "thispartiscompletelywrong.reQc3DiVvbQHF08oW1qOUyDJyv3tfzDNk8jhVZequiCdOI9vXnRlOe" +
+      "-yDYktd4WT8MYhqY7MgS-wR0vO9jZFv8ZCgd_MkKCvCO0HmMjP5iQPZ0kqGkgWUH7X123tfR38MfbCVAdPDba-K3MfkogV1xvDhlkPScFr_6MxE" +
+      "xtedOK2JnQZn7t9sUzSrcyjWverm7gZkPptkIVoS8TsEeMMME5vFXe_nqkEG69q3kuBUm_33tbR5oNS0ZGZKlG9r41lHBjyf9J1xN4UYV8n866d" +
+      "a7RPPCzshIWUtO0q9T2umWTnp-6OnOdBCkndrZmRR6pPxsD5YL0_77Wq8KT_5__fGA"
 }
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/JWTAuthenticationContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/JWTAuthenticationContract.scala
new file mode 100644
index 0000000..5fbad90
--- /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/JWTAuthenticationContract.scala
@@ -0,0 +1,107 @@
+/****************************************************************
+ * 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 io.restassured.RestAssured.{`given`, requestSpecification}
+import io.restassured.authentication.NoAuthScheme
+import io.restassured.http.Header
+import org.apache.http.HttpStatus._
+import org.apache.james.GuiceJamesServer
+import org.apache.james.core.Username
+import org.apache.james.jmap.rfc8621.contract.Fixture._
+import org.apache.james.jmap.rfc8621.contract.JWTAuthenticationContract._
+import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags
+import org.apache.james.utils.DataProbeImpl
+import org.junit.jupiter.api.{BeforeEach, Tag, Test}
+
+object JWTAuthenticationContract {
+  private val USER_TOKEN: String =
+    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGRvbWFpbi50bGQifQ.U-dUPv6OU6KO5N7CooHUfMkCd" +
+      "FJHx2F3H4fm7Q79g1BPfBSkifPj5xyVlZ0JwEGXypC4zBw9ay3l4DxzX7D_6p1Hx_ihXsoLx1Ca-WUo44x-XRSpPfgxiZjHCJkGBLMV3RZlA" +
+      "jip-d18mxkcX3JGplX_sCQkFisduAOAHuKSUg9wI6VBgUQi_0B35FYv6tP_bD6eFtvaAUN9QyXXh8UQjEp8CO12lRz6enfLx_V6BG_fEMkee" +
+      "6vRqdEqx_F9OF3eWTe1giMp_JhQ7_l1OXXtbd4TndVvTeuVy4irPbsRc-M8x_-qTDpFp6saRRsyOcFspxPp5n3yIhEK7B3UZiseXw"
+
+  private val UNKNOWN_USER_TOKEN: String =
+    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
+      "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZD" +
+      "N_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf49" +
+      "t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2qN" +
+      "OR8Q31ydinyqzXvCSzVJOf6T60-w"
+
+  private val INVALID_JWT_TOKEN: String =
+    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbkBvcGVu" +
+      "thispartiscompletelywrong.reQc3DiVvbQHF08oW1qOUyDJyv3tfzDNk8jhVZequiCdOI9vXnRlOe" +
+      "-yDYktd4WT8MYhqY7MgS-wR0vO9jZFv8ZCgd_MkKCvCO0HmMjP5iQPZ0kqGkgWUH7X123tfR38MfbCVAdPDba-K3MfkogV1xvDhlkPScFr_6MxE" +
+      "xtedOK2JnQZn7t9sUzSrcyjWverm7gZkPptkIVoS8TsEeMMME5vFXe_nqkEG69q3kuBUm_33tbR5oNS0ZGZKlG9r41lHBjyf9J1xN4UYV8n866d" +
+      "a7RPPCzshIWUtO0q9T2umWTnp-6OnOdBCkndrZmRR6pPxsD5YL0_77Wq8KT_5__fGA"
+
+  private val USER: Username = Username.fromLocalPartWithDomain("user", DOMAIN)
+  private val USER_PASSWORD: String = "user"
+}
+
+trait JWTAuthenticationContract {
+  @BeforeEach
+  def setUp(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[DataProbeImpl])
+      .fluent()
+      .addDomain(DOMAIN.asString())
+      .addUser(USER.asString(), USER_PASSWORD)
+
+    requestSpecification = baseRequestSpecBuilder(server)
+      .setAuth(new NoAuthScheme)
+      .build
+  }
+
+  @Tag(CategoryTags.BASIC_FEATURE)
+  @Test
+  def getMustReturnEndpointsWhenValidJwtAuthorizationHeader(): Unit = {
+    `given`
+      .headers(getHeadersWith(new Header(AUTHORIZATION_HEADER, s"Bearer $USER_TOKEN")))
+      .body(ECHO_REQUEST_OBJECT)
+    .when
+      .post()
+    .`then`
+      .statusCode(SC_OK)
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMustReturnOKWhenValidUnknownUserJwtAuthorizationHeader(): Unit = {
+    val authHeader: Header = new Header(AUTHORIZATION_HEADER, s"Bearer $UNKNOWN_USER_TOKEN")
+    `given`()
+      .headers(getHeadersWith(authHeader))
+      .body(ECHO_REQUEST_OBJECT)
+    .when()
+      .post()
+    .then
+      .statusCode(SC_UNAUTHORIZED)
+  }
+
+  @Test
+  @Tag(CategoryTags.BASIC_FEATURE)
+  def getMustReturnBadCredentialsWhenInvalidJwtAuthorizationHeader(): Unit = {
+    `given`
+      .headers(getHeadersWith(new Header(AUTHORIZATION_HEADER, s"Bearer $INVALID_JWT_TOKEN")))
+      .body(ECHO_REQUEST_OBJECT)
+    .when
+      .post()
+    .`then`
+      .statusCode(SC_UNAUTHORIZED)
+  }
+}
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/MemoryAuthenticationTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryAuthenticationTest.java
index c99bf0f..5fd5b42 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryAuthenticationTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryAuthenticationTest.java
@@ -24,11 +24,11 @@ import static org.apache.james.MemoryJamesServerMain.IN_MEMORY_SERVER_AGGREGATE_
 import org.apache.james.GuiceJamesServer;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
-import org.apache.james.jmap.rfc8621.contract.AuthenticationContract;
+import org.apache.james.jmap.rfc8621.contract.BasicAuthenticationContract;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
-public class MemoryAuthenticationTest implements AuthenticationContract {
+public class MemoryAuthenticationTest implements BasicAuthenticationContract {
     @RegisterExtension
     static JamesServerExtension testExtension = new JamesServerBuilder<>(JamesServerBuilder.defaultConfigurationProvider())
         .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
diff --git a/server/protocols/jmap-rfc-8621/pom.xml b/server/protocols/jmap-rfc-8621/pom.xml
index feccb0c..0cf4eae 100644
--- a/server/protocols/jmap-rfc-8621/pom.xml
+++ b/server/protocols/jmap-rfc-8621/pom.xml
@@ -73,6 +73,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-jwt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>metrics-tests</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/server/protocols/jmap/pom.xml b/server/protocols/jmap/pom.xml
index a052b80..37cc3db 100644
--- a/server/protocols/jmap/pom.xml
+++ b/server/protocols/jmap/pom.xml
@@ -32,11 +32,19 @@
     <dependencies>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>apache-james-mailbox-api</artifactId>
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
-            <artifactId>james-core</artifactId>
+            <artifactId>james-server-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-jwt</artifactId>
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
@@ -73,15 +81,15 @@
             <artifactId>javax.annotation-api</artifactId>
         </dependency>
         <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpcore</artifactId>
             <version>4.4.13</version>
         </dependency>
         <dependency>
-            <groupId>javax.annotation</groupId>
-            <artifactId>javax.annotation-api</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
             <scope>test</scope>
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxSessionCreationException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java
similarity index 96%
rename from server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxSessionCreationException.java
rename to server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java
index 8865c15..a1971c2 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/exceptions/MailboxSessionCreationException.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/MailboxSessionCreationException.java
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.jmap.draft.exceptions;
+package org.apache.james.jmap.exceptions;
 
 public class MailboxSessionCreationException extends RuntimeException {
 
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JWTAuthenticationStrategy.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/jwt/JWTAuthenticationStrategy.java
similarity index 88%
rename from server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JWTAuthenticationStrategy.java
rename to server/protocols/jmap/src/main/java/org/apache/james/jmap/jwt/JWTAuthenticationStrategy.java
index 6f4a275..41566a7 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JWTAuthenticationStrategy.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/jwt/JWTAuthenticationStrategy.java
@@ -16,14 +16,15 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.jmap.http;
+package org.apache.james.jmap.jwt;
 
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
 
 import org.apache.james.core.Username;
-import org.apache.james.jmap.draft.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
+import org.apache.james.jmap.http.AuthenticationStrategy;
 import org.apache.james.jwt.JwtTokenVerifier;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
@@ -37,14 +38,15 @@ import reactor.netty.http.server.HttpServerRequest;
 
 public class JWTAuthenticationStrategy implements AuthenticationStrategy {
 
-    @VisibleForTesting static final String AUTHORIZATION_HEADER_PREFIX = "Bearer ";
+    @VisibleForTesting
+    public static final String AUTHORIZATION_HEADER_PREFIX = "Bearer ";
     private final JwtTokenVerifier tokenManager;
     private final MailboxManager mailboxManager;
     private final UsersRepository usersRepository;
 
     @Inject
     @VisibleForTesting
-    JWTAuthenticationStrategy(JwtTokenVerifier tokenManager, MailboxManager mailboxManager, UsersRepository usersRepository) {
+    public JWTAuthenticationStrategy(JwtTokenVerifier tokenManager, MailboxManager mailboxManager, UsersRepository usersRepository) {
         this.tokenManager = tokenManager;
         this.mailboxManager = mailboxManager;
         this.usersRepository = usersRepository;


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org