使用Chrome以外的Google用户登录Chrome扩展程序

时间:2015-02-23 14:24:33

标签: google-chrome google-chrome-extension google-oauth

我有一个Chrome扩展程序,要求用户使用chrome.identity.getAuthToken路由进行登录。这样可以正常工作,但是当您登录时,您只能使用您在Chrome中拥有帐户的用户。

客户希望能够使用其他Google帐户登录;因此,他们希望能够使用.client@company.com登录,而不是使用Chrome登录的帐户.client @ gmail.com,这也是一个有效的Google帐户。

我可以使用一个帐户登录Chrome,使用第二个帐户登录Gmail,但我无法在扩展程序中选择。

这可能吗?

2 个答案:

答案 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