1

我遵循 23andMe API 身份验证指南,并使用以下代码将我的用户发送到 23andMe 进行身份验证和授权:

<?php

function base64url_encode($data)
{
 $b64 = base64_encode($data);
 if ($b64 === false) {
   return false;
 }
 $url = strtr($b64, '+/', '-_');
 return rtrim($url, '=');
}

$client_id    = 'xxx';
$redirect_uri = 'https://customURL';

// this is needed for custom redirect scheme
$code_verifier = 'yyy';
$hash = hash('sha256', $code_verifier);
$code_challenge = base64url_encode(pack('H*', $hash));

header("Location: https://api.23andme.com/authorize/"
    . "?redirect_uri=$redirect_uri"
    . "&response_type=code"
    . "&client_id=$client_id"
    . "&scope=basic"
    . "&code_challenge=$code_challenge");
?>

使用 Chrome DevTools,一旦访问上述页面,我可以看到以下 3 个 URL,并且用户授权访问:

https://auth.23andme.com/authorize/?scope=openid&response_type=code&client_id=api&redirect_uri=https%3A%2F%2Fapi.23andme.com%2Foauth_callback%2F

https://api.23andme.com/oauth_callback/?code=30edd45deb5f42d4bb7bd8413866cfc5&state=

https://api.23andme.com/authorize/?response_type=code&client_id=xxx&redirect_uri=https://customURL&scope=basic&code_challenge=wAYGGEPHpGhaG1gZTmyJ8M1Ly7JlGuoUVWBBVJ4OxTU

所以我可以看到已经生成了 code 参数(在这种情况下给定了 value 30edd45deb5f42d4bb7bd8413866。这个值应该给下面的代码(as $_GET["code"]),但它会导致 500 错误:

<?php    

$code = htmlspecialchars($_GET["code"]);

$code_verifier = 'yyy';

$post_field_array = array(
 'client_id'     => 'xxx',
 'client_secret' => 'zzz',
 'grant_type'    => 'authorization_code',
 'code'          => $code,
 'redirect_uri'  => 'https://customURL',
 'scope'         => 'basic',
 'code_verifier' => $code_verifier);

// Encode the field values for HTTP.
$post_fields = '';
foreach ($post_field_array as $key => $value)
 $post_fields .= "$key=" . urlencode($value) . '&';
$post_fields = rtrim($post_fields, '&');

// Use cURL to get the JSON response from 23andMe.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.23andme.com/token/');
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_POST, count($post_field_array));
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$encoded_json = curl_exec($ch);

$response = json_decode($encoded_json, true);
$access_token = $response['access_token'];

//show 
print_r($encoded_json);
echo $access_token;

?>

现在 500 的响应是:

https://api.23andme.com/authorize_check?response_type=code&client_id=xxx&redirect_uri=https%3A%2F%2FcustomURL&scope=basic&select_profile=false

感谢任何有关故障排除的建议。

4

1 回答 1

1

你有一些关于 URL 编码的问题;这个问题并没有明确说明您的问题是什么,但我建议您进行这些更改。

在第一个代码块中,您没有进行 URL 编码。如果您的值之一包含与号或其他特殊字符,这可能是一个问题。用于http_build_query()安全地构建查询字符串。另外,hash()可以直接输出二进制,所以不需要pack()在结果上使用,你应该总是在重定向后退出。PHP 永远不需要结束?>标记,除非后面有 HTML(不应该有。)

<?php

function base64url_encode($data)
{
    $b64 = base64_encode($data);
    if ($b64 === false) {
        return false;
    }
    $url = strtr($b64, '+/', '-_');
    return rtrim($url, '=');
}

$code_verifier = 'yyy';
$hash = hash('sha256', $code_verifier, true);

$query_string_vars = [
    "redirect_uri"   => "https://customURL",
    "response_type"  => "code",
    "client_id"      => "xxx",
    "scope"          => "basic",
    "code_challenge" => base64url_encode($hash),
];

$url = "https://api.23andme.com/authorize/?" . http_build_query($query_string_vars);
header("Location: $url");
exit();

在您的第二个代码块中,您正在使用编码值,urlencode()但这不是必需的,因为 cURL 会自动将传递给的值编码CURLOPT_POSTFIELDS为数组,或者您可以http_build_query()再次使用来传递字符串。此外,您htmlspecialchars()出于某种原因使用您的代码值,这很可能会破坏它。

<?php

$code_verifier = 'yyy';

$post_field_array = [
    'client_id'     => 'xxx',
    'client_secret' => 'zzz',
    'grant_type'    => 'authorization_code',
    'code'          => $_GET["code"],
    'redirect_uri'  => 'https://customURL',
    'scope'         => 'basic',
    'code_verifier' => $code_verifier
];

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL            => 'https://api.23andme.com/token/',
    CURLOPT_VERBOSE        => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query($post_field_array),
    CURLOPT_RETURNTRANSFER => true,
    
]);
$json = curl_exec($ch);

$response = json_decode($json, true);
$access_token = $response['access_token'];

//show 
print_r($json);
echo $access_token;
于 2020-12-28T18:44:48.737 回答