【问题标题】:How to exchange an Apple authorization code for an access token?如何用苹果授权码换取访问令牌?
【发布时间】:2020-01-07 09:08:54
【问题描述】:

我很难弄清楚如何使用 Apple 登录。文档很糟糕,失败的响应让我们一无所知。 Aaron Parecki (https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple) 的文章确实有点帮助,但我现在似乎卡住了。

首先,我使用 /auth/authorize 生成一个登录 URL,如下所示:

$_SESSION['state'] = bin2hex(openssl_random_pseudo_bytes(16));

return 'https://appleid.apple.com/auth/authorize?' . http_build_query([
  'response_type' => 'code',
  'response_mode' => 'form_post',
  'client_id' => 'com.my.service.id',
  'redirect_uri' => 'https://my.app/redirect'),
  'state' => $_SESSION['state'],
  'scope' => 'name email',
]);

在域名验证和返回 URL 苦苦挣扎之后,这会将我带到 Apple 登录页面,并在成功登录后返回到我的 redirect_uri。然后,我需要授权我使用 Guzzle 执行的令牌:

$response = (new Client)->request('POST', 'https://appleid.apple.com/auth/token', [
  RequestOptions::FORM_PARAMS => [
    'grant_type' => 'authorization_code',
    'code' => $_POST['code'],
    'redirect_uri' => 'https://my.app/redirect',
    'client_id' => 'com.my.service.id',
    'client_secret' => $this->getClientSecret(),
  ],
  RequestOptions::HEADERS => [
    'Accept' => 'application/json'
  ]
]);

return json_decode($response, true);

客户端密码是使用 Firebase php-jwt (https://github.com/firebase/php-jwt) 生成的,通过 jwt.io 有效:

$key = openssl_pkey_get_private('file://certificate.p8');

return JWT::encode([
  'iss' => 'APPLETEAMID',
  'iat' => time(),
  'exp' => time() + 3600,
  'aud' => 'https://appleid.apple.com',
  'sub' => 'com.my.service.id',
], $key, 'ES256', 'certificate-id');

但是,向 Apple 执行令牌请求会返回 400 错误,并显示消息“invalid_client”。我无法确定我的客户端 ID/密码是否错误,或者重定向的代码是否无效。有人能指出我正确的方向吗?

编辑: 请注意,我重写了 JWT 类,允许使用 ES256。欲了解更多信息,请查看this open pull request

【问题讨论】:

  • 这可能有帮助吗? stackoverflow.com/a/57622747/2412335
  • @digijay 恐怕不会。我们的域已经过验证。
  • 我确实假设您发送的代码无效。您如何检索从授权端点收到的代码?
  • 当你有你形成的包含代码的重定向 url(所以基本上你的函数返回的内容),然后你可以在那个 url 上执行一个 get ($code = $_GET['code'];) 到提取代码。这可能是你现在正在做的事情,但由于上面没有提到,我想我会提到它。
  • @Axelle 登录后的重定向 URL 被称为来自 Apple 的 POST 请求,包含代码和状态。请查看第二个代码块,我从 $_POST 检索代码并将其用于我对 Apple 的令牌请求。

标签: php oauth-2.0 jwt apple-sign-in


【解决方案1】:

根据您所说的以及 Apple 在其文档中所说的内容,我认为您将 Apple 的 Identity token 误认为是 Client Secret

Apple 的 Identity Token 是一个 JWT 令牌,用于唯一标识用户,是现有 OAuth 2.0 框架“之上”的安全层。

当您在授权服务器中注册/验证您的客户端时,基于RFC 6749 - The OAuth 2.0 Authorization Framework,您很可能会从授权服务器收到一个秘密,这是您的客户端的秘密(即,您的服务器的密码com.my.service.id)用于从获取令牌到使用身份检索资源的每个请求。使用 client_id/client_secret 对的建议方法是 HTTP Basic Authentication 方法,不鼓励使用 POST 参数。

授权服务器必须支持 HTTP Basic 用于认证客户端的认证方案 客户密码。

...

使用这两个在请求正文中包含客户端凭据 不推荐使用参数,应仅限于无法使用的客户端 直接使用 HTTP Basic 身份验证方案(或其他 基于密码的 HTTP 身份验证方案)。参数只能 在请求正文中传输,并且不得包含在 请求 URI。

您将以 JWT 令牌的形式收到用户的Identity Token,并且您应该准确地提供 THAT 而不是篡改,因为此令牌是由 Apple 自己发行的。但是,您可以检查有效负载(参见JWT Introduction at jwt.io)以了解Identity Token 的有效性(iat + exp)。除非您为 Apple 的授权团队编码,否则不应生成 Identity Token

最后,我不能强调要彻底阅读 OAuth 规范并记住 Identity Token 是 Apple 向您发送用户 ID 的方式,但 client_id 和 client_secret 是在 OAuth 规范中定义的。

【讨论】:

  • 不幸的是,这不是“使用 Apple 登录”的工作流程,因为它不完全遵循 OAuth2。重定向返回一个授权码(不是身份令牌),它应该被交换为一个访问令牌。此对 /auth/token 的请求应使用 JWT 生成的应用程序客户端密钥进行签名。请在我的原始问题中查看 Aaron Parecki 的文章,其中逐步描述了流程。
猜你喜欢
  • 1970-01-01
  • 2022-08-16
  • 2021-04-07
  • 2015-06-12
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
  • 2018-04-11
  • 1970-01-01
相关资源
最近更新 更多