Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use new verify token functionality from google-auth-library #2986

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions iap/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,6 @@
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>0.21.0</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>8.19</version>
</dependency>
<!-- [END dependencies] -->

<!-- Test dependencies -->
Expand Down
89 changes: 16 additions & 73 deletions iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,53 +18,14 @@
// [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.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 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<String, JWK> 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.
Expand Down Expand Up @@ -96,38 +57,20 @@ boolean verifyJwtForComputeEngine(
Long.toUnsignedString(projectNumber), Long.toUnsignedString(backendServiceId)));
}

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);
private boolean verifyJwt(String jwtToken, String expectedAudience) {
TokenVerifier tokenVerifier = TokenVerifier.newBuilder()
.setAudience(expectedAudience)
chingor13 marked this conversation as resolved.
Show resolved Hide resolved
.setIssuer(IAP_ISSUER_URL)
.build();
try {
JsonWebToken jsonWebToken = tokenVerifier.verify(jwtToken);

// 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) {
chingor13 marked this conversation as resolved.
Show resolved Hide resolved
return false;
}
lesv marked this conversation as resolved.
Show resolved Hide resolved
}
}
// [END iap_validate_jwt]