From a8e092a57deafe0b85f328bc36091920d8b4f405 Mon Sep 17 00:00:00 2001 From: AlanRoth Date: Wed, 12 Jun 2019 13:28:11 +0100 Subject: [PATCH 1/9] Moved TokenParser class from MP TCK to Payara Server --- .../microprofile/jwtauth/tck/TokenParser.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/TokenParser.java diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/TokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/TokenParser.java new file mode 100644 index 00000000000..006568fff3b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/TokenParser.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017-2019 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.jwtauth.tck; + +import java.security.PublicKey; +import org.eclipse.microprofile.jwt.JsonWebToken; +import fish.payara.microprofile.jwtauth.jwt.JwtTokenParser; + +/** + * + * * This implements the artefact mandated by the MP-JWT TCK for offline + * (outside container) testing + * of the token parser. + * + * @author Arjan Tijms + * + */ +public class TokenParser { + + private final JwtTokenParser jwtTokenParser = new JwtTokenParser(); + + public JsonWebToken parse(String bearerToken, String issuer, PublicKey signedBy) throws Exception { + try { + jwtTokenParser.parse(bearerToken); + return jwtTokenParser.verify(issuer, signedBy); + } catch (Exception e) { + throw new IllegalStateException("", e); + } + } + +} + From 79545358c323fb694f133feb888ee940525012e6 Mon Sep 17 00:00:00 2001 From: AlanRoth Date: Wed, 12 Jun 2019 16:25:12 +0100 Subject: [PATCH 2/9] Moved TokenParser and made slight changes to compatability --- .../eesecurity/SignedJWTIdentityStore.java | 8 ++------ .../jwtauth/jwt/JwtTokenParser.java | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java index 5c5a18c2a1b..9fab511f4ce 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java @@ -113,12 +113,8 @@ public CredentialValidationResult validate(SignedJWTCredential signedJWTCredenti throw new IllegalStateException("No PublicKey found"); } - JsonWebTokenImpl jsonWebToken - = jwtTokenParser.parse( - signedJWTCredential.getSignedJWT(), - acceptedIssuer, - publicKey.get() - ); + jwtTokenParser.parse(signedJWTCredential.getSignedJWT()); + JsonWebTokenImpl jsonWebToken = jwtTokenParser.verify(acceptedIssuer, publicKey.get()); List groups = new ArrayList<>( jsonWebToken.getClaim("groups")); diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java index 08919d0f7d9..576c4bf394b 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2017-2018 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2017-2019 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -72,6 +72,9 @@ public class JwtTokenParser { private final boolean enableNamespacedClaims; private final Optional customNamespace; + private String rawToken; + private SignedJWT signedJWT; + public JwtTokenParser(Optional enableNamespacedClaims, Optional customNamespace) { this.enableNamespacedClaims = enableNamespacedClaims.orElse(false); this.customNamespace = customNamespace; @@ -81,8 +84,17 @@ public JwtTokenParser() { this(Optional.empty(), Optional.empty()); } - public JsonWebTokenImpl parse(String bearerToken, String issuer, PublicKey publicKey) throws Exception { - SignedJWT signedJWT = SignedJWT.parse(bearerToken); + public void parse(String bearerToken) throws Exception { + rawToken = bearerToken; + signedJWT = SignedJWT.parse(rawToken); + + if(!checkIsJWT(signedJWT.getHeader())){ + throw new IllegalStateException("Not JWT"); + } + } + + public JsonWebTokenImpl verify(String issuer, PublicKey publicKey) throws Exception { + SignedJWT signedJWT = SignedJWT.parse(rawToken); // MP-JWT 1.0 4.1 typ if (!checkIsJWT(signedJWT.getHeader())) { @@ -127,7 +139,7 @@ public JsonWebTokenImpl parse(String bearerToken, String issuer, PublicKey publi rawClaims.put( raw_token.name(), - createObjectBuilder().add("token", bearerToken).build().get("token")); + createObjectBuilder().add("token", rawToken).build().get("token")); return new JsonWebTokenImpl(callerPrincipalName, rawClaims); } From 4f5708c1a4f206da34e8d28cab1af1a970eb7c3f Mon Sep 17 00:00:00 2001 From: Alan Date: Mon, 17 Jun 2019 10:58:16 +0100 Subject: [PATCH 3/9] License date --- .../microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java index 9fab511f4ce..42159180174 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/eesecurity/SignedJWTIdentityStore.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2017-2018 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2017-2019 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development From c930adcfb5d0196442685f9441f952643f9cea14 Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 19 Jun 2019 08:10:25 +0100 Subject: [PATCH 4/9] Added null check --- .../fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java index 576c4bf394b..27bfb3710c9 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java @@ -94,7 +94,9 @@ public void parse(String bearerToken) throws Exception { } public JsonWebTokenImpl verify(String issuer, PublicKey publicKey) throws Exception { - SignedJWT signedJWT = SignedJWT.parse(rawToken); + if(signedJWT == null){ + signedJWT = parse(rawToken); + } // MP-JWT 1.0 4.1 typ if (!checkIsJWT(signedJWT.getHeader())) { From c901b4729dadd29969920989be3874699ab1b2d2 Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 19 Jun 2019 08:30:41 +0100 Subject: [PATCH 5/9] Fixed null check --- .../fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java index 27bfb3710c9..feec45eb75b 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java @@ -95,7 +95,7 @@ public void parse(String bearerToken) throws Exception { public JsonWebTokenImpl verify(String issuer, PublicKey publicKey) throws Exception { if(signedJWT == null){ - signedJWT = parse(rawToken); + parse(rawToken); } // MP-JWT 1.0 4.1 typ From c19658a2624e866099b1bbe165af96d074718ffe Mon Sep 17 00:00:00 2001 From: Alan Roth Date: Wed, 3 Jul 2019 13:27:17 +0100 Subject: [PATCH 6/9] Added MockPrefix --- .../jwtauth/tck/{TokenParser.java => MockTokenParser.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/{TokenParser.java => MockTokenParser.java} (100%) diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/TokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java similarity index 100% rename from appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/TokenParser.java rename to appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java From a1c9d8e72217cdb60460c76ebcf6e4e9d9a16eb8 Mon Sep 17 00:00:00 2001 From: Alan Roth Date: Wed, 3 Jul 2019 13:31:16 +0100 Subject: [PATCH 7/9] Added Mock JWT parser --- .../jwtauth/tck/MockJwtTokenParser.java | 190 ++++++++++++++++++ .../jwtauth/tck/MockTokenParser.java | 4 +- 2 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java new file mode 100644 index 00000000000..f15ade329db --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java @@ -0,0 +1,190 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package fish.payara.microprofile.jwtauth.tck; + +import static com.nimbusds.jose.JWSAlgorithm.RS256; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jwt.SignedJWT; +import fish.payara.microprofile.jwtauth.jwt.JsonWebTokenImpl; +import java.io.StringReader; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import static java.util.Arrays.asList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import javax.json.Json; +import static javax.json.Json.createObjectBuilder; +import javax.json.JsonNumber; +import javax.json.JsonReader; +import javax.json.JsonString; +import javax.json.JsonValue; +import org.eclipse.microprofile.jwt.Claims; +import static org.eclipse.microprofile.jwt.Claims.exp; +import static org.eclipse.microprofile.jwt.Claims.groups; +import static org.eclipse.microprofile.jwt.Claims.iat; +import static org.eclipse.microprofile.jwt.Claims.iss; +import static org.eclipse.microprofile.jwt.Claims.jti; +import static org.eclipse.microprofile.jwt.Claims.preferred_username; +import static org.eclipse.microprofile.jwt.Claims.raw_token; +import static org.eclipse.microprofile.jwt.Claims.sub; +import static org.eclipse.microprofile.jwt.Claims.upn; + +/** + * + * @author AlanRoth + */ +public class MockJwtTokenParser { + private final static String DEFAULT_NAMESPACE = "https://payara.fish/mp-jwt/"; + + private final List requiredClaims = asList(iss, sub, exp, iat, jti, groups); + + private final boolean enableNamespacedClaims; + private final Optional customNamespace; + + private String rawToken; + private SignedJWT signedJWT; + + public MockJwtTokenParser(Optional enableNamespacedClaims, Optional customNamespace) { + this.enableNamespacedClaims = enableNamespacedClaims.orElse(false); + this.customNamespace = customNamespace; + } + + public MockJwtTokenParser() { + this(Optional.empty(), Optional.empty()); + } + + public void parse(String bearerToken) throws Exception { + rawToken = bearerToken; + signedJWT = SignedJWT.parse(rawToken); + + if(!checkIsJWT(signedJWT.getHeader())){ + throw new IllegalStateException("Not JWT"); + } + } + + public JsonWebTokenImpl verify(String issuer, PublicKey publicKey) throws Exception { + if(signedJWT == null){ + parse(rawToken); + } + + // MP-JWT 1.0 4.1 typ + if (!checkIsJWT(signedJWT.getHeader())) { + throw new IllegalStateException("Not JWT"); + } + + // 1.0 4.1 alg + MP-JWT 1.0 6.1 1 + if (!signedJWT.getHeader().getAlgorithm().equals(RS256)) { + throw new IllegalStateException("Not RS256"); + } + + try (JsonReader reader = Json.createReader(new StringReader(signedJWT.getPayload().toString()))) { + Map rawClaims = new HashMap<>(reader.readObject()); + + // Vendor - Process namespaced claims + rawClaims = handleNamespacedClaims(rawClaims); + + // MP-JWT 1.0 4.1 Minimum MP-JWT Required Claims + if (!checkRequiredClaimsPresent(rawClaims)) { + throw new IllegalStateException("Not all required claims present"); + } + + // MP-JWT 1.0 4.1 upn - has fallbacks + String callerPrincipalName = getCallerPrincipalName(rawClaims); + if (callerPrincipalName == null) { + throw new IllegalStateException("One of upn, preferred_username or sub is required to be non null"); + } + + // MP-JWT 1.0 6.1 2 + if (!checkIssuer(rawClaims, issuer)) { + throw new IllegalStateException("Bad issuer"); + } + + if (!checkNotExpired(rawClaims)) { + throw new IllegalStateException("Expired"); + } + + // MP-JWT 1.0 6.1 2 + if (!signedJWT.verify(new RSASSAVerifier((RSAPublicKey) publicKey))) { + throw new IllegalStateException("Signature invalid"); + } + + rawClaims.put( + raw_token.name(), + createObjectBuilder().add("token", rawToken).build().get("token")); + + return new JsonWebTokenImpl(callerPrincipalName, rawClaims); + } + } + + private Map handleNamespacedClaims(Map currentClaims){ + if(this.enableNamespacedClaims){ + final String namespace = customNamespace.orElse(DEFAULT_NAMESPACE); + Map processedClaims = new HashMap<>(currentClaims.size()); + for(String claimName : currentClaims.keySet()){ + JsonValue value = currentClaims.get(claimName); + if(claimName.startsWith(namespace)){ + claimName = claimName.substring(namespace.length()); + } + processedClaims.put(claimName, value); + } + return processedClaims; + }else{ + return currentClaims; + } + } + + private boolean checkRequiredClaimsPresent(Map presentedClaims) { + for (Claims requiredClaim : requiredClaims) { + if (presentedClaims.get(requiredClaim.name()) == null) { + return false; + } + } + return true; + } + + private boolean checkNotExpired(Map presentedClaims) { + int currentTime = (int) (System.currentTimeMillis() / 1000); + int expiredTime = ((JsonNumber) presentedClaims.get(exp.name())).intValue(); + + return currentTime < expiredTime; + } + + private boolean checkIssuer(Map presentedClaims, String acceptedIssuer) { + if (!(presentedClaims.get(iss.name()) instanceof JsonString)) { + return false; + } + + String issuer = ((JsonString) presentedClaims.get(iss.name())).getString(); + + // TODO: make acceptedIssuers (set) + return acceptedIssuer.equals(issuer); + } + + private boolean checkIsJWT(JWSHeader header) { + return header.getType().toString().equals("JWT"); + } + + private String getCallerPrincipalName(Map rawClaims) { + JsonString callerPrincipalClaim = (JsonString) rawClaims.get(upn.name()); + + if (callerPrincipalClaim == null) { + callerPrincipalClaim = (JsonString) rawClaims.get(preferred_username.name()); + } + + if (callerPrincipalClaim == null) { + callerPrincipalClaim = (JsonString) rawClaims.get(sub.name()); + } + + if (callerPrincipalClaim == null) { + return null; + } + + return callerPrincipalClaim.getString(); + } +} diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java index 006568fff3b..1b1d1d9ba27 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java @@ -52,9 +52,9 @@ * @author Arjan Tijms * */ -public class TokenParser { +public class MockTokenParser { - private final JwtTokenParser jwtTokenParser = new JwtTokenParser(); + private final MockJwtTokenParser jwtTokenParser = new MockJwtTokenParser(); public JsonWebToken parse(String bearerToken, String issuer, PublicKey signedBy) throws Exception { try { From 5c9ccd0bc33316d6b7a1fe1fa0528d2e8dc0193e Mon Sep 17 00:00:00 2001 From: Alan Roth Date: Wed, 3 Jul 2019 14:14:54 +0100 Subject: [PATCH 8/9] Reverted a change --- .../jwtauth/tck/MockJwtTokenParser.java | 190 ------------------ .../jwtauth/tck/MockTokenParser.java | 2 +- 2 files changed, 1 insertion(+), 191 deletions(-) delete mode 100644 appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java deleted file mode 100644 index f15ade329db..00000000000 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockJwtTokenParser.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package fish.payara.microprofile.jwtauth.tck; - -import static com.nimbusds.jose.JWSAlgorithm.RS256; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jwt.SignedJWT; -import fish.payara.microprofile.jwtauth.jwt.JsonWebTokenImpl; -import java.io.StringReader; -import java.security.PublicKey; -import java.security.interfaces.RSAPublicKey; -import static java.util.Arrays.asList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import javax.json.Json; -import static javax.json.Json.createObjectBuilder; -import javax.json.JsonNumber; -import javax.json.JsonReader; -import javax.json.JsonString; -import javax.json.JsonValue; -import org.eclipse.microprofile.jwt.Claims; -import static org.eclipse.microprofile.jwt.Claims.exp; -import static org.eclipse.microprofile.jwt.Claims.groups; -import static org.eclipse.microprofile.jwt.Claims.iat; -import static org.eclipse.microprofile.jwt.Claims.iss; -import static org.eclipse.microprofile.jwt.Claims.jti; -import static org.eclipse.microprofile.jwt.Claims.preferred_username; -import static org.eclipse.microprofile.jwt.Claims.raw_token; -import static org.eclipse.microprofile.jwt.Claims.sub; -import static org.eclipse.microprofile.jwt.Claims.upn; - -/** - * - * @author AlanRoth - */ -public class MockJwtTokenParser { - private final static String DEFAULT_NAMESPACE = "https://payara.fish/mp-jwt/"; - - private final List requiredClaims = asList(iss, sub, exp, iat, jti, groups); - - private final boolean enableNamespacedClaims; - private final Optional customNamespace; - - private String rawToken; - private SignedJWT signedJWT; - - public MockJwtTokenParser(Optional enableNamespacedClaims, Optional customNamespace) { - this.enableNamespacedClaims = enableNamespacedClaims.orElse(false); - this.customNamespace = customNamespace; - } - - public MockJwtTokenParser() { - this(Optional.empty(), Optional.empty()); - } - - public void parse(String bearerToken) throws Exception { - rawToken = bearerToken; - signedJWT = SignedJWT.parse(rawToken); - - if(!checkIsJWT(signedJWT.getHeader())){ - throw new IllegalStateException("Not JWT"); - } - } - - public JsonWebTokenImpl verify(String issuer, PublicKey publicKey) throws Exception { - if(signedJWT == null){ - parse(rawToken); - } - - // MP-JWT 1.0 4.1 typ - if (!checkIsJWT(signedJWT.getHeader())) { - throw new IllegalStateException("Not JWT"); - } - - // 1.0 4.1 alg + MP-JWT 1.0 6.1 1 - if (!signedJWT.getHeader().getAlgorithm().equals(RS256)) { - throw new IllegalStateException("Not RS256"); - } - - try (JsonReader reader = Json.createReader(new StringReader(signedJWT.getPayload().toString()))) { - Map rawClaims = new HashMap<>(reader.readObject()); - - // Vendor - Process namespaced claims - rawClaims = handleNamespacedClaims(rawClaims); - - // MP-JWT 1.0 4.1 Minimum MP-JWT Required Claims - if (!checkRequiredClaimsPresent(rawClaims)) { - throw new IllegalStateException("Not all required claims present"); - } - - // MP-JWT 1.0 4.1 upn - has fallbacks - String callerPrincipalName = getCallerPrincipalName(rawClaims); - if (callerPrincipalName == null) { - throw new IllegalStateException("One of upn, preferred_username or sub is required to be non null"); - } - - // MP-JWT 1.0 6.1 2 - if (!checkIssuer(rawClaims, issuer)) { - throw new IllegalStateException("Bad issuer"); - } - - if (!checkNotExpired(rawClaims)) { - throw new IllegalStateException("Expired"); - } - - // MP-JWT 1.0 6.1 2 - if (!signedJWT.verify(new RSASSAVerifier((RSAPublicKey) publicKey))) { - throw new IllegalStateException("Signature invalid"); - } - - rawClaims.put( - raw_token.name(), - createObjectBuilder().add("token", rawToken).build().get("token")); - - return new JsonWebTokenImpl(callerPrincipalName, rawClaims); - } - } - - private Map handleNamespacedClaims(Map currentClaims){ - if(this.enableNamespacedClaims){ - final String namespace = customNamespace.orElse(DEFAULT_NAMESPACE); - Map processedClaims = new HashMap<>(currentClaims.size()); - for(String claimName : currentClaims.keySet()){ - JsonValue value = currentClaims.get(claimName); - if(claimName.startsWith(namespace)){ - claimName = claimName.substring(namespace.length()); - } - processedClaims.put(claimName, value); - } - return processedClaims; - }else{ - return currentClaims; - } - } - - private boolean checkRequiredClaimsPresent(Map presentedClaims) { - for (Claims requiredClaim : requiredClaims) { - if (presentedClaims.get(requiredClaim.name()) == null) { - return false; - } - } - return true; - } - - private boolean checkNotExpired(Map presentedClaims) { - int currentTime = (int) (System.currentTimeMillis() / 1000); - int expiredTime = ((JsonNumber) presentedClaims.get(exp.name())).intValue(); - - return currentTime < expiredTime; - } - - private boolean checkIssuer(Map presentedClaims, String acceptedIssuer) { - if (!(presentedClaims.get(iss.name()) instanceof JsonString)) { - return false; - } - - String issuer = ((JsonString) presentedClaims.get(iss.name())).getString(); - - // TODO: make acceptedIssuers (set) - return acceptedIssuer.equals(issuer); - } - - private boolean checkIsJWT(JWSHeader header) { - return header.getType().toString().equals("JWT"); - } - - private String getCallerPrincipalName(Map rawClaims) { - JsonString callerPrincipalClaim = (JsonString) rawClaims.get(upn.name()); - - if (callerPrincipalClaim == null) { - callerPrincipalClaim = (JsonString) rawClaims.get(preferred_username.name()); - } - - if (callerPrincipalClaim == null) { - callerPrincipalClaim = (JsonString) rawClaims.get(sub.name()); - } - - if (callerPrincipalClaim == null) { - return null; - } - - return callerPrincipalClaim.getString(); - } -} diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java index 1b1d1d9ba27..988fa869c10 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/tck/MockTokenParser.java @@ -54,7 +54,7 @@ */ public class MockTokenParser { - private final MockJwtTokenParser jwtTokenParser = new MockJwtTokenParser(); + private final JwtTokenParser jwtTokenParser = new JwtTokenParser(); public JsonWebToken parse(String bearerToken, String issuer, PublicKey signedBy) throws Exception { try { From 0e00ba886c65fab4dd9580ada85ee13801c46fd7 Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 3 Jul 2019 16:12:43 +0100 Subject: [PATCH 9/9] Formatting suggestions Co-Authored-By: Matt Gill --- .../fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java index feec45eb75b..7de6c72df8b 100644 --- a/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java +++ b/appserver/payara-appserver-modules/microprofile/jwt-auth/src/main/java/fish/payara/microprofile/jwtauth/jwt/JwtTokenParser.java @@ -88,13 +88,13 @@ public void parse(String bearerToken) throws Exception { rawToken = bearerToken; signedJWT = SignedJWT.parse(rawToken); - if(!checkIsJWT(signedJWT.getHeader())){ + if (!checkIsJWT(signedJWT.getHeader())) { throw new IllegalStateException("Not JWT"); } } public JsonWebTokenImpl verify(String issuer, PublicKey publicKey) throws Exception { - if(signedJWT == null){ + if (signedJWT == null) { parse(rawToken); }