间歇性错误的 Jws 签名 w/t Google Apps Open ID Authentication

Intermittant Bad Jws Signature w/t Google Apps Open ID Authentication

我们正在使用 Connect2D OAuth SDK(参见:http://connect2id.com/products/nimbus-oauth-openid-connect-sdk)通过 Open ID 协议对 Google Apps 用户进行身份验证。用户报告间歇性的身份验证问题。有时,我们的应用程序无法验证从 Google 个应用程序收到的令牌。

OIDToken token = new OIDToken(getApplication(), tokenResponse.getIDToken(), id);
ReadOnlyJWTClaimsSet claimsSet = null;
try {
    claimsSet = token.verify();
} catch (Exception e) {
   throw new SecurityProviderException(994,"failed to verify token:"+e.getMessage());
}

报错是:"Bad JWS signature".

问题是间歇性的。有时有效,有时无效。

非常感谢任何建议。

我很确定这就是答案,但我可能错了。我们拭目以待。

Google 的主要挑战是它似乎从其证书端点提供了两个备用密钥。它们具有相同的算法,但密钥标识符不同。它看起来有点像这样:

{
 "keys": [
  {
   "kty": "RSA",
   "alg": "RS256",
   "use": "sig",
   "kid": "5543f23f58e980646d8088d9393fcc3c1ac69ec5",
   "n": "xxx...",
   "e": "AQAB"
  },
  {
   "kty": "RSA",
   "alg": "RS256",
   "use": "sig",
   "kid": "da6625b36bc09d300353b28a741ce17525a4c33b",
   "n": "yyy...",
   "e": "AQAB"
  }
 ]
}

不幸的是,Connect2D JWT 解码器的默认实现 select 是一个单独的算法密钥(即,只有 RS256),所以你先得到哪个就看运气了。它们似乎是按顺序添加的,所以计时模式(我猜)是 Google 选择相当一致的键的顺序,结合键轮换。

也就是说,它的逻辑是(简化):

    JWSAlgorithm alg = signedJWT.getHeader().getAlgorithm();        
    JWSVerifier verifier = jwsVerifiers.get(alg);
    boolean verified = signedJWT.verify(verifier);

这意味着密钥 ID (kid) 将被忽略,即使它存在于 JWT header 中。实际上,验证者 selected 有 50% 的可能性是错误的,因此显然不会验证。所有查找都是顺序的,因此可能是稳定的,导致观察到的持续成功或失败时期的行为。

JWT 解码器的正确实现可能应该:

  • 使用孩子以及算法来select使用哪个密钥
  • 如果给定孩子没有可用的验证器,可能应该 re-fetch 来自发现 API 端点的密钥。这是因为 Google 经常更改它们的密钥,因此如果您初始化一次,您将永远不会获得新密钥。

所以我的推断是 Google 这是正确但复杂的 OIDC 行为,默认情况下,Connect2D OAuth SDK 中并未实现该行为。

我应该在 Connect2D OAuth SDK bitbucket 网站上报告这一点。

"workaround" 似乎是不直接使用 DefaultJWTDecoder,而是将其子类化并添加缺少的逻辑,据我所知,这似乎大部分是可行的,尽管关键更新可能需要一点思考。

更新

是的,问题已通过以下评论确认:https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/issues/138/google-openid-connect-cant-locate-which

评论是:

Sorry, there was a bit of a mix up, it is the Nimbus JOSE+JWT lib that now has a brand new framework for processing JWTs (http://connect2id.com/blog/nimbus-jose-jwt-4.0-rc1), however, its integration into the OIDC SDK is still on our to do list. We're currently busy completing a new release of the Connect2id server, and will not be able to dedicate time on the SDK until 29 July.

If you're using v3.4.1 of the OIDC SDK you have two options:

  1. Implement an alternative JWTDecoder.
  2. Extend the JWSVerifier implementation to support key ID lookup.

The first approach will probably be the easier of the two.

因为我在使用 Apache Shiro,所以我不得不与之集成,但大部分逻辑是 here,尽管我没有对其进行大量测试。该功能分支的其余部分似乎确实使用 ConnectID 针对 Google OIDC 进行了可靠的身份验证。

基本原理是添加密钥轮换逻辑。

如果您可以更新到 ConnectID 的 4.0,您可能会更快,但由于各种依赖层,我不得不坚持使用 3.4.1。