通过简单的HttpCall获取Firebase Bearer令牌(Postman)

pkmbmrz7  于 2023-04-13  发布在  Postman
关注(0)|答案(2)|浏览(198)

我目前面临以下情况。
通过Google API端点通过HttpCall发送Firebase消息:

https://fcm.googleapis.com/v1/projects/projectName/messages:send

在这里,我们必须将OAuth2.0与有效的承载令牌一起使用,如本问题中所述:
What Bearer token should I be using for Firebase Cloud Messaging testing?
遵循这些步骤后,我能够通过Google API发送Firebase消息。
现在我想通过HttpCall获取承载令牌,而不需要使用Playground https://developers.google.com/oauthplayground执行手动步骤。
我找不到任何关于如何通过简单的HttpCall“交换令牌的授权代码”的文档。我不可能实现任何代码,因为我想在“云流”中发送Firebase消息,因此不可能加载任何外部DLL(如Firebase Admin Dll,它将实现此功能)。
任何帮助都很感激

v09wglhw

v09wglhw1#

下面的代码是一个Postman Pre-Request Script,它安装在你的API集合中,包含你正在测试的路由。它的目的是将静态凭证(如电子邮件密码组合或服务帐户密钥)转换为API调用所使用的访问令牌。

模拟用户

要使用它来代表用户进行测试,您需要在请求上添加一个X-Auth-Token-Type: user头(由下面的脚本使用和删除),并且您需要设置以下环境变量:
| 姓名|价值|
| --------------|--------------|
| firebase_apiKey|Firebase API Key for a web application|
| firebase_test_user|用于测试的帐户的电子邮件|
| firebase_test_password|用于测试的帐户的密码|

模拟服务账号(慎用!)

要代表服务帐户使用它进行测试,您需要在请求上添加X-Auth-Token-Type: admin头(由下面的脚本使用和删除),并且您需要设置以下环境变量:
| 姓名|价值|
| --------------|--------------|
| firebase_privateKey|private_keyService Account Key中的值**重要提示:**为了安全起见,请不要为该变量设置“初始值”!|
| firebase_scope(可选)|要对其进行身份验证的作用域列表,以空格分隔。**注意:**如果省略,则使用默认的Admin SDK作用域|

预请求脚本

const { Header, Response, HeaderList } = require('postman-collection');

/**
 * Information about the current Firebase user
 * @typedef {Object} UserInfo
 * @property {String} accessToken - The Firebase ID token for this user
 * @property {String | undefined} displayName - Display name of the user, if available
 * @property {Number} expiresAt - When this token expires as a unix timestamp
 * @property {String | undefined} email - Email associated with the user, if available
 * @property {String} refreshToken - Refresh token for this user's ID token
 * @property {String} uid - User ID for this user
 */

/**
 * Loads a third-party JavaScript module from a CDN (e.g. unpkg, jsDelivr)
 * @param {[String, String, String]} moduleTuple - Array containing the module's ID, its source URL and an optional SHA256 signature
 * @param {Object | (err: any, exports: any) => any} exportsRefOrCallback - Object reference to use as `exports` for the module or a result handler callback
 * @param {(err: any, exports: any) => any} callback - result handler callback
 */
function loadModule(moduleTuple, exportsRefOrCallback, callback = undefined) {
    const exports = arguments.length == 2 ? {} : exportsRefOrCallback;
    callback = arguments.length == 2 ? exportsRefOrCallback : callback;
    const [id, src, signature] = moduleTuple;
   
    if (pm.environment.has("jslibcache_" + id)) {
        const script = pm.environment.get("jslibcache_" + id);

        if (signature && signature === CryptoJS.SHA256(script).toString()) {
            console.log("Using cached copy of " + src);
            try {
              eval(script);
              return callback(null, exports);
            } catch {}
        }
    }

    pm.sendRequest(src, (err, response) => {
        try {
            if (err || response.code !== 200) {
                pm.expect.fail('Could not load external library');
            }

            const script = response.text();
            signature && pm.expect(CryptoJS.SHA256(script).toString(), 'External library (' + id + ') has a bad SHA256 signature').to.equal(signature);
            pm.environment.set("jslibcache_" + id, script);
            eval(script);

            callback(null, exports);
        } catch (err) {
            callback(err, null);
        }
    });
}

/**
 * Signs in a test user using an email and password combination
 * 
 * @param {String} email email of the account to sign in with
 * @param {String} password email of the account to sign in with
 * @param {(error: any, response: Response) => any} callback request result handler
 */
function signInWithEmailAndPassword(email, password, callback) {
    pm.sendRequest({
        url: "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=" + encodeURIComponent(pm.environment.get("firebase_apiKey")),
        body: JSON.stringify({ email, password, "returnSecureToken": true }),
        headers: new HeaderList({}, [new Header("application/json", "Content-Type")]),
        method: "POST"
    }, callback);
}

/**
 * Builds an Admin SDK compatible JWT using a Service Account key
 * 
 * Required Environment Variables:
 *  - `firebase_privateKey` - the private key from inside a service account key JSON file
 * 
 * Environment Variables:
 *  - `firebase_scope` - scopes used for the access token, space delimited
 * 
 * @param {Boolean | (error: any, idToken: String) => any} callbackOrForceRefresh token result handler or `true` to force using a fresh user token
 * @param {(error: any, idToken: String) => any} [callback] token result handler
 */
function getAdminToken(callbackOrForceRefresh, callback) {
    let forceRefresh = Boolean(callbackOrForceRefresh);
    if (arguments.length === 1) {
        callback = callbackOrForceRefresh;
        forceRefresh = callbackOrForceRefresh = false;
    }

    loadModule(
        ["jsrsasign", "https://unpkg.com/jsrsasign@10.3.0/lib/jsrsasign.js", "39b7a00e9eed7d20b2e60fff0775697ff43160e02e5276868ae8780295598fd3"],
        (loadErr, { KJUR }) => {
            if (loadErr) return callback(loadErr, null);
            
            const exp = pm.environment.get("currentAdmin.exp");
            const nowSecs = Math.floor(Date.now() / 1000);

            if (exp && exp > nowSecs && forceRefresh === false) {
                return callback(null, pm.environment.get("currentAdmin.jwt"));
            }

            try {
                if (!pm.environment.has('firebase_privateKey')) {
                    pm.expect.fail('Missing required environment variable "firebase_privateKey".');
                }

                // use specified scopes, or fallback to Admin SDK defaults
                const scope = pm.environment.get('firebase_scope') || 'https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/firebase.database https://www.googleapis.com/auth/firebase.messaging https://www.googleapis.com/auth/identitytoolkit https://www.googleapis.com/auth/userinfo.email';
                const privateKey = String(pm.environment.get('firebase_privateKey')).replace("\\n", "\n");

                const header = {"alg" : "RS256", "typ" : "JWT"};
                
                const claimSet =
                {
                    "iss": "https://securetoken.google.com/" + pm.environment.get("firebase_projectId"),
                    "scope": scope,
                    "aud":"https://accounts.google.com/o/oauth2/auth",
                    "exp": nowSecs + 3600, // now + 1 hour
                    "iat": nowSecs
                }

                const jwt = KJUR.jws.JWS.sign(null, header, claimSet, privateKey);
                
                // comment these lines out to disable caching
                pm.environment.set("currentAdmin.jwt", jwt);
                pm.environment.set("currentAdmin.exp", claimSet.exp);

                callback(null, jwt);
            } catch (err) {
                callback(err, null);
            }
        }
    );
}

/**
 * Builds a User ID Token using an email-password combo
 * 
 * Required Environment Variables:
 *  - `firebase_apiKey` - the Firebase API key for a web application
 *  - `firebase_test_user` - an email for a test user
 *  - `firebase_test_password` - the password for the test user
 * 
 * @param {Boolean | (error: any, idToken: String) => any} callbackOrForceRefresh token result handler or `true` to force using a fresh user token
 * @param {(error: any, idToken: String) => any} [callback] token result handler
 */
function getIdToken(callbackOrForceRefresh, callback) {
    let forceRefresh = Boolean(callbackOrForceRefresh);
    if (arguments.length === 1) {
        callback = callbackOrForceRefresh;
        forceRefresh = callbackOrForceRefresh = false;
    }

    if (pm.environment.has("currentUser") && forceRefresh === false) {
        /** @type UserInfo */
        const currentUser = JSON.parse(pm.environment.has("currentUser"));
        if (currentUser.expiresAt > Date.now()) { // has token expired?
            return callback(null, currentUser.accessToken);
        }
    }

    try {
        if (!pm.environment.has('firebase_apiKey')) {
            pm.expect.fail('Missing required environment variable "firebase_apiKey".');
        }
        if (!pm.environment.has('firebase_test_user')) {
            pm.expect.fail('Missing required environment variable "firebase_test_user".');
        }
        if (!pm.environment.has('firebase_test_password')) {
            pm.expect.fail('Missing required environment variable "firebase_test_password".');
        }
    } catch (err) {
        return callback(err, null);
    }

    signInWithEmailAndPassword(pm.environment.get("firebase_test_user"), pm.environment.get("firebase_test_password"), (err, response) => {
        if (err || response.code !== 200) {
            pm.expect.fail('Could not sign in user: ' + response.json().error.message);
        }

        /** @type String */
        let accessToken;

        try {
            const { idToken, refreshToken, email, displayName, localId: uid, expiresIn } = response.json();
            accessToken = idToken;
            const expiresAt = Date.now() + Number(expiresIn);

            // comment these lines out to disable caching
            pm.environment.set("currentUser", JSON.stringify({ accessToken, refreshToken, email, displayName, uid, expiresAt }));
            // pm.environment.set("currentUser.accessToken", accessToken);
            // pm.environment.set("currentUser.refreshToken", refreshToken);
            // pm.environment.set("currentUser.email", email);
            // pm.environment.set("currentUser.displayName", displayName);
            // pm.environment.set("currentUser.uid", uid);
            // pm.environment.set("currentUser.expiresAt", expiresAt);

        } catch (err) {
            return callback(err, null);
        }

        callback(null, accessToken);
    });
}

const tokenTypeHeader = pm.request.headers.one("X-Auth-Token-Type");
pm.request.removeHeader("X-Auth-Token-Type");

switch (tokenTypeHeader && tokenTypeHeader.value.toLowerCase()) {
    case "admin":
        getAdminToken(false, (err, token) => { 
            if (err || !token) pm.expect.fail("failed to get admin SDK token for request: " + err.message);
            pm.request.addHeader(new Header("Bearer " + token, "Authorization"));
        });
        break;
    case "user":
        getIdToken(false, (err, idToken) => {
            if (err || !idToken) pm.expect.fail("failed to get user ID token for request: " + err.message);
            pm.request.addHeader(new Header("Bearer " + idToken, "Authorization"));
        });
        break;
    default:
        break; // no auth, do nothing
}
5uzkadbs

5uzkadbs2#

您可以使用您的firebase服务帐户从OAuth访问令牌获得有效的承载令牌。使用您的Service Account凭据从您的Firebase控制台。如果在您的环境中可能,我建议使用OAuth 2选项,您可以在这里找到:https://firebase.google.com/docs/database/rest/auth#authenticate_with_an_access_token
否则,您将不得不铸造凭证,这将提供一个访问令牌,这将是一个有效的承载令牌。
需要注意的是,这仅提供以下语言版本:

  • node.js
  • Python
  • java

https://firebase.google.com/docs/cloud-messaging/auth-server#use-credentials-to-mint-access-tokens

相关问题