aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2021-04-12 19:09:44 +0300
committerGravatar Vitaly Takmazov2021-04-12 19:09:44 +0300
commitdeb873a5f4ea6429fa5974c0dfe62b9e0544d9fb (patch)
tree2b26f90cdfcd36b6d50bbcf33f55682b34ad1419 /src
parent8a4691ce820d26943a33095055c372461ae3bad6 (diff)
Verify Google JWT tokens without Google libraries
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java66
-rw-r--r--src/main/java/com/juick/www/api/ApiSocialLogin.java22
2 files changed, 71 insertions, 17 deletions
diff --git a/src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java b/src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java
new file mode 100644
index 00000000..35a9d832
--- /dev/null
+++ b/src/main/java/com/github/scribejava/apis/GoogleTokenVerifier.java
@@ -0,0 +1,66 @@
+package com.github.scribejava.apis;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Map;
+import java.util.Optional;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.jwk.source.RemoteJWKSet;
+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.proc.ConfigurableJWTProcessor;
+import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier;
+import com.nimbusds.jwt.proc.DefaultJWTProcessor;
+
+public class GoogleTokenVerifier {
+
+ public static Optional<String> validateToken(String idToken) {
+
+ // Create a JWT processor for the access tokens
+ 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();
+ }
+
+ // The expected JWS algorithm of the access tokens (agreed out-of-band)
+ 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);
+
+ // Set the required JWT claims for access tokens issued by the server
+ jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>());
+
+ // 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();
+ }
+}
diff --git a/src/main/java/com/juick/www/api/ApiSocialLogin.java b/src/main/java/com/juick/www/api/ApiSocialLogin.java
index 43de04bb..e6116173 100644
--- a/src/main/java/com/juick/www/api/ApiSocialLogin.java
+++ b/src/main/java/com/juick/www/api/ApiSocialLogin.java
@@ -20,18 +20,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.scribejava.apis.AppleClientSecretGenerator;
import com.github.scribejava.apis.AppleSignInApi;
import com.github.scribejava.apis.FacebookApi;
+import com.github.scribejava.apis.GoogleTokenVerifier;
import com.github.scribejava.apis.VkontakteApi;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;
-import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
-import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
-import com.google.api.client.http.HttpTransport;
-import com.google.api.client.http.javanet.NetHttpTransport;
-import com.google.api.client.json.JsonFactory;
-import com.google.api.client.json.jackson2.JacksonFactory;
import com.juick.model.AuthResponse;
import com.juick.model.ext.facebook.User;
import com.juick.model.ext.vk.UsersResponse;
@@ -58,8 +53,8 @@ import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.io.IOException;
import java.security.GeneralSecurityException;
-import java.util.Collections;
import java.util.Map;
+import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@@ -109,18 +104,11 @@ public class ApiSocialLogin {
@Inject
private Users users;
- private final HttpTransport transport = new NetHttpTransport();
- private final JsonFactory jsonFactory = new JacksonFactory();
- private GoogleIdTokenVerifier verifier;
-
@PostConstruct
public void init() {
ServiceBuilder facebookBuilder = new ServiceBuilder(FACEBOOK_APPID);
ServiceBuilder twitterBuilder = new ServiceBuilder(twitterConsumerKey);
ServiceBuilder vkBuilder = new ServiceBuilder(VK_APPID);
- verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
- .setAudience(Collections.singletonList(googleClientId))
- .build();
facebookAuthService = facebookBuilder
.apiSecret(FACEBOOK_SECRET)
.callback(FACEBOOK_REDIRECT)
@@ -246,9 +234,9 @@ public class ApiSocialLogin {
throws GeneralSecurityException, IOException {
logger.info("Token: {}", idTokenString);
logger.info("Client: {}", googleClientId);
- GoogleIdToken idToken = verifier.verify(idTokenString);
- if (idToken != null) {
- String email = idToken.getPayload().getEmail();
+ Optional<String> verifiedEmail = GoogleTokenVerifier.validateToken(idTokenString);
+ if (verifiedEmail.isPresent()) {
+ String email = verifiedEmail.get();
com.juick.model.User visitor = userService.getUserByEmail(email);
if (visitor.isAnonymous()) {
String verificationCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase();