Skip to content

Commit

Permalink
Add the mp.jwt.verify.requireiss and restore the mp.jwt.verify.issuer…
Browse files Browse the repository at this point in the history
… config options

Signed-off-by: Scott Stark <[email protected]>
  • Loading branch information
starksm64 committed May 26, 2018
1 parent a88aa7f commit 3bac390
Show file tree
Hide file tree
Showing 12 changed files with 686 additions and 46 deletions.
18 changes: 12 additions & 6 deletions api/src/main/java/org/eclipse/microprofile/jwt/config/Names.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,21 @@ public interface Names {
*/
String VERIFIER_PUBLIC_KEY = "mp.jwt.verify.publickey";

/**
* The expected iss claim value to validate against an MP-JWT. If not provided, there will be no
* validation of the MP-JWT iss claim.
*/
String ISSUER = "mp.jwt.verify.issuer";

/**
* The relative path or full URL of the public key. All relative paths will be resolved within the archive using
* ClassLoader.getResource. If the value is a URL it will be resolved using `new URL(“”).openStream()`
*/
String VERIFIER_PUBLIC_KEY_LOCATION = "mp.jwt.verify.publickey.location";

/**
* A boolean flag that indicates whether or not validation of the iss claim will be done. If true, the MP-JWT
* MUST include an iss claim that matches the {@linkplain #ISSUER} value. If false, no validation of the
* iss claim is performed.
*/
String REQUIRE_ISS = "mp.jwt.verify.requireiss";

/**
* The expected iss claim value to validate against an MP-JWT.
*/
String ISSUER = "mp.jwt.verify.issuer";
}
11 changes: 9 additions & 2 deletions spec/src/main/asciidoc/configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,16 @@ public class Handler extends URLStreamHandler {
}
----

See https://docs.oracle.com/javase/8/docs/api/java/net/URL.html[java.net.URL] javadoc for
more details.
See https://docs.oracle.com/javase/8/docs/api/java/net/URL.html[java.net.URL] javadoc for more details.

Parsing of the `InputStream` occurs as defined in <<Supported Public Key Formats>> and must
return Public Key text in one of the supported formats.

#### `mp.jwt.verify.requireiss`
The `mp.jwt.verify.requireiss` config property is a boolean flag that indicates whether `iss` claim values are required in the MP-JWT tokens. When true (the assumed default), MP-JWT tokens are required to have an `iss` claim value that will be validated against the `mp.jwt.verify.issuer` config property. If false, then
any iss claim value, including none at all, is allowed, and validation of the `iss` claim MUST NOT be performed.

#### `mp.jwt.verify.issuer`

The `mp.jwt.verify.issuer` config property allows for the expected value of the `iss`
claim to be specified. When iss validation is enabled, the MicroProfile JWT implementation must verify the `iss` claim of incoming JWTs is present and matches the configured value of `mp.jwt.verify.issuer`.
18 changes: 0 additions & 18 deletions spec/src/main/asciidoc/future-directions.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,6 @@ The "aud" claim defined in RFC 7519 section 4.1.3 was considered for addition.
Though a "aud" claim is not required, implementations that support it and applications that use it should do so as
detailed in this section to ensure alignment for any future standardization.

### `mp.jwt.verify.issuer` configuration option

Discussion of a standard configuration option for enabling the explicit checking of the
issuer was discussed. Discussion is still ongoing as to what the default behavior of
the property should be if no explicit value is supplied. The definition as last phrased
is below.

The `mp.jwt.verify.issuer` config property allows for the expected value of the `iss`
claim to be optionally specified. When specified, the MicroProfile JWT implementation
must verify the `iss` claim of incoming JWTs is present and matches the configured value
of `mp.jwt.verify.issuer`.

If the `mp.jwt.verify.issuer` config property has not been set, any issuer or none at all
is allowed.

NOTE: In most cases relying on the digital signature check via the Public Key alone is
sufficient to establish trust.

### `classpath:` URL Scheme

The option to have a built-in `classpath:` URL Scheme was discussed with the intended
Expand Down
26 changes: 17 additions & 9 deletions tck/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ invalid tokens. A summary of the tck/resources directory contents is:
** The test issuer 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 public key used by some of the org.eclipse.microprofile.jwt.tck.config.* package tests.
* signer-keyset4k.jwk
Expand Down Expand Up @@ -312,7 +315,7 @@ public class WFSwarmWarArchiveProcessor implements ApplicationArchiveProcessor {
<1> The optional microprofile-config.properties. Only the config related tests currently have this asset.
<2> The optional public key content of the token signer.
<3> The optional 4096 bit public key content of the token signer.
<4> The optional base64 encoded string of the MP-JWT that will be passed by the test. Currently only the `OptionalIssTest` passes this in.
<4> 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.

You can use this information to set vendor specific settings that are need to support proper operation of your MP-JWT implementation.

Expand Down Expand Up @@ -454,7 +457,12 @@ your build root:
<class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsJWKSLocationTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsBase64JWKTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsFileLocationURLTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.OptionalIssTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.IssNoValidationNoIssTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.IssNoValidationBadIssTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.IssValidationTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.IssValidationDefaultTest" />
<class name="org.eclipse.microprofile.jwt.tck.config.IssValidationFailTest" />

</classes>
</test>

Expand All @@ -466,11 +474,11 @@ can be found in the https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck repo
Running

```bash
[wfswarm-jwt-auth-tck 1316]$ mvn -Dswarm.resolver.offline=true test
[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.0-SNAPSHOT
[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 ---
Expand All @@ -494,17 +502,17 @@ Running
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running TestSuite
[INFO] Tests run: 19, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 49.95 s - in 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: 19, Failures: 0, Errors: 0, Skipped: 0
[INFO] Tests run: 116, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 52.805 s
[INFO] Finished at: 2017-08-23T17:23:41-07:00
[INFO] Final Memory: 30M/619M
[INFO] Total time: 07:51 min
[INFO] Finished at: 2018-05-25T23:10:16-07:00
[INFO] Final Memory: 73M/909M
[INFO] ------------------------------------------------------------------------
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2016-2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* 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.
*
*/
package org.eclipse.microprofile.jwt.tck.config;

import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Properties;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

import org.eclipse.microprofile.jwt.config.Names;
import org.eclipse.microprofile.jwt.tck.container.jaxrs.TCKApplication;
import org.eclipse.microprofile.jwt.tck.util.TokenUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.testng.Assert;
import org.testng.Reporter;
import org.testng.annotations.Test;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.microprofile.jwt.tck.TCKConstants.TEST_GROUP_CONFIG;

/**
* Validate the handling of the JWT iss claim.
*
* Validate that if there is a {@linkplain Names#REQUIRE_ISS} property set to false, validation of
* the iss claim is not performed, and {@linkplain Names#ISSUER} property is ignored.
*/
public class IssNoValidationBadIssTest extends Arquillian {
/**
* The base URL for the container under test
*/
@ArquillianResource
private URL baseURL;

/**
* The token used by the test
*/
private static String token;

/**
* Create a CDI aware base web application archive that includes an embedded PEM public key
* that is included as the mp.jwt.verify.publickey property.
* The root url is /
* @return the base base web application archive
* @throws Exception - on resource failure
*/
@Deployment()
public static WebArchive createDeployment() throws Exception {
URL publicKey = PublicKeyAsPEMTest.class.getResource("/publicKey4k.pem");

PrivateKey privateKey = TokenUtils.readPrivateKey("/privateKey4k.pem");
String kid = "publicKey4k";
HashMap<String, Long> timeClaims = new HashMap<>();
token = TokenUtils.generateTokenString(privateKey, kid, "/TokenBadIss.json", null, timeClaims);

// Setup the microprofile-config.properties content
Properties configProps = new Properties();
// Location points to the PEM bundled in the deployment
configProps.setProperty(Names.VERIFIER_PUBLIC_KEY_LOCATION, "/publicKey4k.pem");
// Don't require validation of iss claim
configProps.setProperty(Names.REQUIRE_ISS, "false");
// The issuer config value should be ignored
configProps.setProperty(Names.ISSUER, "https://ignore-me");
StringWriter configSW = new StringWriter();
configProps.store(configSW, "IssNoValidationBadIssTest microprofile-config.properties");
StringAsset configAsset = new StringAsset(configSW.toString());

WebArchive webArchive = ShrinkWrap
.create(WebArchive.class, "IssNoValidationBadIssTest.war")
.addAsResource(publicKey, "/publicKey.pem")
.addAsResource(publicKey, "/publicKey4k.pem")
// Include the token for inspection by ApplicationArchiveProcessor
.add(new StringAsset(token), "MP-JWT")
.addClass(PublicKeyEndpoint.class)
.addClass(TCKApplication.class)
.addClass(SimpleTokenUtils.class)
.addAsWebInfResource("beans.xml", "beans.xml")
.addAsManifestResource(configAsset, "microprofile-config.properties")
;
System.out.printf("WebArchive: %s\n", webArchive.toString(true));
return webArchive;
}

@RunAsClient
@Test(groups = TEST_GROUP_CONFIG,
description = "Validate that JWK with iss and mp.jwt.verify.requireiss=false returns HTTP_OK")
public void testNotRequiredIssIgnored() throws Exception {
Reporter.log("testNotRequiredIssIgnored, expect HTTP_OK");

String uri = baseURL.toExternalForm() + "endp/verifyBadIssIsOk";
WebTarget echoEndpointTarget = ClientBuilder.newClient()
.target(uri)
;
Response response = echoEndpointTarget.request(APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, "Bearer "+token).get();
Assert.assertEquals(response.getStatus(), HttpURLConnection.HTTP_OK);
String replyString = response.readEntity(String.class);
JsonReader jsonReader = Json.createReader(new StringReader(replyString));
JsonObject reply = jsonReader.readObject();
Reporter.log(reply.toString());
Assert.assertTrue(reply.getBoolean("pass"), reply.getString("msg"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@
import static org.eclipse.microprofile.jwt.tck.TCKConstants.TEST_GROUP_CONFIG;

/**
* Validate that a JWT that has not iss claim, and that has no mp.jwt.verify.issuer config
* property is accepted for authentication and authorization
* Validate the handling of the JWT iss claim.
*
* Validate that if there is a {@linkplain Names#REQUIRE_ISS} property set to false, validation of
* the iss claim is not performed even if missing, and {@linkplain Names#ISSUER} property is ignored.
*/
public class OptionalIssTest extends Arquillian {
public class IssNoValidationNoIssTest extends Arquillian {
/**
* The base URL for the container under test
*/
Expand Down Expand Up @@ -88,12 +90,14 @@ public static WebArchive createDeployment() throws Exception {
Properties configProps = new Properties();
// Location points to the PEM bundled in the deployment
configProps.setProperty(Names.VERIFIER_PUBLIC_KEY_LOCATION, "/publicKey4k.pem");
// Don't require validation of iss claim
configProps.setProperty(Names.REQUIRE_ISS, "false");
StringWriter configSW = new StringWriter();
configProps.store(configSW, "OptionalIssTest microprofile-config.properties");
configProps.store(configSW, "IssNoValidationNoIssTest microprofile-config.properties");
StringAsset configAsset = new StringAsset(configSW.toString());

WebArchive webArchive = ShrinkWrap
.create(WebArchive.class, "OptionalIssTest.war")
.create(WebArchive.class, "IssNoValidationNoIssTest.war")
.addAsResource(publicKey, "/publicKey.pem")
.addAsResource(publicKey, "/publicKey4k.pem")
// Include the token for inspection by ApplicationArchiveProcessor
Expand All @@ -110,9 +114,9 @@ public static WebArchive createDeployment() throws Exception {

@RunAsClient
@Test(groups = TEST_GROUP_CONFIG,
description = "Validate that JWK without iss is accepted if there is no mp.jwt.verify.issuer config")
public void testMissingIssIsOk() throws Exception {
Reporter.log("testMissingIssIsOk, expect HTTP_OK");
description = "Validate that JWK without iss and mp.jwt.verify.requireiss=false returns HTTP_OK")
public void testNotRequiredIssMissingIgnored() throws Exception {
Reporter.log("testNotRequiredIssMissingIgnored, expect HTTP_OK");

String uri = baseURL.toExternalForm() + "endp/verifyMissingIssIsOk";
WebTarget echoEndpointTarget = ClientBuilder.newClient()
Expand Down
Loading

0 comments on commit 3bac390

Please sign in to comment.