通过简单的 HttpCall (Postman) 获取 Firebase Bearer 令牌
Getting Firebase Bearer token by simple HttpCall (Postman)
我目前面临以下情况。
通过 google API 端点通过 HttpCall 发送 Firebase 消息:
https://fcm.googleapis.com/v1/projects/projectName/messages:send
这里我们必须使用 OAuth2.0 和有效的 Bearer Token,就像这个问题中讨论的那样:
What Bearer token should I be using for Firebase Cloud Messaging testing?
完成这些步骤后,我能够通过 google API.
发送 Firebase 消息
现在我想通过 HttpCall 获取 Bearer Token,而无需使用 Playground https://developers.google.com/oauthplayground。
我找不到任何关于如何通过简单的 HttpCall“交换令牌的授权代码”的文档。我无法实现任何代码,因为我想在“云流”中发送 Firebase 消息,因此无法加载任何外部 DLL(如 Firebase Admin Dll,它将实现此功能)。
感谢任何帮助
您可以使用您的 firebase 服务帐户从 OAuth 访问令牌中获取有效的不记名令牌。使用您的 Service Account credentials from your Firebase console. If it is at all possible within your environment, I suggest using the OAuth 2 options than you can find here: 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
下面的代码是安装在 API collection 上的 Postman Pre-Request Script,其中包含您正在测试的路由。其目的是将静态凭据(如 email-password 组合或服务帐户密钥)转换为访问令牌以用于 API 调用。
模拟用户
要代表用户使用它进行测试,您需要在请求中添加 X-Auth-Token-Type: user
header(由下面的脚本使用和删除)并且您需要设置following environment variables:
Name
Value
firebase_apiKey
The Firebase API Key for a web application
firebase_test_user
An email for an account used for testing
firebase_test_password
A password for an account used for testing
模拟服务帐户(谨慎使用!)
要代表服务帐户使用它进行测试,您需要在请求中添加 X-Auth-Token-Type: admin
header(由下面的脚本使用和删除)并且您需要设置上 following environment variables:
Name
Value
firebase_privateKey
The value of private_key
in a Service Account Key
Important: For security do not set the "initial value" for this variable!
firebase_scope
(optional)
A space-delimited list of scopes to authenticate for.
Note: If omitted, the default Admin SDK scopes are used
Pre-Request 脚本
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"));
});
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
}
我目前面临以下情况。
通过 google API 端点通过 HttpCall 发送 Firebase 消息:
https://fcm.googleapis.com/v1/projects/projectName/messages:send
这里我们必须使用 OAuth2.0 和有效的 Bearer Token,就像这个问题中讨论的那样:
What Bearer token should I be using for Firebase Cloud Messaging testing?
完成这些步骤后,我能够通过 google API.
发送 Firebase 消息现在我想通过 HttpCall 获取 Bearer Token,而无需使用 Playground https://developers.google.com/oauthplayground。
我找不到任何关于如何通过简单的 HttpCall“交换令牌的授权代码”的文档。我无法实现任何代码,因为我想在“云流”中发送 Firebase 消息,因此无法加载任何外部 DLL(如 Firebase Admin Dll,它将实现此功能)。
感谢任何帮助
您可以使用您的 firebase 服务帐户从 OAuth 访问令牌中获取有效的不记名令牌。使用您的 Service Account credentials from your Firebase console. If it is at all possible within your environment, I suggest using the OAuth 2 options than you can find here: 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
下面的代码是安装在 API collection 上的 Postman Pre-Request Script,其中包含您正在测试的路由。其目的是将静态凭据(如 email-password 组合或服务帐户密钥)转换为访问令牌以用于 API 调用。
模拟用户
要代表用户使用它进行测试,您需要在请求中添加 X-Auth-Token-Type: user
header(由下面的脚本使用和删除)并且您需要设置following environment variables:
Name | Value |
---|---|
firebase_apiKey |
The Firebase API Key for a web application |
firebase_test_user |
An email for an account used for testing |
firebase_test_password |
A password for an account used for testing |
模拟服务帐户(谨慎使用!)
要代表服务帐户使用它进行测试,您需要在请求中添加 X-Auth-Token-Type: admin
header(由下面的脚本使用和删除)并且您需要设置上 following environment variables:
Name | Value |
---|---|
firebase_privateKey |
The value of private_key in a Service Account KeyImportant: For security do not set the "initial value" for this variable! |
firebase_scope (optional) |
A space-delimited list of scopes to authenticate for. Note: If omitted, the default Admin SDK scopes are used |
Pre-Request 脚本
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"));
});
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
}