【发布时间】:2021-08-25 09:47:50
【问题描述】:
我正在通过 Apps Script 构建一个 google hangout bot 并对 google apis 进行了几次调用,我的问题是,当我认为我已正确设置它以调用 Google APIs 时,bot 当前请求 bot 用户的权限代表服务帐户而不是用户。
我在这里遵循了 google 提供的指导:https://developers.google.com/identity/protocols/oauth2/service-account
- 我创建了一个短暂的 JWT 并验证了 https://jwt.io/ 的签名
- 然后我向https://oauth2.googleapis.com/token 发出一个post 请求,访问令牌包含在我通过https://www.googleapis.com/oauth2/v3/tokeninfo 验证的响应中。
回复是:
{
"azp": "XXXXXX", // Google service account id
"aud": "XXXXXX", // Google service account id
"scope": "https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/chat.bot https://www.googleapis.com/auth/script.external_request",
"exp": "1629819391",
"expires_in": "3569",
"access_type": "online"
}
- 然后我使用google访问令牌调用api
代码如下:
/*
* 1. BUILD RS256 JWT: HEADER, CLAIM SET, SIGNATURE
*/
const createJwt = ({ privateKey, expiresInHours, input = {} }) => {
/*
* The only signing algorithm supported by the Google OAuth 2.0 Authorization Server is RSA using SHA-256 hashing algorithm.
* This is expressed as RS256 in the alg field in the JWT header.
*/
const header = {
"alg":"RS256",
"typ":"JWT",
"kid": "XXXXXX", // service account private_key_id
"x5c": SERVICE_ACCOUNT_PUBLIC_KEY
};
const now = Date.now();
const expires = new Date(now);
expires.setHours(expires.getHours() + expiresInHours);
/*
* iat: issued time
* exp: expiration time
*/
const payload = {
iat: Math.round(now / 1000),
exp: Math.round(expires.getTime() / 1000)
};
/* add payload / claim set */
Object.keys(input).forEach(function (key) {
payload[key] = input[key];
});
const base64Encode = (text, json = true) => {
const input = json ? JSON.stringify(text) : text;
return Utilities.base64EncodeWebSafe(input).replace(/=+$/, '');
};
/* encode using the Base64url encoding */
const toSign = `${base64Encode(header)}.${base64Encode(payload)}`;
/*
* Sign the UTF-8 representation of the input using SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function)
* with the private key obtained from the Google API Console.
*/
const signatureBytes = Utilities.computeRsaSha256Signature(
toSign,
privateKey
);
/* The signature must then be Base64url encoded */
const signature = base64Encode(signatureBytes, false);
return `${toSign}.${signature}`;
};
/*
* 2. SET JWT SCOPES & EXPIRY DATE
* must be a short lived access token e.g. 1 hour, or else oauth2.googleapis.com/token throws an error
*/
const generateAccessToken = () => {
const privateKey = SERVICE_ACCOUNT_PRIVATE_KEY;
const scopes = ["https://www.googleapis.com/auth/script.external_request", "https://www.googleapis.com/auth/chat.bot", "https://www.googleapis.com/auth/calendar.readonly"];
const accessToken = createJwt({
privateKey,
expiresInHours: 1,
input: {
iss: SERVICE_ACCOUNT_EMAIL,
scope: scopes.join(" "),
aud: "https://oauth2.googleapis.com/token",
},
});
Logger.log(accessToken);
return accessToken;
};
/*
* 3. GENERATE JWT
*/
const jwt = generateAccessToken();
/*
* 4. USE JWT TO GET GOOGLE ACCESS TOKEN FROM THE GOOGLE AUTHORIZATION SERVER
*/
const getGoogleAccessToken = () => UrlFetchApp.fetch("https://oauth2.googleapis.com/token", {
method: "post",
contentType: "application/x-www-form-urlencoded",
payload: "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=" + jwt
});
调用api示例
/**
* Posts a message into the given space ID via the API,
* using service account authentication.
*/
function postMessage(spaceId, message) {
const CHAT_SPACE_URL = 'https://chat.googleapis.com/v1/spaces/' + spaceId + '/messages';
Logger.log(JSON.parse(getGoogleAccessToken()));
UrlFetchApp.fetch(CHAT_SPACE_URL, {
method: 'post',
headers: { 'Authorization': 'Bearer ' + JSON.parse(getGoogleAccessToken()).access_token },
contentType: 'application/json',
payload: JSON.stringify(message),
});
}
非常感谢我出错的任何帮助:)
【问题讨论】:
-
您是否删除了令牌文件并重新创建了它?
-
@RafaGuillermoI 我确实重命名了生成令牌的文件,在此过程中是否需要执行另一个步骤才能实施更新?
标签: google-apps-script google-api authorization service-accounts