The MicroProfile JWT(MP-JWT) TCK consists of tests that validate the authentication and authorization of JAX-RS application endpoints secured using an MP-JWT authentication mechanism that both authenticates and authorizes access based on an MP-JWT token passed in via the HTTP Authorization header as a bearer token. There are 4 categories of unit tests in the base required TCK:
-
Tests that validate the basic behavior of the MP-JWT JsonWebToken implementation independent of any JAX-RS application. Tests in this category are marked with the TestNG groups of "jwt" and "utils".
-
Tests that validate the authentication and authorization of JAX-RS application endpoints secured using
@RolesAllowed
annotations. This includes testing the requirements for rejecting MP-JWT tokens. Tests in this category are marked with the TestNG group "jaxrs". -
Tests that validate the injection of
o.e.m.j.ClaimValue
,javax.json.JsonValue
andjavax.inject.Provider
wrappers for the JsonWebToken claim values. Tests in this category are marked with the TestNG groups of "cdi", "cdi-json" and "cdi-provider". -
Tests that validate the use of the MicroProfile configuration feature to externalize the information necessariy to verify the MP-JWT signer and optional iss claim.
The TestNG testsuite for these categories of tests can be found in the tck directory
src/test/resources/suites
, file tck-base-suite.xml
.
In addition to the categories of tests defined above, there are optional tests that illustrate the expected container API integration for Java EE container runtimes beyond JAX-RS. These tests are marked with the TestNG groups "ejb-optional", "jacc-optional", "servlet-optional" and "ee-security-optional". An MP-JWT implementation is not required to pass these tests in order to be considered a valid implementation.
The TestNG testsuite for these categories + the base categories of tests can be found in the tck directory
src/test/resources/suites
, file tck-full-suite.xml
.
The TCK includes resources for the the MP-JWT token payload content as well as test issuer public and private RSA keys. These
are used by the unit tests along with the org.eclipse.microprofile.jwt.tck.util.TokenUtils
class to generate both valid and
invalid tokens. A summary of the tck/resources directory contents is:
-
META-INF/microprofile-config-publickey.properties
-
A base MicroProfile config properties file for the org.eclipse.microprofile.jwt.tck.config.* package tests.
-
-
META-INF/microprofile-config-publickey-location.properties
-
A base MicroProfile config properties file for the org.eclipse.microprofile.jwt.tck.jaxrs.* package tests.
-
-
META-INF/microprofile-config-decryptionkey-location.properties
-
A base MicroProfile config properties file for the org.eclipse.microprofile.jwt.tck.jaxrs.jwe.* package tests.
-
-
jwt-content1.json
-
testJWTCallerPrincipal.json
-
usePreferredName.json
-
useSubject.json
-
Used by the
TokenUtilsTest
to validate the MP-JWT implementation under test JsonWebToken implementation.
-
-
Token1.json
-
Token2.json
-
Used by the various JAX-RS and CDI tests
-
-
privateKey.pem
-
The test issuer RSA private key used to sign the MP-JWT tokens generated by the org.eclipse.microprofile.jwt.tck.container.jaxrs.* package tests.
-
-
ecPrivateKey.pem
-
The test issuer EllipticCurve private key used to sign the MP-JWT tokens generated by the org.eclipse.microprofile.jwt.tck.container.jaxrs.* package tests.
-
-
privateKey4k.pem
-
The test issuer RSA private key used to sign the MP-JWT tokens generated by the org.eclipse.microprofile.jwt.tck.config.* package tests.
-
-
publicKey.pem
-
The test issuer RSA public key that MP-JWT implementations under test use to validate the token signature in the org.eclipse.microprofile.jwt.tck.container.jaxrs.* package tests.
-
-
ecPublicKey.pem
-
The test issuer RSA public key that MP-JWT implementations under test use to validate the token signature in the org.eclipse.microprofile.jwt.tck.container.jaxrs.* package tests.
-
-
publicKey4k.pem
-
The test issuer EllipticCurve public key that MP-JWT implementations under test use to validate the token signature in the org.eclipse.microprofile.jwt.tck.config.* package tests.
-
-
RequiredClaims.json
-
Used by the RequiredClaimsTest to generate a MP-JWT with the minimum required claims.
-
-
TokenBadIss.json
-
Used by the IssNoValidationBadIssTest to validate that the iss claim is ignored when validation is disabled.
-
-
signer-key4k.jwk
-
A JWK representation of the signer RSA public key used by some of the org.eclipse.microprofile.jwt.tck.config.* package tests.
-
-
signer-keyset4k.jwk
-
A JWKS representation of the signer RSA public key used by some of the org.eclipse.microprofile.jwt.tck.config.* package tests.
-
-
encryptorPublicKey.jwk
-
A JWK representation of the encryptor RSA public key used by some of the org.eclipse.microprofile.jwt.tck.config.jwe.* package tests.
-
-
decryptorPrivateKey.jwk
-
A JWK representation of the decryptor RSA private key used by some of the org.eclipse.microprofile.jwt.tck.config.jwe.* package tests.
-
-
* decryptorPrivateKeySet.jwk
-
A JWKS representation of the decryptor RSA private key used by some of the org.eclipse.microprofile.jwt.tck.config.jwe.* package tests.
-
The test issuer value used in all valid test MP-JWT tokens as the iss
claim value is "https://server.example.com".
The test issuer public key is the tck/resources/publicKey.pem file. It is included in every test WebArchive artifact as an archive classpath resource at the location /WEB-INF/classes/publicKey.pem.
The generated test JWT has its exp
, iat
and auth_time
claims are set to the current time when the token is generated,
as the number of seconds from 1970-01-01T00:00:00Z UTC.
An example of how this information can be used to verify a JWT using the Jose4j library used by the TCK to generate the test JWTs can be found in the org.eclipse.microprofile.jwt.tck.utils.TokenUtilsTest#validateToken method.
=Running the MicroProfile JWT Auth TCK
The TCK is designed around a set of Arquillian based unit tests that require
the MP-JWT implementation under test to provide a TCK harness
artifact that provides an org.jboss.arquillian.core.spi.LoadableExtension
that
installs a org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor
to augment the base org.jboss.shrinkwrap.api.spec.WebArchive
with the
implementation specific artifacts, descriptors, libraries, etc. needed for
the implementation to properly deploy the test web archive.
There are base implementations of the LoadableExtension
and ApplicationArchiveProcessor
that can be used for straight-forward augmentation scenarios, but you can always
provide your own implementations. An example of the former is:
https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck-viabase
while an example of the latter is: https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck
As described, you need to create an artifact that bundles a LoadableExtension using a Java ServiceProvider that installs an ApplicationArchiveProcessor that augments the base TCK test web application archive with the implementation specific configuration and dependencies needed to successfully deploy and test the web application with MP-JWT authentication enabled.
An example skeleton pom.xml is shown here:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.groupID</groupId>
<artifactId>jwt-auth-tck</artifactId>
<version>1.0-SNAPSHOT</version>
<name>MicroProfile JWT Auth TCK Harness MyCoolMP Implementation</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<version.wildfly.swarm>2017.7.0</version.wildfly.swarm>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.13.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- This is the MP-JWT TCK base extension and utility classes -->(1)
<dependency>
<groupId>org.eclipse.microprofile.jwt</groupId>
<artifactId>microprofile-jwt-auth-tck</artifactId>
<version>2.0</version>
</dependency>
<!-- This is the actual MP-JWT TCK test classes -->(2)
<dependency>
<groupId>org.eclipse.microprofile.jwt</groupId>
<artifactId>microprofile-jwt-auth-tck</artifactId>
<version>2.0</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<!-- Arquillian extension SPI -->(3)
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-container-spi</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-container-test-spi</artifactId>
</dependency>
<!-- You need to specify your JAX-RS client implementation as the unit
tests make use of that API, but do not specify the implementation.
-->(4)
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.1.1.Final</version>
</dependency>
<!-- Specify your container runtime arquillian integration and dependencies -->
<dependency>(5)
<groupId>MY_GROUP</groupId>
<artifactId>arquillian-container</artifactId>
<version>${container-version}</version>
</dependency>
...
</dependencies>
...
</project>
-
org.eclipse.microprofile.jwt:microprofile-jwt-auth-tck is the MP-JWT artifact that contains the base
LoadableExtension
andApplicationArchiveProcessor
classes andTokenUtils
class. -
org.eclipse.microprofile.jwt:microprofile-jwt-auth-tck type=test-jar is the MP-JWT TCK tests themselves you need to run for the TCK. You would only need this if you are running the TCK from within your TCK harness project.
-
The 2 indicated Arquillian extension SPI dependencies provide the
LoadableExtension
andApplicationArchiveProcessor
interfaces and dependent classes. -
The TCK unit tests make use of the JAX-RS client API, but does not provide an implementation, so your TCK harness artifact must specify what implementation to use. Here the Resteasy implementation is being specified.
-
Lastly, you must specify the property Arquillian container runtime that is approriate for you MP-JWT implementation, along with whatever container runtime dependencies are required.
The primary elements that need to be configured in the MP-JWT runtime are the security domain settings. This will entail the installation of the MP-JWT authentication mechanism, the token validation settings, and a group to role mapping.
Some tests in the "ee-security-optional" group require a mapping from a group name in the "groups" claim of the MP-JWT token to a custom role name used in an endpoint @RolesAllowed statement. The following table lists the group names and the role mappings that are expected in the TCK unit tests. This includes the required one-to-one mapping of the group name:
- Echoer
-
Echoer
- Tester
-
Tester
- Token2Role
-
Token2Role
- group1
-
group1, Group1MappedRole
An example of how a TCK harness implementation that is based on Glassfish might perform the group1 required mappings via is shown in the following glassfish-web.xml descriptor that the TCK harness would add to the test WebArchive in it’s ApplicationArchiveProcessor:
<glassfish-web-app>
...
<security-role-mapping>
<role-name>group1</role-name>
<group-name>group1</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>group1</role-name>
<group-name>Group1MappedRole</group-name>
</security-role-mapping>
...
</glassfish-web-app>
This section describes the equivalent web.xml style of security constraints that are expected for the various TCK deployments.
- ClaimValueInjectionTest
-
-
url-pattern: /endp/*
-
role-name: Echoer
-
role-name: Tester
-
- InvalidTokenTest
-
-
url-pattern: /endp/*
-
role-name: Echoer
-
- JsonValueInjectionTest
-
-
url-pattern: /endp/*
-
role-name: Echoer
-
role-name: Tester
-
- ProviderValueInjectionTest
-
-
url-pattern: /endp/*
-
role-name: Echoer
-
role-name: Tester
-
- RequiredClaimsEndpoint
-
-
url-pattern: /endp/*
-
role-name: Tester
-
- RolesAllowedTest
-
-
url-pattern: /endp/echo
-
role-name: Echoer
-
url-pattern: /endp/echo2
-
role-name: NoSuchUser - This role is not granted to any test token
-
url-pattern/endp/getPrincipalClass
-
url-pattern/endp/checkIsUserInRole
-
url-pattern/endp/getInjectedPrincipal
-
role-name: Echoer
-
url-pattern: /endp/needsGroup1Mapping
-
role-name: Group1MappedRole - This role needs to be mapped to the token group1 group
-
url-pattern/endp/echoNeedsToken2Role
-
role-name: Token2Role
-
- UnsecuredPingTest
-
No authentication required
The TCK harness ApplicationArchiveProcessor
implementation has access to information added to the archive during the deployment creation. Some of the key items are shown by this implementation fragment:
public class WFSwarmWarArchiveProcessor implements ApplicationArchiveProcessor {
private static Logger log = Logger.getLogger(WFSwarmWarArchiveProcessor.class.getName());
@Override
public void process(Archive<?> appArchive, TestClass testClass) {
if (!(appArchive instanceof WebArchive)) {
return;
}
WebArchive war = WebArchive.class.cast(appArchive);
Node configProps = war.get("/META-INF/microprofile-config.properties");(1)
Node publicKeyNode = war.get("/WEB-INF/classes/publicKey.pem");(2)
Node publicKey4kNode = war.get("/WEB-INF/classes/publicKey4k.pem");(3)
Node mpJWT = war.get("MP-JWT");(4)
Node testVersionNode = war.get(MpJwtTestVersion.VERSION_LOCATION);(5)
MpJwtTestVersion testVersion = MpJwtTestVersion.MPJWT_V_1_0;
if(testVersionNode != null) {
String content = readAsset(testVersionNode);
testVersion = MpJwtTestVersion.valueOf(content);
}
-
The optional microprofile-config.properties. Only the config related tests currently have this asset.
-
The optional public key content of the token signer.
-
The optional 4096 bit public key content of the token signer.
-
The optional base64 encoded string of the MP-JWT that will be passed by the test. Currently only the
Iss*Validation*
tests pass this in. -
A marker resource used to indicate the version of MP-JWT the test is targeting. It will be the string value of one of the MpJwtTestVersion enums. The absense of a marker should be treated as an MP-JWT 1.0 test as shown. For MpJwtTestVersion.MPJWT_V_1_0, there will be no bundled META-INF/microprofile-config.properties, and so your harness should set any vendor specific defaults such as the signer public key.
You can use this information to set vendor specific settings that are need to support proper operation of your MP-JWT implementation.
Once you have built and installed your TCK harness artifact, you can run the
TCK tests against it by using either the tokens-se
or container
profiles.
The container profile is a test of JAX-RS client tests that validate a JAX-RS endpoint bundled in a WebArchive deployment via your implementation. These tests require Arquillian container runtime integration to properly deploy and start your container. You typically provide this via a dependency on an arquillian container artificat, for example, Tomcat based containers might include a dependency like:
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-tomcat-embedded-7</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
This test of tests also require the org.jboss.arquillian.core.spi.LoadableExtension
and org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor
implementations as discussed above.
To run this set of tests, issue the following command from within the microprofile-jwt-auth/tck directory:
mvn -Pcontainer -Dtck.container.groupId={MY_GROUP} -Dtck.container.artifactId={MY_ARTIFACT} -Dtck.container.version={MY_VERSION} test
where you would replace the {MY_GROUP}
, {MY_ARTIFACT}
and {MY_VERSION}
with
the <groupId>…<groupId>
, <artifactId>…</artifactId>
, and <version>…</version>
respectively from your TCK harness artifact.
A concrete example is for running with the TCK harness artifiact from the https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck project is:
mvn -Pcontainer -Dtck.container.groupId=org.wildfly.swarm -Dtck.container.artifactId=jwt-auth-tck -Dtck.container.version=1.0-SNAPSHOT
You can run the TCK tests from within your TCK harness build by including the following in your pom.xml:
<dependencies>
...
<!-- Include the MP-JWT TCK dependencies, utility and base classes + actual test classes -->
<dependency>
<groupId>org.eclipse.microprofile.jwt</groupId>
<artifactId>microprofile-jwt-auth-tck</artifactId>
<version>2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.jwt</groupId>
<artifactId>microprofile-jwt-auth-tck</artifactId>
<version>2.0</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<!-- You need to add a dependency for a JAX-RS client implementation -->
<dependency>
<groupId>FIXME</groupId>
<artifactId>some-jaxrs-client-impl</artifactId>
<version>x.y</version>
<scope>test</scope>
</dependency>
<!-- Your additional container dependences... -->
</dependencies>
<build>
<plugins>
...
<!-- Run the TCK tests aginst the tck-base-suite.xml -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<suiteXmlFiles>
<suiteXmlFile>${project.build.directory}/tck-suite/suites/tck-base-suite.xml</suiteXmlFile>
</suiteXmlFiles>
<forkCount>1</forkCount>
</configuration>
</plugin>
<!-- Extract the TCK Suite Files -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>process-test-classes</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.microprofile.jwt</groupId>
<artifactId>microprofile-jwt-auth-tck</artifactId>
<version>2.0</version>
<type>test-jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/tck-suite</outputDirectory>
<!-- Includes the tck-base-suite.xml file -->
<includes>**/tck-build-suite.xml</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Alternatively, you can copy any of the TCK suite files present in src/test/resources/suites
to your project and
customize to fit your needs. If this is the case, you can remove the maven-dependency-plugin
section from the
previous XML fragment.
You then simply run mvn test
to run the TCK tests. An example of using this approach
can be found in the https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck repo.
Running
[wfswarm-jwt-auth-tck 664]$ mvn -Dswarm.resolver.offline=true test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building MicroProfile JWT Auth TCK Harness WFSwarm Implementation 1.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jwt-auth-tck ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 6 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ jwt-auth-tck ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jwt-auth-tck ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/starksm/Dev/JBoss/Microprofile/wfswarm-jwt-auth-tck/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ jwt-auth-tck ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.20:test (default-test) @ jwt-auth-tck ---
[INFO] No tests to run.
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running TestSuite
[INFO] Tests run: 116, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 469.176 s - in TestSuite
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 116, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 07:51 min
[INFO] Finished at: 2018-05-25T23:10:16-07:00
[INFO] Final Memory: 73M/909M
[INFO] ------------------------------------------------------------------------
There is one system property that may need to be set in order for the tests that attempt to load a public key from a JWKS URL:
-
mp.jwt.tck.jwks.baseURL : Set to the location of your container JAX-RS root. This defaults to "http://localhost:8080/", so it is only necessary to set this property if that default does not match your container’s default.
For example,
mvn -Pcontainer -Dmp.jwt.tck.jwks.baseURL=http://jwks-host:9090/ -Dtck.container.groupId=org.wildfly.swarm -Dtck.container.artifactId=jwt-auth-tck -Dtck.container.version=1.0-SNAPSHOT