【问题标题】:firebase | Verify ID tokens using a third-party JWT library火力基地|使用第三方 JWT 库验证 ID 令牌
【发布时间】:2017-01-06 10:02:55
【问题描述】:

尝试使用 jjwt 验证 firebase id 令牌。使用 GoogleCredential 类提取私钥。但我不确定这是否正确。收到错误:JWT signature does not match locally computed signature.我应该在此处使用服务帐户 json 中的私钥吗?也许我误解了 ...setSigningKey(...) 的含义。

@Service
public class FirebaseAuthVerifier implements AuthVerifier {

    private static final Logger logger = LoggerFactory.getLogger(FirebaseAuthVerifier.class);

    @Autowired
    private FirebaseProperties fbProps;

    public boolean verify(AuthToken token) throws GeneralSecurityException, IOException {
        // get google credential
        InputStream stream = new FileInputStream("src/main/resources/service-account.json");
        ByteArrayOutputStream streamCopy = new ByteArrayOutputStream();
        ByteStreams.copy(stream, streamCopy);
        stream.close();

        GoogleCredential gc = GoogleCredential.fromStream(
                new ByteArrayInputStream(streamCopy.toByteArray()),
                new NetHttpTransport(),
                GsonFactory.getDefaultInstance());

        try {
            Jwts.parser().setSigningKey(gc.getServiceAccountPrivateKey()).parse(token.getTokenId());
        } catch(Exception e) {
            // log
            logger.info("Firebase auth token verification error: ");
            logger.info(e.getMessage());
            // claims may have been tampered with
            return false;
        }

        return true;
    }

}

【问题讨论】:

    标签: java firebase jwt firebase-authentication google-authentication


    【解决方案1】:

    你在正确的路线上!创建 JWT 以发送到 Google/Firebase 时会使用服务帐户中的密钥。你真的不想把它放在你的 APK 中,因为任何恶意的人都可以窃取它并像你一样使用它来创建 ID 令牌!

    当您从 Firebase 验证令牌时,您需要检查 Firebase 自己的密钥 - 幸运的是,这些密钥是公开的!您可以从https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com 获取它们 - 它们每隔几个小时轮换一次。如果您查看该文件,您会看到它是一个 JSON 字典,如下所示:

    "8226146523a1b8894ba03ad525667b9475d393f5": "---CERT---",
    

    其中的密钥是 ID 令牌 JWT 标头中的 kid 字段 - 它对应于签署令牌的密钥,这意味着对应的证书可用于验证签名。

    查看validating ID tokens 的(服务器端)文档了解更多信息。

    【讨论】:

    • 我明白了,我在想也许声明验证会检查 keyId。我会检查一下。存储私钥仅用于开发目的,一旦事情进展,我将确保删除并重新制作。
    • 我成功了!将我的代码发布在下面的另一个答案中,以防您想查看它。再次感谢您(:
    【解决方案2】:

    使用自定义 jwt id 令牌验证

    @Service
    public class FirebaseAuthVerifier implements AuthVerifier {
    
        private static final Logger logger = LoggerFactory.getLogger(FirebaseAuthVerifier.class);
        private static final String pubKeyUrl = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
    
        /**
         *
         * @param token
         * @return
         * @throws GeneralSecurityException
         * @throws IOException
         */
        public boolean verify(AuthToken token) throws GeneralSecurityException, IOException {
            // get public keys
            JsonObject publicKeys = getPublicKeysJson();
    
            // verify count
            int size = publicKeys.entrySet().size();
            int count = 0;
    
            // get json object as map
            // loop map of keys finding one that verifies
            for (Map.Entry<String, JsonElement> entry: publicKeys.entrySet()) {
                // log
                logger.info("attempting jwt id token validation with: ");
    
                try {
                    // trying next key
                    count++;
    
                    // get public key
                    PublicKey publicKey = getPublicKey(entry);
    
                    // validate claim set
                    Jwts.parser().setSigningKey(publicKey).parse(token.getTokenId());
    
                    // success, we can return
                    return true;
                } catch(Exception e) {
                    // log
                    logger.info("Firebase id token verification error: ");
                    logger.info(e.getMessage());
                    // claims may have been tampered with
                    // if this is the last key, return false
                    if (count == size) {
                        return false;
                    }
                }
            }
    
            // no jwt exceptions
            return true;
        }
    
        /**
         *
         * @param entry
         * @return
         * @throws GeneralSecurityException
         */
        private PublicKey getPublicKey(Map.Entry<String, JsonElement> entry) throws GeneralSecurityException, IOException {
            String publicKeyPem = entry.getValue().getAsString()
                    .replaceAll("-----BEGIN (.*)-----", "")
                    .replaceAll("-----END (.*)----", "")
                    .replaceAll("\r\n", "")
                    .replaceAll("\n", "")
                    .trim();
    
            logger.info(publicKeyPem);
    
            // generate x509 cert
            InputStream inputStream = new ByteArrayInputStream(entry.getValue().getAsString().getBytes("UTF-8"));
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate)cf.generateCertificate(inputStream);
    
            return cert.getPublicKey();
        }
    
        /**
         *
         * @return
         * @throws IOException
         */
        private JsonObject getPublicKeysJson() throws IOException {
            // get public keys
            URI uri = URI.create(pubKeyUrl);
            GenericUrl url = new GenericUrl(uri);
            HttpTransport http = new NetHttpTransport();
            HttpResponse response = http.createRequestFactory().buildGetRequest(url).execute();
    
            // store json from request
            String json = response.parseAsString();
            // disconnect
            response.disconnect();
    
            // parse json to object
            JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
    
            return jsonObject;
        }
    
    }
    

    【讨论】:

    • 我错过了这是一个自我回答。以后,标记帖子而不是评论,并保持建设性的语气。
    猜你喜欢
    • 2022-01-21
    • 2021-04-02
    • 2015-06-27
    • 2017-06-25
    • 2023-03-12
    • 1970-01-01
    • 2017-07-31
    • 2019-06-23
    相关资源
    最近更新 更多