From 8b70eded6c9cc3b9cf634356239701fe65779791 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 13 Jan 2023 15:46:48 +0300 Subject: Specify explicit list of claims expected in JWT verification --- .../com/github/scribejava/apis/AppleSignInApi.java | 20 +++++- .../scribejava/apis/GoogleTokenVerifier.java | 71 +++++++++++++--------- 2 files changed, 58 insertions(+), 33 deletions(-) (limited to 'src/main/java/com/github') diff --git a/src/main/java/com/github/scribejava/apis/AppleSignInApi.java b/src/main/java/com/github/scribejava/apis/AppleSignInApi.java index 84bd781f..5d11a2a6 100644 --- a/src/main/java/com/github/scribejava/apis/AppleSignInApi.java +++ b/src/main/java/com/github/scribejava/apis/AppleSignInApi.java @@ -27,22 +27,29 @@ import com.nimbusds.jose.proc.BadJOSEException; import com.nimbusds.jose.proc.JWSKeySelector; import com.nimbusds.jose.proc.JWSVerificationKeySelector; import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier; import com.nimbusds.jwt.proc.DefaultJWTProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; import java.util.Map; import java.util.Optional; +import java.util.Set; public class AppleSignInApi extends DefaultApi20 { + private static final Logger logger = LoggerFactory.getLogger("JWT"); private final AppleClientSecretGenerator clientSecretGenerator; + private final String applicationId; - public AppleSignInApi(AppleClientSecretGenerator clientSecretGenerator) { + public AppleSignInApi(AppleClientSecretGenerator clientSecretGenerator, String applicationId) { this.clientSecretGenerator = clientSecretGenerator; + this.applicationId = applicationId; } @Override @@ -89,17 +96,24 @@ public class AppleSignInApi extends DefaultApi20 { jwtProcessor.setJWSKeySelector(keySelector); // Set the required JWT claims for access tokens issued by the server - jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>()); + jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>( + new JWTClaimsSet.Builder() + .issuer("https://appleid.apple.com") + .audience(applicationId) + .build(), + Set.of("exp", "iat", "aud", "email") + )); // Process the token Map claimsSet; try { claimsSet = jwtProcessor.process(idToken, null).toJSONObject(); } catch (ParseException | BadJOSEException | JOSEException e) { + logger.error(e.getMessage(), e); return Optional.empty(); } - String email = (String)claimsSet.get("email"); + String email = (String) claimsSet.get("email"); boolean verified = claimsSet.get("email_verified").equals("true"); return verified ? Optional.of(email) : Optional.empty(); } diff --git a/src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java b/src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java index a7d48a34..138238e9 100644 --- a/src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java +++ b/src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java @@ -6,6 +6,7 @@ import java.text.ParseException; import java.util.Collections; import java.util.Map; import java.util.Optional; +import java.util.Set; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSAlgorithm; @@ -15,52 +16,62 @@ import com.nimbusds.jose.proc.BadJOSEException; import com.nimbusds.jose.proc.JWSKeySelector; import com.nimbusds.jose.proc.JWSVerificationKeySelector; import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier; import com.nimbusds.jwt.proc.DefaultJWTProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class GoogleTokenVerifier { + private static final Logger logger = LoggerFactory.getLogger("JWT"); public static Optional validateToken(String clientId, String idToken) { // Create a JWT processor for the access tokens - ConfigurableJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); - + ConfigurableJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); + // The public RSA keys to validate the signatures will be sourced from the // OAuth 2.0 server's JWK set, published at a well-known URL. The RemoteJWKSet // object caches the retrieved keys to speed up subsequent look-ups and can // also handle key-rollover - JWKSource keySource = - null; - try { - keySource = new RemoteJWKSet<>(new URL("https://www.googleapis.com/oauth2/v3/certs")); - } catch (MalformedURLException e) { - return Optional.empty(); - } - + JWKSource keySource = + null; + try { + keySource = new RemoteJWKSet<>(new URL("https://www.googleapis.com/oauth2/v3/certs")); + } catch (MalformedURLException e) { + return Optional.empty(); + } + // The expected JWS algorithm of the access tokens (agreed out-of-band) - JWSAlgorithm expectedJWSAlg = JWSAlgorithm.RS256; - + JWSAlgorithm expectedJWSAlg = JWSAlgorithm.RS256; + // Configure the JWT processor with a key selector to feed matching public // RSA keys sourced from the JWK set URL - JWSKeySelector keySelector = - new JWSVerificationKeySelector<>(expectedJWSAlg, keySource); - - jwtProcessor.setJWSKeySelector(keySelector); - + JWSKeySelector keySelector = + new JWSVerificationKeySelector<>(expectedJWSAlg, keySource); + + jwtProcessor.setJWSKeySelector(keySelector); + // Set the required JWT claims for access tokens issued by the server - jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>(Collections.singleton(clientId), null, null, null)); - + jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>( + new JWTClaimsSet.Builder() + .issuer("https://accounts.google.com") + .audience(clientId) + .build(), + Set.of("exp", "iat", "aud", "email"))); + // Process the token - Map claimsSet; - try { - claimsSet = jwtProcessor.process(idToken, null).toJSONObject(); - } catch (BadJOSEException | JOSEException | ParseException e) { - return Optional.empty(); - } - - String email = (String)claimsSet.get("email"); - boolean verified = claimsSet.get("email_verified").equals(true); - return verified ? Optional.of(email) : Optional.empty(); - } + Map claimsSet; + try { + claimsSet = jwtProcessor.process(idToken, null).toJSONObject(); + } catch (BadJOSEException | JOSEException | ParseException e) { + logger.error(e.getMessage(), e); + return Optional.empty(); + } + + String email = (String) claimsSet.get("email"); + boolean verified = claimsSet.get("email_verified").equals(true); + return verified ? Optional.of(email) : Optional.empty(); + } } -- cgit v1.2.3