我已经阅读了从Google OpenID 2
迁移到OAuth 2/OpenIDConnect
时可以找到的所有文档,目前我正在使用phpclasses.org中的一个很好的类。这个类似乎与谷歌和Facebook都很合作(还没有尝试过其他供应商),但我对谷歌迁移路径的一个方面存在问题,这对于我:获取谷歌用户的旧OpenID标识符以及新的OpenIDConnect' sub'该用户的价值。我只是通过旧的OpenID标识符在我的数据库中注册了用户。
根据Google Migration Guide中的第3步,看起来我需要做的就是向发送到"openid.realm=http://www.example.com"
的身份验证请求添加参数https://accounts.google.com/o/oauth2/auth
。
我在我的旧代码中查找了我用于其OpenID注册过程的域(它是'http://' . $_SERVER['HTTP_HOST'];
),然后我确保我的应用程序中的重定向URL与该域兼容。
我将该值(url-encoded)添加为在类中生成的身份验证请求上传递的openid.realm
参数的值。但是当类交换令牌以获取访问令牌时,它会返回正确的电子邮件,名称,子等,但是没有openid_id
参数存在。顺便说一下,我的范围参数是'openid email profile'
有没有人建议我应该尝试什么,或者我可以做些什么来确定问题是什么?有没有人在PHP代码中获得openid_id
参数值的成功经验?我真的不愿意使用他们的#34;登录Google"而不是客户端路线。按钮,并根据真正不应该有必要的文档(如果我这样做的话,没有特别的理由相信它会解决我的问题)。
答案 0 :(得分:0)
当你交换access_token的authorization_code时,刚刚发现它与id_token一起返回了access_token。
在Migration Document,第3步前两段:
向Google发送OpenID Connect身份验证请求URI时 如步骤1中所述,您包含一个openid.realm参数。该 发送到您的redirect_uri的响应包括授权 您的应用程序可用于检索访问令牌的代码 ID令牌。 (您还可以直接从OpenID检索ID令牌 通过将id_token添加到response_type来连接身份验证请求 参数,可能保存对令牌端点的后端调用。)
来自该令牌请求的响应包括通常的字段 (access_token等),加上openid_id字段和标准OpenID 连接子字段。您在此上下文中需要的字段是openid_id 和子:
这令人困惑和误导/错误。什么令牌请求?身份验证请求会返回授权代码,您可以为access_token
和id_token
进行交换。关于将id_token
添加到response_type的括号内容并没有多大帮助,因为我尝试这样做的各种方法导致了错误。但无论如何,
“常用字段(access_token等),加上openid_id字段......”
错了。 access_token
永远不会出现在openid_id
字段的同一列表中。 access_token
显示在包含id_token
的列表中,openid_id
字段在id_token
内编码!
出于测试目的,您可以使用id_token
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=<string>
在本文档中,我找不到有关如何解码id_token
的有用说明,只是对其敏感性的警告,以及如何验证它们(尽管如果直接从谷歌端点获取验证则不需要就像这里的情况一样)。我下载了谷歌的php client,并从中提取了代码(src/Google/Auth/OAuth2.php
和src/Google/Utils.php
)。从中可以很容易地弄清楚如何解码id_token
字符串:在 .
,base64_decode
元素1和json_decode
上爆炸
更新2015-05-21:回复@Arthur的“回答”,这对这个答案的评论更合适。我本人会对这个答案发表评论,但评论不允许很长,不允许图片上传,而且我认为这些额外信息可以改善我的答案...
下面是netbeans / xdebug的截图,显示了解码id_token时得到的数组元素。有趣的是,此处列出的字段与@Arthur列出的字段的交集是空集。所以我怀疑无论@Arthur正在解码,它都不是这里描述的那种id_token。我对这些东西还不够熟悉,甚至不知道在那个答案中被解码的是什么。
我担心我没有时间挖掘我用来提取产生id_token的确切代码路径的库,我使用我描述的简单算法解码得到这个数组。但我可以告诉你,我使用的库是:http://www.phpclasses.org/package/7700-PHP-Authorize-and-access-APIs-using-OAuth.html
正如文档所述使用它并没有为您提供所需的id_token,原因有两个:
使用Oauth 2的Google预配置服务器不处理openid.realm参数。为了解决这个问题,我将以下服务器定义添加到oauth_configuration.json文件中:
"Google-OpenIdConnect":
{
"oauth_version": "2.0",
"dialog_url": "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}&openid.realm={REALM}",
"offline_dialog_url": "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}&access_type=offline&approval_prompt=force",
"access_token_url": "https://accounts.google.com/o/oauth2/token"
},
在致电Initialize()
之后,您需要添加
$client->store_access_token_response = true;
没有它,实际的access_token响应是不可访问的(至少不是我使用该类的方式)。有了这两个更改,我使用此类获取openid_id的确切代码如下:
protected function jwt_decode($jwt) {
$segments = explode(".", $jwt);
if (count($segments) != 3) {
throw new Exception("Wrong number of segments in token: $jwt");
}
// Parse envelope.
$envelope = json_decode($this->urlSafeB64Decode($segments[0]), true);
if (!$envelope) {
throw new Exception("Can't parse token envelope: " . $segments[0]);
}
// Parse token
$json_body = $this->urlSafeB64Decode($segments[1]);
$payload = json_decode($json_body, true);
return $payload;
}
protected function getOpenid_id() {
require_once 'Phpclasses/Http/Class.php';
require_once 'Phpclasses/OauthClient/Class.php';
require 'Phpclasses/Google/private/keys.php';
$client = new oauth_client_class;
$client->configuration_file = $phpclasses_oauth_dir . '/oauth_configuration.json';
$client->server = 'Google-OpenIdConnect';
$client->redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . strtok($_SERVER['REQUEST_URI'], '?');
$client->client_id = $GOOGLE_APPID;
$client->client_secret = $GOOGLE_APPSECRET;
$client->scope = 'openid email';
$client->realm = $this->getRequest()->getScheme() . '://' . $this->getRequest()->getHttpHost();
$me = null;
if (($success = $client->Initialize())) {
// set *after* the call to Initialize
$client->store_access_token_response = true;
if (($success = $client->Process())) {
if (strlen($client->authorization_error)) {
$client->error = $client->authorization_error;
$success = false;
}
elseif (strlen($client->access_token)) {
$success = $client->CallAPI('https://www.googleapis.com/oauth2/v1/userinfo', 'GET', array(), array('FailOnAccessError' => true), $user);
$me = (array) $user;
if (!array_key_exists('id_token', $client->access_token_response)) {
throw new Exception('No id_token in \$client->access_token_response');
}
$openid_id = $this->jwt_decode($client->access_token_response['id_token']);
$me['openid_id'] = $openid_id;
}
}
$success = $client->Finalize($success);
}
if ($client->exit)
exit;
$client->ResetAccessToken();
if ($success) {
return $me;
}
// Code to handle failure...
}
答案 1 :(得分:0)
尽管有sootsnoot(自己)的答案,我仍然无法在任何地方找到openid_id字段。当解码id_token时,只有&#34;发行者&#34;,&#34; issued_to&#34;,&#34;观众&#34;,&#34; user_id&#34; ,&#34; expires_in&#34; ,&#34; issued_at&#34;,&#34; email&#34;和&#34; nonce&#34;领域。 不&#34; openid_id&#34;看见的领域..
有什么想法吗?
回应sootsnoot的回复:)我为没有足够的声誉发表评论而道歉,否则就会这样做。
使用从自动配置获取端点的OpenID Connect库:https://accounts.google.com/.well-known/openid-configuration 所以假设端点不是问题。事实上,我似乎正在检查错误的id_token。但是,即使检查正确的,我仍然没有看到&#34; openid_id&#34;领域。我现在看到你拥有的一切,除了我有一个&#34; nonce&#34;字段而不是&#34; openid_id&#34;字段:
stdClass :: __ set_state(array(&#39; iss&#39; =&gt;&#39; https://accounts.google.com&#39;,&#39; sub&#39; =&gt; **** ,&#39; azp&#39; =&gt; ****,&#39;电子邮件&#39; =&gt; ****,&#39; nonce&#39; =&gt; ****,& #39; at_hash&#39; =&gt; ****,&#39; email_verified&#39; =&gt; true,&#39; aud&#39; =&gt; ****,&#39; iat&# 39; =&gt; ****,&#39; exp&#39; =&gt; 1432300788,))
一定是做错了什么,但是......
最终更新:
发现问题:将realm参数传递为openid_realm = ...而不是openid.realm = ...
哦,我感到愚蠢......:)