问题描述
假设您在网络平台上有一个现有的用户管理/数据库。应该集成与 Apple 的登录,以便更快地登录和注册过程 - 尽管它始终会创建一个链接到电子邮件地址的常规帐户(只是没有常规密码)。 使用 Apple 提供的(经过验证的)JWT 进行身份验证是否安全?
登录(现有帐户)将是以下步骤:
- 用户在应用中点击“使用 Apple 登录”
- Apple 生成的 JWT 被发送到身份验证服务器
- 服务器使用 Apple 的 API 端点提供的公钥验证 JWT
- 服务器从(经过验证的)JWT 中提取电子邮件,如果存在拥有该电子邮件的用户,则该用户已登录(API 返回会话的内部访问/刷新令牌)
解决方法
我尝试为 iOS 应用制作一个答案。但首先要澄清问题:
“使用 Apple 提供的(经过验证的)JWT 进行身份验证是否安全?”
我们从授权任务中收到的唯一已知 JWT 是“id_token”。其他参数也可能是 JWT,但这些对客户端来说是不透明的。
现在的问题是,如果我们将 id_token 发送到应用服务器,是否仅验证 id_token 就足以向客户端分发应用服务器域的访问令牌?答案:不!
使用 Apple 的 iOS 身份验证框架进行 Apple 登录时,授权任务在完成处理程序中返回 ASAuthorization
值。这基本上包含以下参数:
-
user
:标识符 -
identityToken
:JWT “id_token”(参见 OIDC) -
authorizationCode
:一种短期有效的令牌,可提供对应用程序服务器组件的授权证明。授权代码使用授权请求中传递的状态属性绑定到特定事务。应用程序的服务器组件可以使用 Apple 为此目的提供的身份服务端点来验证代码。 *)
*) 如果该值确实对应于客户端将通过“前端通道” aka 用户代理 aka 浏览器获得的 OIDC“代码”值,那么我们还应该确保附加机制实际上提供了安全的“授权证明”(Universal Links,PKCE),请参阅Authorization Code Interception Attack。 如果这些攻击在技术上是不可能的,因为身份验证系统提供了与应用的安全通信渠道,但我们不需要 PKCE。
id_token 包含有关已通过身份验证的用户的信息,这些信息存储在提供者上。这是一个签名的 JWT。即使 JWT 可以成功验证,单独使用 JWT,应用服务器也无法确定发送者就是它所相信的那个人。我们不想给任何人未经身份验证的访问令牌!
应用服务器需要更多证明,这将通过 authorizationCode
参数完成。但是,必须在提供程序上进行此检查。
So,we have to perform two steps:
-
Verify the Identity Token (id_token) 这将在应用服务器上执行。
-
验证授权码
第二步将由您的应用服务器 obtaining a refresh token 形成的 Providers 特殊端点完成。
在第 2 步中,我们收到一个 TokenResponse。
如果成功,我们会收到一个访问令牌和一个刷新令牌。访问令牌没有用,但我们需要刷新令牌:
“您最多可以每天验证一次刷新令牌,以确认用户在该设备上的 Apple ID 在 Apple 服务器上的信誉仍然良好。”
将其存储在您的应用服务器上。
在您的应用服务器上完成所有操作后,您将继续:
管理用户会话
验证身份令牌后,您的应用负责管理用户会话。您可以将会话的生命周期与 Apple 设备上成功的 getCredentialState(forUserID:completion:) 调用联系起来。这是一种本地、廉价、非网络呼叫,由 Apple ID 系统启用,该系统使设备上的 Apple ID 状态与 Apple 服务器保持同步。
“用户会话”可能需要域特定的访问令牌和刷新令牌。当客户端需要您的令牌端点上的新访问令牌时,您可能会再次验证 Apple 的刷新令牌。
因此,最后一步是您的应用向您的客户端发送域特定的访问令牌和刷新令牌。