是否仍然可以在 Firebase 3 中对令牌进行服务器端验证?
Posted
技术标签:
【中文标题】是否仍然可以在 Firebase 3 中对令牌进行服务器端验证?【英文标题】:Is it still possible to do server side verification of tokens in Firebase 3? 【发布时间】:2016-09-21 09:04:56 【问题描述】:我们使用我们现有的身份验证系统(使用服务帐户)在运行 Golang 的服务器上生成自定义令牌 (JWT)。 令牌在 ios 客户端上使用
FIRAuth.auth()?.signInWithCustomToken(customToken)
直到那里一切正常。但是当我们将客户端令牌传递给从以下位置检索的服务器时:
FIRUser.getTokenWithCompletion( token, error in ..)
我们无法验证。 JWT 令牌是使用 RS256 签名的,并且有一个我们无法识别的 header.kid。服务帐户的公钥(用于签署自定义令牌)不会验证客户端令牌。 验证客户端令牌所需的公钥是否可用?
我知道可以使用 Java 或 javascript 中的“verifyIdToken”调用来验证客户端令牌,但我们希望仍然能够使用标准 JWT 库在 Golang 中执行此操作。
这一切在 Firebase 2 中运行良好(使用 HS256 和 Firebase 机密)。
【问题讨论】:
嗨,蒂姆,等一下。我现在已经记录了大部分内容。今天下午和专家讨论了一些细节,然后我会在这里发表一个相当权威的回复。 谢谢加藤,很高兴听到这个消息! 我已经更新了答案并添加了一些 Go 解决方案。我将努力将其纳入其他人的官方文档中。 非常感谢加藤!比我希望的要多得多,您的回答确实有助于选择正确的策略来安全地与服务器逻辑交互。 查看***.com/questions/38188122/… 【参考方案1】:好吧,firebase 不允许验证 custom tokens
他们所做的是允许验证 id tokens
一旦用户使用自定义令牌登录它们就会生成。
就像我的情况一样,如果您将 firebase 自定义令牌传递给其他服务以通过您的后端进行身份验证,那么自定义令牌验证责任就在您身上!
此外,谷歌服务帐户X509 cert
格式不正确。它有这些\n (new line)
分隔符,在文本编辑器中不会被新行替换(我使用vim
)。因此,我所做的是:
val factory = CertificateFactory.getInstance("X.509")
val certificateFile = this.getClass.getResourceAsStream(Play.current.configuration.getString("firebase.certificate").get)
val publicKey = factory.generateCertificate(certificateFile).asInstanceOf[X509Certificate].getPublicKey
val claimBody = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(compactJws).getBody
-
从设置 firebase 时下载的 json 中指定的 google 服务帐户链接获取证书
手动将
\n
替换为新行
获取 JWT 库。我用这个很棒的图书馆做验证Java JWT
读取证书并提取公钥。
使用公钥验证令牌
确保您有另一个 api 来刷新令牌,因为令牌仅在一个小时内有效
希望有帮助!
【讨论】:
【参考方案2】:简短的回答是肯定的。完整的答案是,在大多数情况下,我们现在有一个更合适的工具。因此,很大程度上取决于您要解决的用例。
新版 SDK 的功能要强大一些,但我们在功能总结方面做得还不够好。这似乎是一个对比可用工具及其用途的好地方,然后我将在最后添加一些第三方(即 Go)特定说明。
使用外部身份验证工具进行客户端身份验证
铸造自定义令牌的主要用途是允许用户根据您控制的外部/旧版身份验证机制(例如您的 LDAP 服务器)进行身份验证。此处介绍了基本流程:iOS、android、Web。
本质上,您的服务只是铸造 JWT 令牌并将其传递给客户端。客户端使用您提供的自定义令牌进行验证/身份验证。
验证您的特权工人
不再需要使用自定义令牌来验证您的服务器进程。这是通过创建服务帐户来完成的,Adding Firebase to your Server 中将逐步介绍该服务帐户。完成后,您将得到一个包含私钥的 JSON 文件。
然后,您通过使用firebase.initializeApp()
中的serviceAccount
属性引用该JSON 来包含您的服务帐户凭据,然后您就可以了!这已记录在 here 中,看起来像这样(参见 Java 版本的链接):
var firebase = require("firebase");
// Initialize the app with a service account, granting admin privileges
firebase.initializeApp(
databaseURL: "https://databaseName.firebaseio.com",
serviceAccount: "./serviceAccountCredentials.json"
);
模拟用户或限制来自服务器进程的访问
模拟用户或限制来自服务器进程的访问(强烈推荐)相当简单。您不再需要为此创建自定义令牌。
这只需将databaseAuthVariableOverride
添加到您对database.initializeApp()
的调用中:
firebase.initializeApp(
databaseURL: "https://databaseName.firebaseio.com",
serviceAccount: "./serviceAccountCredentials.json",
databaseAuthVariableOverride:
uid: "my-service-worker-or-user-uid"
);
通过安全验证客户身份
首先,如果您使用 Firebase 数据库,通常可以避免处理服务器端验证,方法是让您的客户端写入数据库并使用安全规则来验证其身份。如果您的服务器侦听需要身份验证才能写入的路径,那么在服务器没有任何特殊安全性的情况下,这已经解决了。
通过将其建模为事件队列,它创建了一个简单、模块化且可扩展的服务器工作者策略。有关一些出色的 Node.js 工具,请参阅 firebase-queue。它supports 3.x。
验证服务器上的客户端 ID 令牌
如果您没有使用实时数据库并且需要接收客户端令牌(例如通过 REST 调用)并验证它们是否有效,您可以使用verifyIdToken()
来完成,如here 所述。如下所示:
auth.verifyIdToken(idToken).then(function(decodedToken)
var uid = decodedToken.sub;
);
如果您想以该用户身份进行身份验证以写入数据库并实施安全性,您将使用上面的模拟用户部分。换句话说,调用initializeApp()
并将databaseAuthVariableOverride
设置为适当的uid。
请注意,如果您尝试多次调用 initializeApp()
并遇到类似以下的错误:Error: Firebase App named '[DEFAULT]' already exists.
您可以通过在 initializeApp() 调用中添加第二个参数来初始化多个应用程序上下文(例如 @987654344 @),然后使用 firebase.database('asUser'+uid)
.ref(...) 引用该应用程序实例。要了解有关使用多个应用实例的更多信息,请look here。
上面的链接中提供了 Java 代码。 Go 和下面介绍的其他第三方解决方案。
创建用于 REST API 的令牌
Michael Bleigh 报道了这个场景 here 并值得一些代表来解决这个问题。
创建令牌或通过 REST 验证它们
这不受支持。对不起。
Golang 和其他语言:更多内容
我们正在开发 Go 令牌铸造和验证库。我们也将很快为此添加 Python 工具。没有发布日期或球场。同时,如果您想在不使用官方 Firebase Node.js 或 Java 库(具有内置验证方法)的情况下验证客户端 ID 令牌,则需要确保 ID 令牌(即 JWT)符合以下条件:
其解码后的标头具有等于"RS256"
的alg
(算法)声明。
其解码后的负载具有与您的 Firebase 项目 ID 相同的 aud
(受众)声明。
其解码后的有效载荷有一个iss
(颁发者)声明等于"https://securetoken.google.com/<projectId>"
。
其解码的有效负载有一个非空字符串sub
(主题)声明。请注意,这是该 Firebase 用户的 uid
。
其解码的标头有一个kid
(密钥ID)声明,对应于https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com
中列出的公钥之一。
您还需要使用 JWT 库来使用公钥验证令牌,以证明令牌是使用公钥对应的私钥签署的。
对于 Go,您似乎可以使用 jwt-go
来解码和验证客户端 ID 令牌。
【讨论】:
我们应该多久检查一次公钥?它们是否每天都在更换,就像每个太平洋夏令时间午夜一样? 测试怎么样,我生成了一个令牌(自定义,所以我可以在 fb 3x 中设置过期时间),我很想知道它在服务器端测试中有效。 很烦人,其中 99% 用于 SDK 或其他自定义 JS 库,而不用于 REST API。 认为您的 SDK 将涵盖所有可能的服务器端语言是非常天真的。Creating tokens or verifying them via REST: This isn't supported. Sorry.
这是为什么呢?以后会支持吗?以上是关于是否仍然可以在 Firebase 3 中对令牌进行服务器端验证?的主要内容,如果未能解决你的问题,请参考以下文章
我是否可以使用客户端登录中的Firebase ID令牌来使用Java SDK对Java桌面应用程序进行身份验证?
通过 laravel API 使用 firebase 令牌身份验证