5356450e8a879be669cbf53ed6bb473b
Sign In with Apple - 使用苹果账号登录你的应用

WWDC 2019 Session 706: Introducing Sign In with Apple

1. 引言

对于绝大部分应用,通常都会有自己的账号体系,但为了避免用户经常忘记账号密码,那些中小型 App 一般都会接入来自大厂的第三方登录 SDK,如在国外常见的有使用 Google、Facebook、Twitter、GitHub 等账号登录;而在国内,近两年几乎所有的 App 都会推荐首先使用手机号+短信验证码的方式注册/登录,同时也会接入像微信、QQ、微博、支付宝等超级 App 的登录 SDK,便于用户实现一键登录,如下图:

在 WWDC 2019 上,苹果也想在第三方登录服务这块“蛋糕”上分一杯羹,给我们带来了 “Sign In with Apple”(使用苹果账号登录)。这似乎也是水到渠成的事,因为几乎所有的 iOS/macOS 设备都会登录 Apple ID,而且近两年来,苹果强制对所有的 Apple ID 推行 Two-Factor Authentication(2FA,双重因子验证),大大提高了苹果账号的安全性,减少了盗号风险。

因此,我们可以畅想在不久的将来,在苹果设备上,只要登录了 Apple ID,就可以直接登录所有的 App,是不是很方便?但这同时也是黑客们的福音啊...👻

2. 概览

The fast, easy way to sign in to apps and websites.

通过 Sign In with Apple,用户可以轻松登录开发者的应用和网站,开发者可以获取到以下三个信息用于创建自己的账号体系(下一节会细讲):

  • ID
  • Full name
  • Verified email address

它具有如下几个特性,详见这里

  • 简化账号的创建和登录流程,无缝跨设备使用;
  • 开发者可以获取到已验证过的邮箱作为登录账号或者与用户进行通信(注:用户可以选择隐藏真实邮箱,并使用苹果提供的虚拟邮箱进行授权);

  • 系统内置的安全性:2FA 双重验证(Face ID 或 Touch ID),从此登录不再需要密码;
  • 尊重用户隐私:开发者仅仅可获取到用户的姓名和邮箱,同时苹果不会收集任何用户与应用之间使用数据;
  • 反欺诈:使用设备上的机器学习等技术和其他信息,帮助开发者判断一个账号是否为真实用户;

最重要的是,它是跨平台的!!!

3. 如何集成

在你的 App 中集成 “Sign In with Apple” 服务,大致只需要以下 4 步骤:

添加登录按钮

首先,在你 App 的登录页面添加一个 “Sign In with Apple” 按钮 ASAuthorizationAppleIDButton,并添加按钮点击响应事件,代码大致如下:

// Add “Sign In with Apple” button to your login view
func setupProviderLoginView() {
    let authorizationButton = ASAuthorizationAppleIDButton()
    authorizationButton.addTarget(self, action: #selector(handleAuthorizationAppleIDButtonPress), for: .touchUpInside)
    self.loginProviderStackView.addArrangedSubview(authorizationButton)
}

当然你也可以自定义苹果登录按钮的样式,样式要求详见这个文档:Human Interface Guidelines

最终的登录页面效果图参考如下:

发起授权请求

在上述按钮点击事件 handleAuthorizationAppleIDButtonPress 中配置需要获取的数据权限范围(例如:用户名、邮箱等),然后设置回调代理,并发起登录授权请求,代码如下:

// Configure request, setup delegates and perform authorization request
@objc func handleAuthorizationAppleIDButtonPress() {
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    let request = appleIDProvider.createRequest()
    request.requestedScopes = [.fullName, .email]

    let authorizationController = ASAuthorizationController(authorizationRequests: [request])

    authorizationController.delegate = self
    authorizationController.presentationContextProvider = self

    authorizationController.performRequests()
}

当用户点击 “Sign In with Apple” 按钮后,就会显示苹果登录授权确认页面,如下图所示:

在该页面,用户可以选择是否给你返回他的名字和真实邮箱,当然用户也可以选择隐藏自己的邮箱,此时开发者会得到一个虚拟的用户邮箱地址(此邮箱收到的邮件会转发到用户真实的邮箱上)。当用户点击 “Continue” 按钮后,会进行 “Touch ID” 或者 “Face ID” 双重验证,然后回调数据给开发者。

处理回调,服务端验证结果

我们需要在代码中实现两个代理回调 ASAuthorizationControllerDelegateASAuthorizationControllerPresentationContextProviding 分别用于处理授权登录成功和失败、以及提供用于展示授权页面的 Window,代码如下:

/// MARK: ASAuthorizationControllerDelegate
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {

        let userIdentifier = appleIDCredential.user
        let fullName = appleIDCredential.fullName
        let email = appleIDCredential.email
        let realUserStatus = credential.realUserStatus

        let identityToken = credential.identityToken
        let authCode = credential.authorizationCode

        // Create account in your system 
    }
}

func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
    // Handle error.
}

/// MARK: ASAuthorizationControllerPresentationContextProviding
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
    return self.view.window!
}

在授权成功回调中,我们可以拿到以下几类数据:

  • User ID: Unique, stable, team-scoped user ID,苹果用户唯一标识符,该值在同一个开发者账号下的所有 App 下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来(这与国内的微信、QQ、微博等第三方登录流程基本一致)。

  • Verification data: Identity token, code,验证数据,用于传给开发者后台服务器,然后开发者服务器再向苹果的身份验证服务端验证本次授权登录请求数据的有效性和真实性,详见 Sign In with Apple REST API。如果验证成功,可以根据 userIdentifier 判断账号是否已存在,若存在,则返回自己账号系统的登录态,若不存在,则创建一个新的账号,并返回对应的登录态给 App。

  • Account information: Name, verified email,苹果用户信息,包括全名、邮箱等。

  • Real user indicator: High confidence indicator that likely real user,用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupportedunknownlikelyReal

处理苹果账号会话发生变化

当开发者的 App 通过苹果账号登录后,iOS/macOS 设备上登录的 Apple ID 发生变化时,例如:

  • 设备上的 Apple ID 退出登录、切换新的账号登录;
  • 用户在设置页面禁止 App 使用苹果账号登录,如下图:

此时,也需要通知到 App 做账号登出处理。因此,我们可以 App 启动时,调用 ASAuthorizationAppleIDProvidergetCredentialState 方法,传入当前用户的 UserIdentifier 进行判断:

```swift
let appleIDProvider = ASAuthorizationAppleIDProvider()
appleIDProvider.getCredentialState(forUserID: "currentUserIdentifier") { (credentialState, error) in
switch credentialState {
case .authorized:

top Created with Sketch.