2022年5月10日 星期二

紀錄驗證 Google 第三方登入傳入的RSA 256 JWT token 的程式碼

紀錄驗證 Google 第三方登入傳入的RSA 256 JWT token 的程式碼
, 其官方網站有公開 Public key 的 JWK
JWK網址是:https://www.googleapis.com/oauth2/v3/certs

有兩種驗證的方法:

  1. 使用第三方 JWK 驗證相關的 Library 來做驗證 (不限只能驗證 Google 的 JWK,例如也能驗證 Apple 的 JWK)。
  2. 使用 Google 提供的 LIbrary 來做驗證。

首先是第一種,
1. 使用第三方 JWK 驗證相關的 Library 來做驗證 (不限只能驗證 google 的 JWT)。
範例如下:

Maven 的 pom.xml :

<dependency>
	    <groupId>com.auth0</groupId>
	    <artifactId>java-jwt</artifactId>
	    <version>3.18.2</version>
	</dependency>
	
	<dependency>
	    <groupId>com.auth0</groupId>
	    <artifactId>jwks-rsa</artifactId>
	    <version>0.20.0</version>
	</dependency>

可能會需要 javax.xml.bind 這個 lib,因為 jdk 8 以上沒有 jaxb 模塊,詳見: 真正解决方案:java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter

<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
	<dependency>
	    <groupId>javax.xml.bind</groupId>
	    <artifactId>jaxb-api</artifactId>
	    <version>2.3.0</version>
	</dependency>

Java:

package test;

import java.io.IOException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;

import net.sf.json.JSONObject;

public class JWTTest {
	public static void main(String[] args) throws IOException, GeneralSecurityException, JwkException {
		String token = "someGoogleJwtToken";
		
		verifyToken(token);
		
	}

	public static verifyToken(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			JwkProvider provider = new UrlJwkProvider(new URL("https://www.googleapis.com/oauth2/v3/certs"));
			RSAPublicKey publicKey = (RSAPublicKey) provider.get(jwt.getKeyId()).getPublicKey();
			
			Algorithm algorithm = Algorithm.RSA256(publicKey, null);
			JWTVerifier verifier = JWT.require(algorithm)
					// more validations if needed
					.build();
			jwt = verifier.verify(token);
			
			System.out.println("User Id: " + jwt.getSubject());
			System.out.println("Email: " + jwt.getClaim("email").asString());
		} catch (Exception e) {
			System.out.println("Exception in verifying " + e.toString());
		}
	}
}

再來是第二種,
2. 使用 Google 提供的 LIbrary 來做驗證。
範例如下:

Maven 的 pom.xml :

<dependency>
		<groupId>com.google.api-client</groupId>
		<artifactId>google-api-client</artifactId>
		<version>1.32.1</version>
	</dependency>

Java:

package test;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;

import com.auth0.jwk.JwkException;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;

public class GoogleJWTTest2 {
	public static void main(String[] args) throws IOException, GeneralSecurityException, JwkException {
		String token = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImZjYmQ3ZjQ4MWE4MjVkMTEzZTBkMDNkZDk0ZTYwYjY5ZmYxNjY1YTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2NTE4MzA3OTQsImF1ZCI6Ijk2MjE1NTMyNDMyMy1vY3U5MzNkazFiYzY0dGhkM3JrdnVsaXI5MHVya3Nuby5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwMjA1NTY1MzA0MDM0ODc0MzExMyIsImVtYWlsIjoiaHVnb2dvNzY0NkBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXpwIjoiOTYyMTU1MzI0MzIzLW9jdTkzM2RrMWJjNjR0aGQzcmt2dWxpcjkwdXJrc25vLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwibmFtZSI6IumDreeAmumahiIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQU9oMTRHaExOWHdxT2l2cXAyOHdRY3lNZ3FGZERUU0dyYW1sSDQwTDRTejEzZz1zOTYtYyIsImdpdmVuX25hbWUiOiLngJrpmoYiLCJmYW1pbHlfbmFtZSI6IumDrSIsImlhdCI6MTY1MTgzMTA5NCwiZXhwIjoxNjUxODM0Njk0LCJqdGkiOiJmZDFlY2VmMThiNzAyZDIyNzQwNjRjZjdmMzMwOTExZDBlYzQ4NDI0In0.OFc3-NIiSsChOJWgF_SJZ9yWhSpAhY95PSllh7gSqS8YYiBJD6DIZCvbHnL2SLU69lv2kntoR-hG1aQU07ppgGN5xuqJAagvKJ8KSSkxJxSR5qOLFNMBYPghp0zgFybNEAQDTbj3E5zRlemX7w9irEMqkMliRAMDYE3aUkcOrho9X2vd9wJDrkwKmMaLfXa71MVPwIYpsOrg2Gq82nHLw24eM47VRTp3m1sqXdKz9WHgfW2_2y9GB0qn3E8Fo99wBgegRyAlz6UvbTzDNQOUdrvuSALXcrOZzog5rrfW0MinxdVfRbSNRKL0VGMJWzuGefxNqEV-Fu0CTPIqliXf1A";

		verifyToken(token);

	}

	public static boolean verifyToken(String token) {
		try {
			GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
					GoogleNetHttpTransport.newTrustedTransport(), new GsonFactory())
							// Specify the CLIENT_ID of the app that accesses the backend:
//							.setAudience(Collections.singletonList(
//									"962155324323-ocu933dk1bc64thd3rkvulir90urksno.apps.googleusercontent.com"))
							// Or, if multiple clients access the backend:
							// .setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
							.build();

			// (Receive idTokenString by HTTPS POST)
			GoogleIdToken idToken = verifier.verify(token);
			if (idToken != null) {
				Payload payload = idToken.getPayload();
				// Print user identifier
				String userId = payload.getSubject();
				System.out.println("User ID: " + userId);

				// Get profile information from payload
				String email = payload.getEmail();
				boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
				String name = (String) payload.get("name");
				String pictureUrl = (String) payload.get("picture");
				String locale = (String) payload.get("locale");
				String familyName = (String) payload.get("family_name");
				String givenName = (String) payload.get("given_name");
				System.out.println(email);
				// Use or store profile information
				// ...

			} else {
				System.out.println("Invalid ID token.");
			}

			return true;
		} catch (Exception e) {
			System.out.println("Exception in verifying " + e.toString());
			return false;
		}
	}
}