首页 > 解决方案 > 创建 Firebase CustomToken 的 Javascript 函数

问题描述

我正在为 Firebase 创建一个自定义 TamperMonkey 脚本,以便在我的网站上进行一些本地测试插入给定 FireBase 身份验证用户的 UID 我为它创建了一个customToken

这是我当前的代码

// ==UserScript==
// @name         my-name
// @namespace    my-namespace
// @version      1.0
// @author       Myself
// @match        XXXX-my-website-XXXX
// @require      https://www.gstatic.com/firebasejs/6.6.2/firebase-app.js
// @require      https://www.gstatic.com/firebasejs/6.6.2/firebase-auth.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js
// @grant        none
// ==/UserScript==

(function() {
    function generateCustomToken(uid) {
        if(typeof CryptoJS === undefined) {
            alert("CryptoJS not found");
            return;
        }

        // https://console.firebase.google.com/project/my-project-id/settings/serviceaccounts/adminsdk
        var serviceAccount = { ... content of json service account file ... };

        // https://firebase.google.com/docs/auth/admin/create-custom-tokens#create_custom_tokens_using_a_third-party_jwt_library
        var header = {
            "alg": "RS256",
            "typ": "JWT"
        };

        var seconds = Math.trunc(new Date().getTime() / 1000);
        var payload = {
            iss : serviceAccount.client_email,
            sub : serviceAccount.client_email,
            aud : "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
            iat : seconds,
            exp : seconds + (60*60), // Maximum expiration time is one hour
            uid : uid,
            claims : {
                premium_account : false
            }
        };

        // https://github.com/firebase/php-jwt/blob/master/src/JWT.php#L156
        var segments = [];

        segments.push(base64url(CryptoJS.enc.Utf8.parse(JSON.stringify(header))));
        segments.push(base64url(CryptoJS.enc.Utf8.parse(JSON.stringify(payload))));

        var signingInput = segments.join(".");
        var secret = serviceAccount.private_key
                    .replace('-----END PRIVATE KEY-----', '') // remove head
                    .replace('-----BEGIN PRIVATE KEY-----', '') // remove tail
                    .replace(/\n/g, ''); // remove all new-line chars

        segments.push(base64url(CryptoJS.HmacSHA256(signingInput, secret)));
        return segments.join(".");
    }

    /**
    * https://codepen.io/jpetitcolas/pen/zxGxKN
    */
    function base64url(source) {
        // Encode in classical base64
        var encodedSource = CryptoJS.enc.Base64.stringify(source);

        // Remove padding equal characters
        encodedSource = encodedSource.replace(/=+$/, '');

        // Replace characters according to base64url specifications
        encodedSource = encodedSource.replace(/\+/g, '-');
        encodedSource = encodedSource.replace(/\//g, '_');

        return encodedSource;
    }

    function firebaseLogin(customToken) {
        // FIXME omitted code, firebase initialize

        firebase
            .auth()
            .signInWithCustomToken(customToken)
            .then(function(response) {
                alert("Success signInWithCustomToken");
            })
            .catch(function(error) {
                alert("signInWithCustomToken ERROR\n" + error.code + "\n" + error.message);
            });
    }
})();

我遵循了我找到的不同示例(我在代码中链接了它们)和使用自定义客户端的官方文档

我多次阅读代码,一切似乎都很好,但是对于给定的错误,我认为我错过了一些非常简单的东西,在生成 jwt 令牌时我无法看到。

POST https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=xxx-my-api-key-xxx
{"token":"xxx-generated-custom-token-xxx","returnSecureToken":true}

HTTP RESPONSE 400 
{
  "error": {
    "code": 400,
    "message": "INVALID_CUSTOM_TOKEN",
    "errors": [
      {
        "message": "INVALID_CUSTOM_TOKEN",
        "domain": "global",
        "reason": "invalid"
      }
    ]
  }
}

作为 TamperMonkey,这意味着直接在浏览器中运行,没有可用的 Node 环境或其他环境,这就是为什么我使用“原始”库而不是更集成的库的原因。

您在生成 jwt 令牌时是否看到任何错误?

标签: firebasefirebase-authenticationjwtgreasemonkeytampermonkey

解决方案


使用jsrsasign解决(它是https://jwt.io/网站建议的 JS 库之一)。我仍然不知道问题出在哪里,我认为与解析密钥有关。大多数代码现在由库管理,因此流程正常工作。

这是与令牌生成相关的更新代码:

添加到脚本标题:

 // @require      https://cdnjs.cloudflare.com/ajax/libs/jsrsasign/8.0.12/jsrsasign-all-min.js

以及创建jwt的功能

    var serviceAccount = { ... content of json service account file ... };

    // https://firebase.google.com/docs/auth/admin/create-custom-tokens#create_custom_tokens_using_a_third-party_jwt_library
    var header = {
        "alg": "RS256",
        "typ": "JWT"
    };

    var payload = {
        iss : serviceAccount.client_email,
        sub : serviceAccount.client_email,
        aud : "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
        iat : KJUR.jws.IntDate.get('now'),
        exp : KJUR.jws.IntDate.get('now + 1hour'),
        uid : uid
    };

    // https://github.com/kjur/jsrsasign/wiki/Tutorial-for-JWT-generation
    var sHeader = JSON.stringify(header);
    var sPayload = JSON.stringify(payload);

    var prvKey = KEYUTIL.getKey(serviceAccount.private_key);
    var sJWT = KJUR.jws.JWS.sign("RS256", sHeader, sPayload, prvKey);

    return sJWT;

推荐阅读