Skip to content

Commit 3bac390

Browse files
committed
Add the mp.jwt.verify.requireiss and restore the mp.jwt.verify.issuer config options
Signed-off-by: Scott Stark <starksm64@gmail.com>
1 parent a88aa7f commit 3bac390

File tree

12 files changed

+686
-46
lines changed

12 files changed

+686
-46
lines changed

api/src/main/java/org/eclipse/microprofile/jwt/config/Names.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,21 @@ public interface Names {
3030
*/
3131
String VERIFIER_PUBLIC_KEY = "mp.jwt.verify.publickey";
3232

33-
/**
34-
* The expected iss claim value to validate against an MP-JWT. If not provided, there will be no
35-
* validation of the MP-JWT iss claim.
36-
*/
37-
String ISSUER = "mp.jwt.verify.issuer";
38-
3933
/**
4034
* The relative path or full URL of the public key. All relative paths will be resolved within the archive using
4135
* ClassLoader.getResource. If the value is a URL it will be resolved using `new URL(“”).openStream()`
4236
*/
4337
String VERIFIER_PUBLIC_KEY_LOCATION = "mp.jwt.verify.publickey.location";
38+
39+
/**
40+
* A boolean flag that indicates whether or not validation of the iss claim will be done. If true, the MP-JWT
41+
* MUST include an iss claim that matches the {@linkplain #ISSUER} value. If false, no validation of the
42+
* iss claim is performed.
43+
*/
44+
String REQUIRE_ISS = "mp.jwt.verify.requireiss";
45+
46+
/**
47+
* The expected iss claim value to validate against an MP-JWT.
48+
*/
49+
String ISSUER = "mp.jwt.verify.issuer";
4450
}

spec/src/main/asciidoc/configuration.asciidoc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,16 @@ public class Handler extends URLStreamHandler {
347347
}
348348
----
349349

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

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

355+
#### `mp.jwt.verify.requireiss`
356+
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
357+
any iss claim value, including none at all, is allowed, and validation of the `iss` claim MUST NOT be performed.
358+
359+
#### `mp.jwt.verify.issuer`
360+
361+
The `mp.jwt.verify.issuer` config property allows for the expected value of the `iss`
362+
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`.

spec/src/main/asciidoc/future-directions.asciidoc

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,6 @@ The "aud" claim defined in RFC 7519 section 4.1.3 was considered for addition.
5656
Though a "aud" claim is not required, implementations that support it and applications that use it should do so as
5757
detailed in this section to ensure alignment for any future standardization.
5858

59-
### `mp.jwt.verify.issuer` configuration option
60-
61-
Discussion of a standard configuration option for enabling the explicit checking of the
62-
issuer was discussed. Discussion is still ongoing as to what the default behavior of
63-
the property should be if no explicit value is supplied. The definition as last phrased
64-
is below.
65-
66-
The `mp.jwt.verify.issuer` config property allows for the expected value of the `iss`
67-
claim to be optionally specified. When specified, the MicroProfile JWT implementation
68-
must verify the `iss` claim of incoming JWTs is present and matches the configured value
69-
of `mp.jwt.verify.issuer`.
70-
71-
If the `mp.jwt.verify.issuer` config property has not been set, any issuer or none at all
72-
is allowed.
73-
74-
NOTE: In most cases relying on the digital signature check via the Public Key alone is
75-
sufficient to establish trust.
76-
7759
### `classpath:` URL Scheme
7860

7961
The option to have a built-in `classpath:` URL Scheme was discussed with the intended

tck/README.adoc

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ invalid tokens. A summary of the tck/resources directory contents is:
6767
** 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.
6868
* RequiredClaims.json
6969
** Used by the RequiredClaimsTest to generate a MP-JWT with the minimum required claims.
70+
* TokenBadIss.json
71+
** Used by the IssNoValidationBadIssTest to validate that the iss claim is
72+
ignored when validation is disabled.
7073
* signer-key4k.jwk
7174
** A JWK representation of the signer public key used by some of the org.eclipse.microprofile.jwt.tck.config.* package tests.
7275
* signer-keyset4k.jwk
@@ -312,7 +315,7 @@ public class WFSwarmWarArchiveProcessor implements ApplicationArchiveProcessor {
312315
<1> The optional microprofile-config.properties. Only the config related tests currently have this asset.
313316
<2> The optional public key content of the token signer.
314317
<3> The optional 4096 bit public key content of the token signer.
315-
<4> The optional base64 encoded string of the MP-JWT that will be passed by the test. Currently only the `OptionalIssTest` passes this in.
318+
<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.
316319

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

@@ -454,7 +457,12 @@ your build root:
454457
<class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsJWKSLocationTest" />
455458
<class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsBase64JWKTest" />
456459
<class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsFileLocationURLTest" />
457-
<class name="org.eclipse.microprofile.jwt.tck.config.OptionalIssTest" />
460+
<class name="org.eclipse.microprofile.jwt.tck.config.IssNoValidationNoIssTest" />
461+
<class name="org.eclipse.microprofile.jwt.tck.config.IssNoValidationBadIssTest" />
462+
<class name="org.eclipse.microprofile.jwt.tck.config.IssValidationTest" />
463+
<class name="org.eclipse.microprofile.jwt.tck.config.IssValidationDefaultTest" />
464+
<class name="org.eclipse.microprofile.jwt.tck.config.IssValidationFailTest" />
465+
458466
</classes>
459467
</test>
460468

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

468476
```bash
469-
[wfswarm-jwt-auth-tck 1316]$ mvn -Dswarm.resolver.offline=true test
477+
[wfswarm-jwt-auth-tck 664]$ mvn -Dswarm.resolver.offline=true test
470478
[INFO] Scanning for projects...
471479
[INFO]
472480
[INFO] ------------------------------------------------------------------------
473-
[INFO] Building MicroProfile JWT Auth TCK Harness WFSwarm Implementation 1.0-SNAPSHOT
481+
[INFO] Building MicroProfile JWT Auth TCK Harness WFSwarm Implementation 1.1-SNAPSHOT
474482
[INFO] ------------------------------------------------------------------------
475483
[INFO]
476484
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jwt-auth-tck ---
@@ -494,17 +502,17 @@ Running
494502
[INFO] T E S T S
495503
[INFO] -------------------------------------------------------
496504
[INFO] Running TestSuite
497-
[INFO] Tests run: 19, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 49.95 s - in TestSuite
505+
[INFO] Tests run: 116, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 469.176 s - in TestSuite
498506
[INFO]
499507
[INFO] Results:
500508
[INFO]
501-
[INFO] Tests run: 19, Failures: 0, Errors: 0, Skipped: 0
509+
[INFO] Tests run: 116, Failures: 0, Errors: 0, Skipped: 0
502510
[INFO]
503511
[INFO] ------------------------------------------------------------------------
504512
[INFO] BUILD SUCCESS
505513
[INFO] ------------------------------------------------------------------------
506-
[INFO] Total time: 52.805 s
507-
[INFO] Finished at: 2017-08-23T17:23:41-07:00
508-
[INFO] Final Memory: 30M/619M
514+
[INFO] Total time: 07:51 min
515+
[INFO] Finished at: 2018-05-25T23:10:16-07:00
516+
[INFO] Final Memory: 73M/909M
509517
[INFO] ------------------------------------------------------------------------
510518
```
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2016-2018 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* You may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
package org.eclipse.microprofile.jwt.tck.config;
21+
22+
import java.io.StringReader;
23+
import java.io.StringWriter;
24+
import java.net.HttpURLConnection;
25+
import java.net.URL;
26+
import java.security.PrivateKey;
27+
import java.util.HashMap;
28+
import java.util.Properties;
29+
30+
import javax.json.Json;
31+
import javax.json.JsonObject;
32+
import javax.json.JsonReader;
33+
import javax.ws.rs.client.ClientBuilder;
34+
import javax.ws.rs.client.WebTarget;
35+
import javax.ws.rs.core.HttpHeaders;
36+
import javax.ws.rs.core.Response;
37+
38+
import org.eclipse.microprofile.jwt.config.Names;
39+
import org.eclipse.microprofile.jwt.tck.container.jaxrs.TCKApplication;
40+
import org.eclipse.microprofile.jwt.tck.util.TokenUtils;
41+
import org.jboss.arquillian.container.test.api.Deployment;
42+
import org.jboss.arquillian.container.test.api.RunAsClient;
43+
import org.jboss.arquillian.test.api.ArquillianResource;
44+
import org.jboss.arquillian.testng.Arquillian;
45+
import org.jboss.shrinkwrap.api.ShrinkWrap;
46+
import org.jboss.shrinkwrap.api.asset.StringAsset;
47+
import org.jboss.shrinkwrap.api.spec.WebArchive;
48+
import org.testng.Assert;
49+
import org.testng.Reporter;
50+
import org.testng.annotations.Test;
51+
52+
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
53+
import static org.eclipse.microprofile.jwt.tck.TCKConstants.TEST_GROUP_CONFIG;
54+
55+
/**
56+
* Validate the handling of the JWT iss claim.
57+
*
58+
* Validate that if there is a {@linkplain Names#REQUIRE_ISS} property set to false, validation of
59+
* the iss claim is not performed, and {@linkplain Names#ISSUER} property is ignored.
60+
*/
61+
public class IssNoValidationBadIssTest extends Arquillian {
62+
/**
63+
* The base URL for the container under test
64+
*/
65+
@ArquillianResource
66+
private URL baseURL;
67+
68+
/**
69+
* The token used by the test
70+
*/
71+
private static String token;
72+
73+
/**
74+
* Create a CDI aware base web application archive that includes an embedded PEM public key
75+
* that is included as the mp.jwt.verify.publickey property.
76+
* The root url is /
77+
* @return the base base web application archive
78+
* @throws Exception - on resource failure
79+
*/
80+
@Deployment()
81+
public static WebArchive createDeployment() throws Exception {
82+
URL publicKey = PublicKeyAsPEMTest.class.getResource("/publicKey4k.pem");
83+
84+
PrivateKey privateKey = TokenUtils.readPrivateKey("/privateKey4k.pem");
85+
String kid = "publicKey4k";
86+
HashMap<String, Long> timeClaims = new HashMap<>();
87+
token = TokenUtils.generateTokenString(privateKey, kid, "/TokenBadIss.json", null, timeClaims);
88+
89+
// Setup the microprofile-config.properties content
90+
Properties configProps = new Properties();
91+
// Location points to the PEM bundled in the deployment
92+
configProps.setProperty(Names.VERIFIER_PUBLIC_KEY_LOCATION, "/publicKey4k.pem");
93+
// Don't require validation of iss claim
94+
configProps.setProperty(Names.REQUIRE_ISS, "false");
95+
// The issuer config value should be ignored
96+
configProps.setProperty(Names.ISSUER, "https://ignore-me");
97+
StringWriter configSW = new StringWriter();
98+
configProps.store(configSW, "IssNoValidationBadIssTest microprofile-config.properties");
99+
StringAsset configAsset = new StringAsset(configSW.toString());
100+
101+
WebArchive webArchive = ShrinkWrap
102+
.create(WebArchive.class, "IssNoValidationBadIssTest.war")
103+
.addAsResource(publicKey, "/publicKey.pem")
104+
.addAsResource(publicKey, "/publicKey4k.pem")
105+
// Include the token for inspection by ApplicationArchiveProcessor
106+
.add(new StringAsset(token), "MP-JWT")
107+
.addClass(PublicKeyEndpoint.class)
108+
.addClass(TCKApplication.class)
109+
.addClass(SimpleTokenUtils.class)
110+
.addAsWebInfResource("beans.xml", "beans.xml")
111+
.addAsManifestResource(configAsset, "microprofile-config.properties")
112+
;
113+
System.out.printf("WebArchive: %s\n", webArchive.toString(true));
114+
return webArchive;
115+
}
116+
117+
@RunAsClient
118+
@Test(groups = TEST_GROUP_CONFIG,
119+
description = "Validate that JWK with iss and mp.jwt.verify.requireiss=false returns HTTP_OK")
120+
public void testNotRequiredIssIgnored() throws Exception {
121+
Reporter.log("testNotRequiredIssIgnored, expect HTTP_OK");
122+
123+
String uri = baseURL.toExternalForm() + "endp/verifyBadIssIsOk";
124+
WebTarget echoEndpointTarget = ClientBuilder.newClient()
125+
.target(uri)
126+
;
127+
Response response = echoEndpointTarget.request(APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, "Bearer "+token).get();
128+
Assert.assertEquals(response.getStatus(), HttpURLConnection.HTTP_OK);
129+
String replyString = response.readEntity(String.class);
130+
JsonReader jsonReader = Json.createReader(new StringReader(replyString));
131+
JsonObject reply = jsonReader.readObject();
132+
Reporter.log(reply.toString());
133+
Assert.assertTrue(reply.getBoolean("pass"), reply.getString("msg"));
134+
}
135+
136+
}

tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/OptionalIssTest.java renamed to tck/src/test/java/org/eclipse/microprofile/jwt/tck/config/IssNoValidationNoIssTest.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@
5353
import static org.eclipse.microprofile.jwt.tck.TCKConstants.TEST_GROUP_CONFIG;
5454

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

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

111115
@RunAsClient
112116
@Test(groups = TEST_GROUP_CONFIG,
113-
description = "Validate that JWK without iss is accepted if there is no mp.jwt.verify.issuer config")
114-
public void testMissingIssIsOk() throws Exception {
115-
Reporter.log("testMissingIssIsOk, expect HTTP_OK");
117+
description = "Validate that JWK without iss and mp.jwt.verify.requireiss=false returns HTTP_OK")
118+
public void testNotRequiredIssMissingIgnored() throws Exception {
119+
Reporter.log("testNotRequiredIssMissingIgnored, expect HTTP_OK");
116120

117121
String uri = baseURL.toExternalForm() + "endp/verifyMissingIssIsOk";
118122
WebTarget echoEndpointTarget = ClientBuilder.newClient()

0 commit comments

Comments
 (0)