是否可以通过Chrome App Identity Api获取Id令牌?

时间:2014-10-08 11:47:39

标签: google-chrome-extension oauth-2.0 token google-chrome-app google-openid

我无法从Chrome身份API(https://developers.google.com/accounts/docs/CrossClientAuth)获取用户的id_token(https://developer.chrome.com/apps/identity)。

当清单中的oauth部分为:

时,我可以使用chrome身份示例获取access_token
"oauth2": {
  "client_id": "<chrome-app-client-id>.apps.googleusercontent.com",
  "scopes": ["https://www.googleapis.com/auth/plus.login"]
}

但是当我尝试以同样的方式获取id_token时,我会在我的Android客户端上获取错误:

“OAuth2请求失败:服务响应错误:'无效范围:{0}'”}

清单部分现在是:

"oauth2": {
  "client_id": "<chrome-app-client-id>.apps.googleusercontent.com",
  "scopes": ["audience:server:client_id:<app-engine-client-id>.apps.googleusercontent.com"]
}

在Android上,我通过将相同的范围字符串传递给android.gms.auth.GoogleAuthUtil.getToken()来获取id_token,但我无法使用chrome身份API进行操作。

是否可以通过Chrome App Indentity Api获取id_token?如果没有,我如何获得Chrome应用的id_token?

感谢您的帮助!

3 个答案:

答案 0 :(得分:10)

我昨天遇到了同样的问题,因为我找到了解决方案,我不妨分享一下,因为它不是那么明显。据我所知,谷歌没有提供直接和记录的方式来执行此操作,但您可以使用chrome.identity.launchWebAuthFlow()功能。

首先,您应该在Google控制台中创建 Web应用程序凭据,并将以下网址添加为有效的Authorized redirect URIhttps://<EXTENSION_OR_APP_ID>.chromiumapp.org。 URI不一定存在,chrome只会捕获重定向到此URL并稍后调用您的回调函数。

<强>的manifest.json

{
  "manifest_version": 2,
  "name": "name",
  "description": "description",
  "version": "0.0.0.1",
  "background": {
    "scripts": ["background.js"]
  },
  "permissions": [
    "identity"
  ],
  "oauth2": {
    "client_id": "<CLIENT_ID>.apps.googleusercontent.com",
    "scopes": [
      "openid", "email", "profile"
    ]
  }
}

<强> background.js

// Using chrome.identity
var manifest = chrome.runtime.getManifest();

var clientId = encodeURIComponent(manifest.oauth2.client_id);
var scopes = encodeURIComponent(manifest.oauth2.scopes.join(' '));
var redirectUri = encodeURIComponent('https://' + chrome.runtime.id + '.chromiumapp.org');

var url = 'https://accounts.google.com/o/oauth2/auth' + 
          '?client_id=' + clientId + 
          '&response_type=id_token' + 
          '&access_type=offline' + 
          '&redirect_uri=' + redirectUri + 
          '&scope=' + scopes;

chrome.identity.launchWebAuthFlow(
    {
        'url': url, 
        'interactive':true
    }, 
    function(redirectedTo) {
        if (chrome.runtime.lastError) {
            // Example: Authorization page could not be loaded.
            console.log(chrome.runtime.lastError.message);
        }
        else {
            var response = redirectedTo.split('#', 2)[1];

            // Example: id_token=<YOUR_BELOVED_ID_TOKEN>&authuser=0&hd=<SOME.DOMAIN.PL>&session_state=<SESSION_SATE>&prompt=<PROMPT>
            console.log(response);
        }
    }
);

可以在此处找到Google OAuth2 API(适用于OpenID Connect)文档:https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters

PS:如果您不需要清单中的oauth2部分。您可以安全地省略它,并仅在代码中提供标识符和范围。

编辑: 对于那些感兴趣的人,您不需要身份API。您甚至可以使用制表符API的小技巧来访问令牌。代码有点长,但您有更好的错误消息和控制。请注意,在以下示例中,您需要创建 Chrome应用凭据。

<强>的manifest.json

{
  "manifest_version": 2,
  "name": "name",
  "description": "description",
  "version": "0.0.0.1",
  "background": {
    "scripts": ["background.js"]
  },
  "permissions": [
    "tabs"
  ],
  "oauth2": {
    "client_id": "<CLIENT_ID>.apps.googleusercontent.com",
    "scopes": [
      "openid", "email", "profile"
    ]
  }
}

<强> background.js

// Using chrome.tabs
var manifest = chrome.runtime.getManifest();

var clientId = encodeURIComponent(manifest.oauth2.client_id);
var scopes = encodeURIComponent(manifest.oauth2.scopes.join(' '));
var redirectUri = encodeURIComponent('urn:ietf:wg:oauth:2.0:oob:auto');

var url = 'https://accounts.google.com/o/oauth2/auth' + 
          '?client_id=' + clientId + 
          '&response_type=id_token' + 
          '&access_type=offline' + 
          '&redirect_uri=' + redirectUri + 
          '&scope=' + scopes;

var RESULT_PREFIX = ['Success', 'Denied', 'Error'];
chrome.tabs.create({'url': 'about:blank'}, function(authenticationTab) {
    chrome.tabs.onUpdated.addListener(function googleAuthorizationHook(tabId, changeInfo, tab) {
        if (tabId === authenticationTab.id) {
            var titleParts = tab.title.split(' ', 2);

            var result = titleParts[0];
            if (titleParts.length == 2 && RESULT_PREFIX.indexOf(result) >= 0) {
                chrome.tabs.onUpdated.removeListener(googleAuthorizationHook);
                chrome.tabs.remove(tabId);

                var response = titleParts[1];
                switch (result) {
                    case 'Success':
                        // Example: id_token=<YOUR_BELOVED_ID_TOKEN>&authuser=0&hd=<SOME.DOMAIN.PL>&session_state=<SESSION_SATE>&prompt=<PROMPT>
                        console.log(response);
                    break;
                    case 'Denied':
                        // Example: error_subtype=access_denied&error=immediate_failed
                        console.log(response);
                    break;
                    case 'Error':
                        // Example: 400 (OAuth2 Error)!!1
                        console.log(response);
                    break;
                }
            }
        }
    });

    chrome.tabs.update(authenticationTab.id, {'url': url});
});

答案 1 :(得分:0)

我想这取决于为什么人们想要令牌ID,但在我的情况下  access_token足以授权用户 - 通过从https://www.googleapis.com/oauth2/v2/userinfo?alt=json提取用户信息(使用Authorization header = access_token)。

答案 2 :(得分:-1)

首先,我假设在您的manifest.json片段中,您并不意味着您的client_id确实是"<chrome-app-client-id>.apps.googleusercontent.com。它应该类似于9414861317621.apps.googleusercontent.com - 您从开发者控制台获得的内容,或者您​​用于注册应用的任何Google网站。

假设上述情况正常,并且您拥有client_id权限,并且范围正确,那么您将获得所谓的&#34; OAuth2访问令牌&#34;打电话给chrome.identity.getAuthToken。由于您没有向我们展示任何JavaScript代码,因此我无法判断这是您正在做的事情。您调用API函数时需要保存的访问令牌以供后续使用。例如:

var access_token;

chrome.identity.getAuthToken(
    {
        'interactive': true
    },
    function(token) {
        access_token = token;
        // do something if you like to indicate
        // that the app is authorized
    }
);

然后,当您进行API调用时,您提供该访问令牌,如下所示:

var url = 'https://www.googleapis.com/' + method;
Ajax.ajaxSend(url, "json",
    function (status, response) {
        if (response && response.error && response.error.message)
            errorCallback(response.error.message);
        else if (status == 200)
            successCallback(response);
        else
            errorCallback('Result code: ' + status);
    },
    function (e) {
        if (errorCallback)
            errorCallback('Communication error');
    },
    {
        Authorization: 'Bearer ' + access_token
    }
);

Ajax.ajaxSend是我自己的功能:

var Ajax = (function () {
    var api = {
        ajaxSend: function (url, responseType, successCallback, errorCallback, headers) {
            var req = new XMLHttpRequest();
            req.onload = function (e) {
                successCallback(req.status, req.response);
            };
            req.onerror = errorCallback;
            req.responseType = responseType ? responseType : "text";
            req.open("get", url);
            if (headers)
                for (var v in headers)
                    req.setRequestHeader(v, headers[v]);
            req.send();
        }
    };
    return api;
})();

其他未定义的功能也是您所期望的。 Ajax.ajaxSend的第三个参数是要发送的标头。 (对不起,我没有时间为这个答案开发独立代码。)

我希望上面的内容很有用。