7

每当我启动我的应用程序时,我想确保苹果登录仍然有效,但我也希望能够向我的服务器证明它。
成功登录后,我可以ASAuthorizationAppleIDProvider.getCredentialState用来确定我的凭据对于后续登录是否仍然有效。
这工作正常,但无法向我的服务器证明此过程已成功执行,任何使用我的服务器 API 的人都可以告诉服务器不要担心,服务器将无法判断它是否是真正的客户端或不。
服务器还可以使用刷新令牌来验证用户是否仍然有效,方法是发出一个新的访问令牌(Apple 说每天不超过一次,但现在还可以),但这也无济于事,因为我不希望密钥出现在客户端应用程序中,此外,即使我从客户端获得了新的访问令牌 - 服务器暂时无法将其用于任何事情,因为目前不存在这样的 API任何带有访问令牌的东西。

我需要的是一个新的身份验证令牌,但是 - 当ASAuthorizationController.performRequests()使用 Apple ID 请求调用时 - UI 再次显示,而不仅仅是ASAuthorizationControllerDelegate使用新的授权令牌调用。
我还尝试refresh在第二次登录尝试中将请求操作设置为:

    let request = appleIDProvider.createRequest()
    request.requestedOperation = .operationRefresh

同样的结果,UI 仍然显示。
有没有办法在不显示 UI 的情况下使用 Apple ID 重新进行身份验证,并获得某种令牌,服务器可以使用它来验证登录的真实性?
请不要建议超出范围的方法,例如在向服务器提供初始证明后涉及我自己的登录机制并让服务器从那时起使用刷新令牌的方法 - 我可以自己想出这些解决方案- 我正在寻找苹果登录方法的解决方案。

4

1 回答 1

3

根据文档

验证身份令牌后,您的应用程序负责管理用户会话。您可以将会话的生命周期与 Apple 设备上成功的 getCredentialState(forUserID:completion:) 调用联系起来。这是一个本地、廉价、非网络呼叫,由 Apple ID 系统启用,该系统使设备上的 Apple ID 状态与 Apple 服务器保持同步。

任何时候请求新的身份令牌时都需要用户交互。用户会话在设备上是长期存在的,因此在每次启动时调用新的身份令牌,或者每天调用一次以上,可能会导致您的请求因节流而失败

如果系统中用户的 Apple ID 发生更改,则调用 getCredentialState(forUserID:completion:) 表示用户已更改。假设其他用户已登录并注销应用程序的当前已知用户。

对于在其他系统上运行的应用程序,使用刷新令牌的定期成功验证来确定用户会话的生命周期。

因此,这基本上意味着您应该在从 getCredentialState 获取结果时检查 userIdentifier,并确保它与当前用户的值相同。如果您想在应用程序中重新进行身份验证,您将无法避免您在问题中提到的用户交互。

首先,您必须从应用程序登录中获取 userIdentifier 和 authenticationCode 并将其发送到您的服务器。还要制作一个长会话 ID 并将其发送到您的服务器:

if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {

        let userIdentifier = appleIDCredential.user
        print("userIdentifier: \(userIdentifier)")

        let authorizationCode = String(data: appleIDCredential.authorizationCode!, encoding: .utf8)!
        print("authorizationCode: \(authorizationCode)")  

        let sessionID = veryLongToken

        //send authorizationCode, userIdentifier and sessionId to server
    }

在您的服务器上,您在调用时获得 identityToken:appleid.apple.com/token/auth

和:

#create token
import jwt  # pip install pyjwt
claims = {
    'iss': teamID,
    'iat': timeNow,
    'exp': time3Months,
    'aud': 'https://appleid.apple.com',
    'sub': app_id,
}
client_secret = jwt.encode(claims, private_key_from_developer_portal, algorithm='ES256', headers={'kid': kid})


data = {
    'client_id': app_id,
    'client_secret': client_secret,
    'grant_type': grant_type,
    'redirect_uri': redirect_uri,
    'code': authorizationCode,
}

headers = {
    'Content-Type': 'application/x-www-form-urlencoded',
}

response = requests.request(
    method='POST',
    url='https://appleid.apple.com/auth/token',
    data=data,
    headers=headers,
)
#response will include refresh_token, access_token and id_token

您现在可以从 id_token 检索用户电子邮件和 userIdentifier:

result = jwt.decode(id_token, verify=False)
print("jwtResult: " + result)

userIdentifier = result["sub"]
email = result["email"]

现在你从苹果知道它是什么用户。根据 验证令牌文档,您首先在grant_type 中使用authorization_code 来获取refresh_token,然后使用refresh_token 作为grant_type

这是验证用户仍然在服务器上进行身份验证的唯一方法。

因此,将 sessionId 以及有关苹果登录(刷新令牌...)的一些信息存储在您的服务器上。

当应用程序需要来自服务器的数据时,您可以让应用程序将 sessionId 发送到服务器,并且在将一些信息交给应用程序之前,在服务器上进行刷新令牌验证。如果身份验证无效,则应用程序应删除 sessionId 并注销。

如果身份验证有效,这是您能够在服务器上进行验证的唯一方法。

通过阅读文档,这似乎是推荐的方式,而您尝试做的事情会导致问题,并且苹果不希望您一直在应用程序中对用户进行身份验证。

您还应该查看来自苹果的这个示例项目,以使用苹果登录。

果汁示例项目

于 2020-01-28T08:22:43.687 回答