我正在做一个项目,我们将在微服务架构中使用不同的服务,并且我们还想使用一些 Firebase 服务。我正在开发一个身份验证服务器,该服务器将创建自定义 JWT,以用于 Firebase 以及其他 API 项目。
我们希望使用 Firebase Auth SDK 轻松与 FB、Google、Twitter 等集成,但我们需要用更多数据丰富用户的令牌。因此,我的想法是创建一个使用 Firebase Admin SDK 来执行此操作的 Node.JS 身份验证服务器。流程如下:
- 用户在客户端使用最喜欢的提供商登录
- 如果登录成功,用户会收到来自 Firebase 的 JWT。这被发送到身份验证服务器进行验证
- 如果身份验证服务器可以使用管理 SDK 验证令牌,则创建一个包含更多数据的新自定义令牌,并将此新自定义令牌返回给客户端
- 让客户端使用新的自定义令牌重新进行身份验证,并将其用于与 Firebase 以及我们的其他 API 项目(主要在 .NET Core 中)进行通信
步骤 1-3 工作正常。尝试验证其他服务上的自定义令牌时会出现问题。
TL; DR:这里有两个问题:
- 验证使用 Firebase Node.JS Admin SDK 发布的自定义令牌时,我应该使用什么作为公钥?从 Google 公开的 JWK 中提取的密钥,还是从用于签名的私钥中提取的密钥?
- 在 JWK 方法的情况下,我应该如何构造带有
kid
标题的自定义令牌?
首先,我怀疑验证它的正确方法。(请原谅,我在创建 OAuth 流程方面没有经验。)使用的算法是 RS256,所以我应该能够使用公钥来验证令牌。如我所见,有两种方法可以获取此密钥:
- 从私钥中提取公钥并使用它进行验证。我可以这样做并在我的身份验证服务器上的测试端点上成功验证,但是我觉得这是不正确的方法
- 我认为另一种更正确的方法是使用令牌中的值在我的项目的 Google 的“/.well-known/openid-configuration/”端点上找到 JWK,即
https://securetoken.google.com/[项目 ID]/.well-known/openid-configuration
检索正确(密钥 ID)的指数和模数kid
并从中创建公钥。
通过执行从 admin SDK 生成的令牌
admin.auth().createCustomToken(uid, additionalClaims).then(function(customToken)
一些自定义声明看起来像这样:
标题:
{
"alg": "RS256",
"typ": "JWT"
}
有效载荷:
{
"claims": {
"premiumAccount": true,
"someRandomInnerObject": {
"something": "somethingRandom"
}
},
"uid": "<uid for the user>",
"iat": 1488454663,
"exp": 1488458263,
"aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iss": "firebase-adminsdk-le7ge@<PROJECT ID>.iam.gserviceaccount.com",
"sub": "firebase-adminsdk-le7ge@<PROJECT ID>.iam.gserviceaccount.com"
}
不过,我似乎无法让方法 2 起作用。一个问题是生成的令牌没有 kid
标头,因此不符合 OpenID 规范 (AFAIK),这导致以下两种选择之一:
- 使用上面的第一种方法。但是这会导致问题 - 如果我出于某种原因需要撤销或重置身份验证服务器上的私钥,我需要这样做并将更改也部署到所有其他服务上,从而使解决方案的动态性降低且更容易出错.
- 使用 jwt.io 中提到的库之一手动生成类似的令牌,并将原始 Firebase ID 令牌中的孩子添加到其标头中。
2号问题:
- 那么我应该把什么作为 iss、aud 和 sub 呢?与 admin SDK 的值相同吗?如果是这样,那不是“作弊”吗,因为他们不再是发行人?
- 我已经尝试过了(生成令牌的类似副本,但添加了
kid
原始令牌的副本),但我似乎无法使用为孩子创建的 PEM 密钥验证生成的令牌。
我做后者的方式是这样的(遵循关于该主题的博客指南):
转到https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com并检索相关孩子的模数 (n) 和指数 (e)
使用密钥验证使用“官方”jwt lib
上面的结果是这样的公钥:
-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAxXpo7ChLMnv1QTovmm9DkAnYgINO1WFBWGAVRt93ajftPpVNcxMT MAQI4Jf06OxFCQib94GyHxKDNOYiweVrHVYH9j/STF+xbQwiPF/8L7+haC2WXMl2 tkTgmslVewWuYwpfm4CoQFV29OVGWCqwEcbCaycWVddm1ykdryXzNTqfzCyrSZdZ k0yoE0Q1GDcuUl/6tjH1gAfzN6c8wPvI2YDhc5gIHm04BcLVVMBXnC0hxgjbJbN4 zg2QafiUpICZzonOUbK6+rrIFGfHpcv8mWG1Awsu5qs33aFu1Qx/4LdMAuEsvX9f EmFZCUS8+trilqJbcsd/AQ9eOZLAB0BdKwIDAQAB -----END RSA PUBLIC KEY--- --
有两件事似乎是错误的。一是密钥与我可以从私钥中提取的密钥不同。另一个是我从私钥中提取的那个有这些注释:
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
没有'RSA'。这有关系吗?在任何情况下,它都无法验证。
最后,我是不是完全误解了 OpenID 流程?JWK 是否也是从我需要的私钥生成的,以验证我的 JWT?我是否应该在我的身份验证服务器上公开我自己的 JWK 以供其他服务联系和使用,而不是 Google 的?我对 Firebase Admin SDK 做什么和不做什么有点困惑,我想:-)
我知道这是很多问题,但我认为它们都是相关的。
我在研究中依赖的一些资源(当然除了官方的 admin sdk 文档):