我有一个Chrome扩展程序,要求用户使用chrome.identity.getAuthToken路由进行登录。这样可以正常工作,但是当您登录时,您只能使用您在Chrome中拥有帐户的用户。
客户希望能够使用其他Google帐户登录;因此,他们希望能够使用.client@company.com登录,而不是使用Chrome登录的帐户.client @ gmail.com,这也是一个有效的Google帐户。
我可以使用一个帐户登录Chrome,使用第二个帐户登录Gmail,但我无法在扩展程序中选择。
这可能吗?
答案 0 :(得分:14)
不是使用chrome.identity.getAuthToken
对用户进行身份验证,而是自己实施OAuth部分。
您可以使用图书馆来帮助您,但我上次尝试使用最有用的库(Google API客户端)时,Chrome扩展程序无效。
查看Google OpenID Connect文档以获取更多信息。最后,您只需将用户重定向到OAuth网址,使用您的扩展程序获取Google的答案(授权代码),然后将授权代码转换为访问令牌(这是一个简单的POST调用)。
由于Chrome扩展程序无法重定向到网络服务器,因此您可以使用installed app重定向URI:urn:ietf:wg:oauth:2.0:oob
。有了这个,Google将显示一个包含授权码的页面。
只需使用您的扩展程序在此页面中注入一些javascript代码即可获取授权代码,关闭HTML页面,执行POST调用以获取用户的电子邮件。
答案 1 :(得分:0)
根据 David 的回答,我发现 chrome.identity
(以及通用 browser.identity
)API 现在提供了一个 chrome.identity.launchWebAuthFlow 方法,可用于启动 OAuth 工作流。以下是展示如何使用它的示例类:
class OAuth {
constructor(clientId) {
this.tokens = [];
this.redirectUrl = chrome.identity.getRedirectURL();
this.clientId = clientId;
this.scopes = [
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/gmail.compose",
"https://www.googleapis.com/auth/gmail.send"
];
this.validationBaseUrl = "https://www.googleapis.com/oauth2/v3/tokeninfo";
}
generateAuthUrl(email) {
const params = {
client_id: this.clientId,
response_type: 'token',
redirect_uri: encodeURIComponent(this.redirectUrl),
scope: encodeURIComponent(this.scopes.join(' ')),
login_hint: email
};
let url = 'https://accounts.google.com/o/oauth2/auth?';
for (const p in params) {
url += `${p}=${params[p]}&`;
}
return url;
}
extractAccessToken(redirectUri) {
let m = redirectUri.match(/[#?](.*)/);
if (!m || m.length < 1)
return null;
let params = new URLSearchParams(m[1].split("#")[0]);
return params.get("access_token");
}
/**
Validate the token contained in redirectURL.
This follows essentially the process here:
https://developers.google.com/identity/protocols/OAuth2UserAgent#tokeninfo-validation
- make a GET request to the validation URL, including the access token
- if the response is 200, and contains an "aud" property, and that property
matches the clientID, then the response is valid
- otherwise it is not valid
Note that the Google page talks about an "audience" property, but in fact
it seems to be "aud".
*/
validate(redirectURL) {
const accessToken = this.extractAccessToken(redirectURL);
if (!accessToken) {
throw "Authorization failure";
}
const validationURL = `${this.validationBaseUrl}?access_token=${accessToken}`;
const validationRequest = new Request(validationURL, {
method: "GET"
});
function checkResponse(response) {
return new Promise((resolve, reject) => {
if (response.status != 200) {
reject("Token validation error");
}
response.json().then((json) => {
if (json.aud && (json.aud === this.clientId)) {
resolve(accessToken);
} else {
reject("Token validation error");
}
});
});
}
return fetch(validationRequest).then(checkResponse.bind(this));
}
/**
Authenticate and authorize using browser.identity.launchWebAuthFlow().
If successful, this resolves with a redirectURL string that contains
an access token.
*/
authorize(email) {
const that = this;
return new Promise((resolve, reject) => {
chrome.identity.launchWebAuthFlow({
interactive: true,
url: that.generateAuthUrl(email)
}, function(responseUrl) {
resolve(responseUrl);
});
});
}
getAccessToken(email) {
if (!this.tokens[email]) {
const token = await this.authorize(email).then(this.validate.bind(this));
this.tokens[email] = token;
}
return this.tokens[email];
}
}
免责声明:以上类基于开源sample code from Mozilla Developer Network。
用法:
const clientId = "YOUR-CLIENT-ID"; // follow link below to see how to get client id
const oauth = new OAuth();
const token = await oauth.getAccessToken("sample@gmail.com");
当然,您需要自己处理令牌的过期情况,即当您从 Google 的 API 获取 401 时,移除令牌并再次尝试授权。
A complete sample extension using Google's OAuth can be found here。