是否仍然可以在 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 令牌身份验证

Firebase 身份验证:如何验证用户仍然存在

FCM 主题缓存在旧令牌上 - 用户仍然收到推送

在Firebase安全规则中是否可以验证用户的身份验证令牌?

是否可以从服务器(Java)验证 Firebase 令牌(JWT)