From c7a25d6a8b9833f8389e4bc2094c9fe96607ec3a Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Thu, 28 May 2020 15:03:11 -0700 Subject: [PATCH 1/4] refactor: use new verify token functionality from google-auth-library --- iap/pom.xml | 7 +- .../example/iap/VerifyIapRequestHeader.java | 84 +++---------------- 2 files changed, 11 insertions(+), 80 deletions(-) diff --git a/iap/pom.xml b/iap/pom.xml index 63d890ed840..535163fec73 100644 --- a/iap/pom.xml +++ b/iap/pom.xml @@ -58,12 +58,7 @@ com.google.auth google-auth-library-oauth2-http - 0.20.0 - - - com.nimbusds - nimbus-jose-jwt - 8.17.1 + 0.20.1-SNAPSHOT diff --git a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java index 61503264456..b2e463e8ec5 100644 --- a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java +++ b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java @@ -18,53 +18,11 @@ // [START iap_validate_jwt] import com.google.api.client.http.HttpRequest; -import com.google.common.base.Preconditions; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.ECDSAVerifier; -import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jose.jwk.JWK; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import java.net.URL; -import java.security.interfaces.ECPublicKey; -import java.time.Clock; -import java.time.Instant; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; +import com.google.auth.oauth2.TokenVerifier; /** Verify IAP authorization JWT token in incoming request. */ public class VerifyIapRequestHeader { - private static final String PUBLIC_KEY_VERIFICATION_URL = - "https://www.gstatic.com/iap/verify/public_key-jwk"; - - private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap"; - - // using a simple cache with no eviction for this sample - private final Map keyCache = new HashMap<>(); - - private static Clock clock = Clock.systemUTC(); - - private ECPublicKey getKey(String kid, String alg) throws Exception { - JWK jwk = keyCache.get(kid); - if (jwk == null) { - // update cache loading jwk public key data from url - JWKSet jwkSet = JWKSet.load(new URL(PUBLIC_KEY_VERIFICATION_URL)); - for (JWK key : jwkSet.getKeys()) { - keyCache.put(key.getKeyID(), key); - } - jwk = keyCache.get(kid); - } - // confirm that algorithm matches - if (jwk != null && jwk.getAlgorithm().getName().equals(alg)) { - return ECKey.parse(jwk.toJSONString()).toECPublicKey(); - } - return null; - } - // Verify jwt tokens addressed to IAP protected resources on App Engine. // The project *number* for your Google Cloud project via 'gcloud projects describe $PROJECT_ID' // The project *number* can also be retrieved from the Project Info card in Cloud Console. @@ -97,37 +55,15 @@ boolean verifyJwtForComputeEngine( } private boolean verifyJwt(String jwtToken, String expectedAudience) throws Exception { - - // parse signed token into header / claims - SignedJWT signedJwt = SignedJWT.parse(jwtToken); - JWSHeader jwsHeader = signedJwt.getHeader(); - - // header must have algorithm("alg") and "kid" - Preconditions.checkNotNull(jwsHeader.getAlgorithm()); - Preconditions.checkNotNull(jwsHeader.getKeyID()); - - JWTClaimsSet claims = signedJwt.getJWTClaimsSet(); - - // claims must have audience, issuer - Preconditions.checkArgument(claims.getAudience().contains(expectedAudience)); - Preconditions.checkArgument(claims.getIssuer().equals(IAP_ISSUER_URL)); - - // claim must have issued at time in the past - Date currentTime = Date.from(Instant.now(clock)); - Preconditions.checkArgument(claims.getIssueTime().before(currentTime)); - // claim must have expiration time in the future - Preconditions.checkArgument(claims.getExpirationTime().after(currentTime)); - - // must have subject, email - Preconditions.checkNotNull(claims.getSubject()); - Preconditions.checkNotNull(claims.getClaim("email")); - - // verify using public key : lookup with key id, algorithm name provided - ECPublicKey publicKey = getKey(jwsHeader.getKeyID(), jwsHeader.getAlgorithm().getName()); - - Preconditions.checkNotNull(publicKey); - JWSVerifier jwsVerifier = new ECDSAVerifier(publicKey); - return signedJwt.verify(jwsVerifier); + TokenVerifier tokenVerifier = TokenVerifier.newBuilder() + .setAudience(expectedAudience) + .build(); + try { + tokenVerifier.verify(jwtToken); + return true; + } catch (TokenVerifier.VerificationException e) { + return false; + } } } // [END iap_validate_jwt] From 660c13cb5a368beea79214f9f5082a8360e961b2 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 29 May 2020 12:42:02 -0700 Subject: [PATCH 2/4] fix: restore check for subject and email --- .../com/example/iap/VerifyIapRequestHeader.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java index b2e463e8ec5..5f2442bd976 100644 --- a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java +++ b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java @@ -18,11 +18,14 @@ // [START iap_validate_jwt] import com.google.api.client.http.HttpRequest; +import com.google.api.client.json.webtoken.JsonWebToken; import com.google.auth.oauth2.TokenVerifier; /** Verify IAP authorization JWT token in incoming request. */ public class VerifyIapRequestHeader { + private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap"; + // Verify jwt tokens addressed to IAP protected resources on App Engine. // The project *number* for your Google Cloud project via 'gcloud projects describe $PROJECT_ID' // The project *number* can also be retrieved from the Project Info card in Cloud Console. @@ -54,13 +57,17 @@ boolean verifyJwtForComputeEngine( Long.toUnsignedString(projectNumber), Long.toUnsignedString(backendServiceId))); } - private boolean verifyJwt(String jwtToken, String expectedAudience) throws Exception { + private boolean verifyJwt(String jwtToken, String expectedAudience) { TokenVerifier tokenVerifier = TokenVerifier.newBuilder() .setAudience(expectedAudience) + .setIssuer(IAP_ISSUER_URL) .build(); try { - tokenVerifier.verify(jwtToken); - return true; + JsonWebToken jsonWebToken = tokenVerifier.verify(jwtToken); + + // must have subject, email + JsonWebToken.Payload payload = jsonWebToken.getPayload(); + return payload.getSubject() != null && payload.get("email") != null; } catch (TokenVerifier.VerificationException e) { return false; } From 8b8783de903c5dd9b0aa3d1112753567e676b5fd Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Wed, 24 Jun 2020 13:50:12 -0700 Subject: [PATCH 3/4] docs: clarify comment about subject/email claims --- iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java index 5f2442bd976..9d8007704c2 100644 --- a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java +++ b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java @@ -65,7 +65,7 @@ private boolean verifyJwt(String jwtToken, String expectedAudience) { try { JsonWebToken jsonWebToken = tokenVerifier.verify(jwtToken); - // must have subject, email + // Verify that the token contain subject and email claims JsonWebToken.Payload payload = jsonWebToken.getPayload(); return payload.getSubject() != null && payload.get("email") != null; } catch (TokenVerifier.VerificationException e) { From 783c5874161b842704b6003a12a361ef546b1b6c Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Thu, 25 Jun 2020 10:09:33 -0700 Subject: [PATCH 4/4] fix: print verification exception message to stdout --- iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java index 9d8007704c2..5f894187cd9 100644 --- a/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java +++ b/iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java @@ -69,6 +69,7 @@ private boolean verifyJwt(String jwtToken, String expectedAudience) { JsonWebToken.Payload payload = jsonWebToken.getPayload(); return payload.getSubject() != null && payload.get("email") != null; } catch (TokenVerifier.VerificationException e) { + System.out.println(e.getMessage()); return false; } }