苹果登录 (apple sign)
背景
在今年(2019)的开发者大会上(北京时间6月4号),苹果宣布支持应用可以通过苹果登录这一第三方登录自身app.
当然,审核条款也更新了.
4.8 通过 Apple 登录
如果 app 专门使用第三方或社交登录服务 (例如,Facebook 登录、Google 登录、通过 Twitter 登录、通过 LinkedIn 登录、通过 Amazon 登录或微信登录) 来对其进行设置或验证这个 app 的用户主帐户,则该 app 必须同时提供“通过 Apple 登录”作为等效选项。用户的主帐户是指在 app 中建立的、用于标识身份、登录和访问功能和相关服务的帐户。
在以下情况下,不要求提供“通过 Apple 登录”选项:
- 您的 app 仅使用公司自有的帐户设置和登录系统。
- 您的 app 是一款教育、企业或商务 app,要求用户使用现有的教育或企业帐户登录。
- 您的 app 使用政府或行业支持的公民身份系统或电子身份证来鉴定用户身份。
- 您的 app 是特定第三方服务的客户端,用户需要使用他们的邮件、社交媒体或其他第三方帐户直接登录才能访问内容。
所以,如果你的app符合上面要求,那么苹果登录就少不了,或者你觉得给用户多一种第三方登录选择,也可以选择接进来.
这个没有一点难度,和接入qq,微信一样的.超级简单.
需使用xcode11,准备开发者账号并且证书勾选苹果登录权限
苹果原话(需要准备的东西)
Configure the Sample Code Project
Perform the following steps before building and running the app:
Set your development team in the Signing & Capabilities tab so Xcode can create a provisioning profile that uses the Sign In with Apple capability.
Choose a target device that you’re signed into with an Apple ID that uses Two-Factor Authentication.
大家跟着我走就是了.
也可以自己去苹果官方文档里.
友情提示下,苹果的官方代码是swift的.swift不会的要学习起来了.
首先,需要有个苹果登录按钮 .这是默认样式
Simulator Screen Shot - iPhone 11 Pro Max - 2019-10-10 at 21.58.13.png它就长这样.苹果不建议我们改东西.只能用苹果提供的.就圆角和大小可以更换.有其他的要求的也可以试试.看看苹果会不会给过.文本可以添加多语言本地化.
这个类 ASAuthorizationAppleIDButton,继承UIControl
extension ASAuthorizationAppleIDButton {
@available(iOS 13.0, *)
public enum ButtonType : Int {
case signIn
case `continue`
public static var `default`: ASAuthorizationAppleIDButton.ButtonType { get }
}
@available(iOS 13.0, *)
public enum Style : Int {
case white
case whiteOutline
case black
}
}
@available(iOS 13.0, *)
open class ASAuthorizationAppleIDButton : UIControl {
public convenience init(type: ASAuthorizationAppleIDButton.ButtonType, style: ASAuthorizationAppleIDButton.Style)
public init(authorizationButtonType type: ASAuthorizationAppleIDButton.ButtonType, authorizationButtonStyle style: ASAuthorizationAppleIDButton.Style)
/** @abstract Set a custom corner radius to be used by this button.
*/
open var cornerRadius: CGFloat
}
//可改的东西好少
let authorizationButton = ASAuthorizationAppleIDButton.init(authorizationButtonType: ASAuthorizationAppleIDButton.ButtonType.signIn, authorizationButtonStyle: ASAuthorizationAppleIDButton.Style.white)
Simulator Screen Shot - iPhone 11 Pro Max - 2019-10-10 at 22.08.18.png
let authorizationButton = ASAuthorizationAppleIDButton.init(authorizationButtonType: ASAuthorizationAppleIDButton.ButtonType.continue, authorizationButtonStyle: ASAuthorizationAppleIDButton.Style.white)
Simulator Screen Shot - iPhone 11 Pro Max - 2019-10-10 at 22.08.18.png
let authorizationButton = ASAuthorizationAppleIDButton.init(authorizationButtonType: ASAuthorizationAppleIDButton.ButtonType.continue, authorizationButtonStyle: ASAuthorizationAppleIDButton.Style.whiteOutline)
Simulator Screen Shot - iPhone 11 Pro Max - 2019-10-10 at 22.11.43.png
有按钮了,那就要开始点击了.
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
let authorizationController =
ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
extension LoginViewController: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
//告诉苹果弹窗到哪个window上
return self.view.window!
}
}
-在用户没有同意授权之前或者取消授权之后,点击登录的时候,都会弹出授权界面,在这个授权页面,我们可以修改自己的用户名,以及可以选择共享我的电子邮箱或者隐藏邮件地址。这样一来,就可以达到隐藏自己真实信息的目的。
授权一次后,再次点击登录按钮,则会直接弹出正在登录.
//这是这次的,下次的呢. 直接可以通过keychain直接登录.
这个performExistingAccountSetupFlows放到你要登录的地方去.
/// Prompts the user if an existing iCloud Keychain credential or Apple ID credential is found.
func performExistingAccountSetupFlows() {
// Prepare requests for both Apple ID and password providers.
let requests = [ASAuthorizationAppleIDProvider().createRequest(),
ASAuthorizationPasswordProvider().createRequest()]
// Create an authorization controller with the given requests.
let authorizationController = ASAuthorizationController(authorizationRequests: requests)
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}
下面是回调了
extension LoginViewController: 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
// Create an account in your system.
// For the purpose of this demo app, store the userIdentifier in the keychain.
do {
try KeychainItem(service: "com.example.liknddd", account: "userIdentifier").saveItem(userIdentifier)
} catch {
print("Unable to save userIdentifier to keychain.")
}
// For the purpose of this demo app, show the Apple ID credential information in the ResultViewController.
if let viewController = self.presentingViewController as? ResultViewController {
DispatchQueue.main.async {
viewController.userIdentifierLabel.text = userIdentifier
if let givenName = fullName?.givenName {
viewController.givenNameLabel.text = givenName
}
if let familyName = fullName?.familyName {
viewController.familyNameLabel.text = familyName
}
if let email = email {
viewController.emailLabel.text = email
}
self.dismiss(animated: true, completion: nil)
}
}
} else if let passwordCredential = authorization.credential as? ASPasswordCredential {
// Sign in using an existing iCloud Keychain credential.
let username = passwordCredential.user
let password = passwordCredential.password
// For the purpose of this demo app, show the password credential as an alert.
DispatchQueue.main.async {
let message = "The app has received your selected credential from the keychain. \n\n Username: \(username)\n Password: \(password)"
let alertController = UIAlertController(title: "Keychain Credential Received",
message: message,
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
// Handle error.
NSString * errorMsg = nil;
switch (error.code) {
case ASAuthorizationErrorCanceled:
errorMsg = @"用户取消了授权请求";
break;
case ASAuthorizationErrorFailed:
errorMsg = @"授权请求失败";
break;
case ASAuthorizationErrorInvalidResponse:
errorMsg = @"授权请求响应无效";
break;
case ASAuthorizationErrorNotHandled:
errorMsg = @"未能处理授权请求";
break;
case ASAuthorizationErrorUnknown:
errorMsg = @"授权请求失败未知原因";
break;
}
}
}
能拿到userID、email、fullName、authorizationCode、identityToken 以及 realUserStatus 等信息。
∙ userID苹果用户唯一标识符,该值在同一个开发者账号下的所有 App下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来。
∙ Verification data: Identity token, authorizationCode,验证数据,用于传给开发者后台服务器,然后开发者服务器再向苹果的身份验证服务端验证本次授权登录请求数据的有效性和真实性,详见 Sign In with Apple REST API。如果验证成功,可以根据 userIdentifier 判断账号是否已存在,若存在,则返回自己账号系统的登录态,若不存在,则创建一个新的账号,并返回对应的登录态给 App。
∙ Account information: Name, verified email,苹果用户信息,包括全名、邮箱等。
∙ Real user indicator: High confidence indicator that likely real user,用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupported、unknown、likelyReal。
意外情况处理
通过上面的步骤一个完整的授权,已经完成。BUT,我们还需要处理一些 Case。
-
用户终止 App 中使用 Sign in with Apple 功能
-
用户在设置里注销了 AppleId
这些情况下,App 需要获取到这些状态,然后做退出登录操作,或者重新登录。
我们需要在 App 启动的时候,通过 getCredentialState:completion: 来获取当前用户的授权状态。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (@available(iOS 13.0, *)) {
NSString *userIdentifier = 钥匙串中取出的 userIdentifier;
if (userIdentifier) {
ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
[appleIDProvider getCredentialStateForUserID:userIdentifier
completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState,
NSError * _Nullable error)
{
switch (credentialState) {
case ASAuthorizationAppleIDProviderCredentialAuthorized:
// The Apple ID credential is valid
break;
case ASAuthorizationAppleIDProviderCredentialRevoked:
// Apple ID Credential revoked, handle unlink
break;
case ASAuthorizationAppleIDProviderCredentialNotFound:
// Credential not found, show login UI
break;
}
}];
}
}
return YES;
}
ASAuthorizationAppleIDProviderCredentialState 解析如下:
∙ASAuthorizationAppleIDProviderCredentialAuthorized 授权状态有效;∙ASAuthorizationAppleIDProviderCredentialRevoked 上次使用苹果账号登录的凭据已被移除,需解除绑定并重新引导用户使用苹果登录;
∙ASAuthorizationAppleIDProviderCredentialNotFound 未登录授权,直接弹出登录页面,引导用户登录。
另外,在 App 使用过程中,你还可以通过通知方法来监听 revoked 状态,可以添加 ASAuthorizationAppleIDProviderCredentialRevokedNotification 这个通知,收到这个通知的时候,我们可以:
∙ Sign user out on this device
∙ Guide to sign in again
具体怎么添加和处理,可以根据业务需求来决定。
- (void)observeAppleSignInState
{
if (@available(iOS 13.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSignInWithAppleStateChanged:)
name:ASAuthorizationAppleIDProviderCredentialRevokedNotification
object:nil];
}
}
- (void)handleSignInWithAppleStateChanged:(NSNotification *)notification
{
// Sign the user out, optionally guide them to sign in again
NSLog(@"%@", notification.userInfo);
}
这是结尾,没了,各位!!!