diff options
4 files changed, 60 insertions, 35 deletions
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<String, Object> 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<String> validateToken(String clientId, String idToken) { // Create a JWT processor for the access tokens - ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>(); - + ConfigurableJWTProcessor<SecurityContext> 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<SecurityContext> keySource = - null; - try { - keySource = new RemoteJWKSet<>(new URL("https://www.googleapis.com/oauth2/v3/certs")); - } catch (MalformedURLException e) { - return Optional.empty(); - } - + JWKSource<SecurityContext> 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<SecurityContext> keySelector = - new JWSVerificationKeySelector<>(expectedJWSAlg, keySource); - - jwtProcessor.setJWSKeySelector(keySelector); - + JWSKeySelector<SecurityContext> 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<String, Object> 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<String, Object> 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(); + } } diff --git a/src/main/java/com/juick/www/api/ApiSocialLogin.java b/src/main/java/com/juick/www/api/ApiSocialLogin.java index c1e31b68..a05e33d9 100644 --- a/src/main/java/com/juick/www/api/ApiSocialLogin.java +++ b/src/main/java/com/juick/www/api/ApiSocialLogin.java @@ -122,7 +122,7 @@ public class ApiSocialLogin { appleSignInService = appleSignInBuilder .callback(appleSignInRedirectUri) .defaultScope("email") - .build(new AppleSignInApi(clientSecretGenerator)); + .build(new AppleSignInApi(clientSecretGenerator, appleApplicationId)); } @GetMapping("/api/_fblogin") diff --git a/src/main/java/com/juick/www/controllers/SocialLogin.java b/src/main/java/com/juick/www/controllers/SocialLogin.java index 3e865215..4e26ab99 100644 --- a/src/main/java/com/juick/www/controllers/SocialLogin.java +++ b/src/main/java/com/juick/www/controllers/SocialLogin.java @@ -124,7 +124,7 @@ public class SocialLogin { ServiceBuilder appleSignInBuilder = new ServiceBuilder(appleApplicationId); String appleSignInRedirectUri = redirectBuilder.replacePath("/_apple").build().toUriString(); appleSignInService = appleSignInBuilder.callback(appleSignInRedirectUri).defaultScope("email") - .build(new AppleSignInApi(clientSecretGenerator)); + .build(new AppleSignInApi(clientSecretGenerator, appleApplicationId)); } @GetMapping("/_fblogin") |