【问题标题】:Firebase Custom Authentication in .NET gives "The custom token format is incorrect".NET 中的 Firebase 自定义身份验证给出“自定义令牌格式不正确”
【发布时间】:2016-10-13 06:26:35
【问题描述】:

我们正在尝试设置自定义身份验证,以便 Android 应用程序可以利用我们现有的身份验证系统来访问 Firebase 数据库。

移动应用根据 .NET WebAPI 端点进行身份验证。我们在响应中发回一个 Firebase 身份验证令牌,然后应用可以使用该令牌在 Firebase SDK 中进行身份验证。

根据文档,我们认为这将非常简单。使用来自 Nuget 的 FirebaseTokenGenerator 包,我们从 Firebase 控制台 > 项目 > 管理 > 数据库 > 数据库机密中找到的机密生成此令牌。

但是,当我们使用此处生成的令牌时,我们发现 Android SDK 会返回此异常:

com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The custom token format is incorrect. Please check the documentation.
at com.google.android.gms.internal.zzafd.zzes(Unknown Source)
at com.google.android.gms.internal.zzafa$zzg.zza(Unknown Source)
at com.google.android.gms.internal.zzafl.zzet(Unknown Source)
at com.google.android.gms.internal.zzafl$zza.onFailure(Unknown Source)
at com.google.android.gms.internal.zzafg$zza.onTransact(Unknown Source)

查看文档让我更加困惑,因为不清楚问题可能是什么。似乎有很多候选解决方案,但我无法弄清楚哪个是正确的。以下是文档中讨论的一些内容:

  • 创建服务帐户,下载 JWT json 文件,包括 .NET WebApi 项目中的文件以及如何将其与 FirebaseTokenGenerator 一起使用
  • 使用第 3 方 JWT 库从密钥生成令牌

这两种策略似乎在同一件事上,但似乎没有任何关系。

我是否使用了错误的秘密?我是否需要对我的 WebAPI 项目进行更多设置以使其生成正确的令牌?

作为参考,这是生成我的令牌的方法:

public string GetSignedFirebaseAuthToken(Agent agent, DateTime expires)
{
    var tokenGenerator = new Firebase.TokenGenerator(BusinessLogicResources.FirebaseSecret);
    var authPayload = new Dictionary<string, object>()
    {
        { "uid", agent.AgentId.ToString() },
        { "UserName", agent.FullName },
        { "AgencyId", agent.AgencyId },
        { "AgencyName", agent.AgencyName }
    };
    return tokenGenerator.CreateToken(authPayload, new Firebase.TokenOptions(expires:expires));
}

更新: 这是我用来铸造我的 firebase 令牌的新代码。

public string GetSignedFirebaseAuthToken(Agent agent, DateTime expires)
    {
        // I set the expiry time to 3600 because this is the maximum number of seconds
        // allowed according the the Firebase documenation.
        var expiryTime = 3600;
        // This claims object contains some additional information that Telenotes would
        // like to track for each user of Firebase. This is used for reporting and
        // security rules in Firebase.
        var claims = new Dictionary<string, object>()
        {
            { "UserName", agent.FullName },
            { "AgencyId", agent.AgencyId.ToString() },
            { "AgencyName", agent.AgencyName }
        };
        // In order for the cryptography algorithms to be happy, I need to put our
        // keys into stream objects.
        var memoryStream = new MemoryStream();
        var streamWriter = new StreamWriter(memoryStream);
        streamWriter.Write(BusinessLogicResources.FirebaseSecret.Replace(@"\n", "\n"));
        streamWriter.Flush();
        memoryStream.Position = 0;
        var streamReader = new StreamReader(memoryStream);
        // BouncyCastle takes care of the cryptography stuff, so we'll hand our
        // secret over to BouncyCastle to read and encode.
        var bouncyCastleReader = new PemReader(streamReader);
        var rsaParams = (RsaPrivateCrtKeyParameters) bouncyCastleReader.ReadObject();
        // Now that the secret is packed up all nicely in rsaParams, I need to
        // put together the rest of the payload that Firebase needs in my auth token.
        var tokenPayload = new Dictionary<string, object>()
        {
            { "claims", claims },
            { "uid", agent.AgentId },
            { "iat", (DateTime.Now).SecondsSinceEpoch() },
            { "exp", (DateTime.Now).AddSeconds(expiryTime).SecondsSinceEpoch() },
            { "aud", BusinessLogicResources.FirebasePayloadAud },
            { "iss", BusinessLogicResources.FirebasePayloadISS },
            { "sub", BusinessLogicResources.FirebasePayloadSUB },
            { "alg", "RS256" }
        };

        // Lastly, we need to put it all together using JOSE JWT (see https://github.com/dvsekhvalnov/jose-jwt)
        // and BouncyCastle encoding magic.
        var rsaKey = DotNetUtilities.ToRSA(rsaParams);
        return JWT.Encode(tokenPayload, rsaKey, JwsAlgorithm.RS256);
    }

【问题讨论】:

  • 由于您使用的是 .NET,因此您正在寻找 Create custom tokens using a third-party JWT library。文档的其他部分适用于受支持的 SDK。 BusinessLogicResources.FirebasePayload* 的值是多少?您如何验证这些与您的服务帐户电子邮件、身份 URL 等完全匹配?您能否以模糊的方式分享这些值以帮助验证?
  • 另外,agent.AgentId 的值是多少?错误在哪里产生?当您尝试调用身份验证时,在客户端?您如何验证那里使用的令牌与您在此处输出的内容相匹配?这里的基本方法看起来是正确的,因为我了解任何 .NET,所以它可能在细节中。
  • 错误是在客户端生成的,在 Android 应用程序中。我们已经验证了此处输出的令牌正确地通过线路到达客户端。
  • agent.AgentId 是一个 int 字段。如果这些信息对您来说还不够,请告诉我。
  • BusinessLogicResources.FirebasePayload 值... iss and sub: xxx@xxx.iam.gserviceaccount.com

标签: asp.net-web-api firebase firebase-authentication


【解决方案1】:

需要理解的几个关键点:

问题 #1

我在项目中使用 FirebaseTokenGenerator 2.0.0。如果您查看 the GitHub project,它会明确指出此软件包与 Firebase 3.x.x 不兼容。

因此,如果您尝试在 .NET 中使用现有的身份验证系统,则需要使用第 3 方来整合您的 Firebase 令牌。在this post 上可以找到一个很好的例子来说明如何做到这一点。

如果您不关心 BouncyCastle 或 JOSE JWT(可帮助您从 Firebase 机密中编码令牌的库),您可以查看 Google 提供的规范 here 并找到另一种方法来使这一切都适用你的系统。

问题 #2

在上述过程中使用的正确令牌不是您的数据库密码。 Firebase 团队希望您使用服务帐户设置您的服务器。有关如何完成此操作的详细信息,请参阅 here

从这个过程中,您应该得到一个 JSON 文档(作为文件下载)。在此 JSON 文档中,您将找到一个名为“private_key_id”的属性。尽管它看起来有点古怪 ("-----BEGIN PRIVATE KEY-----\n..."),但在上面提到的密钥生成过程中,您需要这整个过程。所以不需要任何字符串解析或类似的东西。您还需要该文档中的其他值,但这些详细信息已在已提供的链接中列出。

听起来事情不会永远这样下去,一个不错的库将在接下来的几个月左右出现,以使这个过程更加简单。

【讨论】:

    猜你喜欢
    • 2016-12-04
    • 2017-02-16
    • 2023-03-12
    • 2017-12-19
    • 2016-11-06
    • 2021-05-06
    • 1970-01-01
    • 2017-07-03
    • 2020-04-01
    相关资源
    最近更新 更多