最近了解了iOS 13新增功能之Sign In with Apple,Sign In with Apple是跨平台的,可以支持iOS、macOS、watchOS、tvOS、JS。本文主要內容為Sign In with Apple在iOS上的基礎使用。詳情參考WWDC 2019
- 審核備注
Sign In with Apple will be available for beta testing this summer. It will be required as an option for users in apps that support third-party sign-in when it is commercially available later this year.
也就是說,當 Sign In with Apple 服務正式上線以后,所有已接入其它第三方登錄的 App,Sign In with Apple 將被要求作為一種登錄選擇,否則有可能就不給過。
開發Sign In with Apple的注意事項
需要在蘋果后台打開該選項,並且重新生成Profiles配置文件,並安裝到Xcode,如下圖

iOS使用Sign In with Apple在Xcode的准備工作
在Xcode11 Signing & Capabilities中添加Sign In With Apple,如下圖

-
iOS Sign In with Apple流程
1、添加 Sign In with Apple 登錄按鈕,設置 ASAuthorizationAppleIDButton 相關布局,並添加按鈕點擊響應事件 2、獲取授權碼 3、驗證
1、添加Sign In with Apple登錄按鈕,設置ASAuthorizationAppleIDButton相關布局,並添加按鈕點擊響應事件。
當然蘋果也允許自定義蘋果登錄按鈕的樣式,樣式要求詳見這個文檔:Human Interface Guidelines
- (void)configUI{
// 用於展示Sign In With Apple 登錄過程的信息
_appleIDInfoLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 40.0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) * 0.4)];
_appleIDInfoLabel.font = [UIFont systemFontOfSize:22.0];
_appleIDInfoLabel.numberOfLines = 0;
_appleIDInfoLabel.lineBreakMode = NSLineBreakByWordWrapping;
_appleIDInfoLabel.text = @"顯示Sign In With Apple 登錄信息\n";
[self.view addSubview:_appleIDInfoLabel];
if (@available(iOS 13.0, *)) {
// Sign In With Apple Button
ASAuthorizationAppleIDButton *appleIDBtn = [ASAuthorizationAppleIDButton buttonWithType:ASAuthorizationAppleIDButtonTypeDefault style:ASAuthorizationAppleIDButtonStyleWhite];
appleIDBtn.frame = CGRectMake(30, self.view.bounds.size.height - 180, self.view.bounds.size.width - 60, 100);
// appleBtn.cornerRadius = 22.f;
[appleIDBtn addTarget:self action:@selector(handleAuthorizationAppleIDButtonPress) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:appleIDBtn];
}
}
// 處理授權
- (void)handleAuthorizationAppleIDButtonPress{
NSLog(@"////////");
if (@available(iOS 13.0, *)) {
// 基於用戶的Apple ID授權用戶,生成用戶授權請求的一種機制
ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
// 創建新的AppleID 授權請求
ASAuthorizationAppleIDRequest *appleIDRequest = [appleIDProvider createRequest];
// 在用戶授權期間請求的聯系信息
appleIDRequest.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
// 由ASAuthorizationAppleIDProvider創建的授權請求 管理授權請求的控制器
ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest]];
// 設置授權控制器通知授權請求的成功與失敗的代理
authorizationController.delegate = self;
// 設置提供 展示上下文的代理,在這個上下文中 系統可以展示授權界面給用戶
authorizationController.presentationContextProvider = self;
// 在控制器初始化期間啟動授權流
[authorizationController performRequests];
}
}
- 已經使用
Sign In with Apple登錄過app的用戶
如果設備中存在iCloud Keychain憑證或者AppleID憑證,提示用戶直接使用TouchID或FaceID登錄即可,代碼如下
// 如果存在iCloud Keychain 憑證或者AppleID 憑證提示用戶
- (void)perfomExistingAccountSetupFlows{
NSLog(@"///已經認證過了/////");
if (@available(iOS 13.0, *)) {
// 基於用戶的Apple ID授權用戶,生成用戶授權請求的一種機制
ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
// 授權請求AppleID
ASAuthorizationAppleIDRequest *appleIDRequest = [appleIDProvider createRequest];
// 為了執行鑰匙串憑證分享生成請求的一種機制
ASAuthorizationPasswordProvider *passwordProvider = [[ASAuthorizationPasswordProvider alloc] init];
ASAuthorizationPasswordRequest *passwordRequest = [passwordProvider createRequest];
// 由ASAuthorizationAppleIDProvider創建的授權請求 管理授權請求的控制器
ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest, passwordRequest]];
// 設置授權控制器通知授權請求的成功與失敗的代理
authorizationController.delegate = self;
// 設置提供 展示上下文的代理,在這個上下文中 系統可以展示授權界面給用戶
authorizationController.presentationContextProvider = self;
// 在控制器初始化期間啟動授權流
[authorizationController performRequests];
}
}
2、獲取授權碼
獲取授權碼需要在代碼中實現兩個代理回調ASAuthorizationControllerDelegate、ASAuthorizationControllerPresentationContextProviding分別用於處理授權登錄成功和失敗、以及提供用於展示授權頁面的Window,代碼如下
#pragma mark - delegate
//@optional 授權成功地回調
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization{
NSLog(@"授權完成:::%@", authorization.credential);
NSLog(@"%s", __FUNCTION__);
NSLog(@"%@", controller);
NSLog(@"%@", authorization);
// 測試配置UI顯示
NSMutableString *mStr = [NSMutableString string];
if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
// 用戶登錄使用ASAuthorizationAppleIDCredential
ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
NSString *user = appleIDCredential.user;
NSString *familyName = appleIDCredential.fullName.familyName;
NSString *givenName = appleIDCredential.fullName.givenName;
NSString *email = appleIDCredential.email;
// NSData *identityToken = appleIDCredential.identityToken;
// NSData *authorizationCode = appleIDCredential.authorizationCode;
// Create an account in your system.
// For the purpose of this demo app, store the userIdentifier in the keychain.
// 需要使用鑰匙串的方式保存用戶的唯一信息
[YostarKeychain save:KEYCHAIN_IDENTIFIER(@"userIdentifier") data:user];
[mStr appendString:user];
[mStr appendString:@"\n"];
[mStr appendString:familyName];
[mStr appendString:@"\n"];
[mStr appendString:givenName];
[mStr appendString:@"\n"];
[mStr appendString:email];
NSLog(@"mStr:::%@", mStr);
[mStr appendString:@"\n"];
_appleIDInfoLabel.text = mStr;
}else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]){
// Sign in using an existing iCloud Keychain credential.
// 用戶登錄使用現有的密碼憑證
ASPasswordCredential *passwordCredential = authorization.credential;
// 密碼憑證對象的用戶標識 用戶的唯一標識
NSString *user = passwordCredential.user;
// 密碼憑證對象的密碼
NSString *password = passwordCredential.password;
[mStr appendString:user];
[mStr appendString:@"\n"];
[mStr appendString:password];
[mStr appendString:@"\n"];
NSLog(@"mStr:::%@", mStr);
_appleIDInfoLabel.text = mStr;
}else{
NSLog(@"授權信息均不符");
mStr = [@"授權信息均不符" copy];
_appleIDInfoLabel.text = mStr;
}
}
// 授權失敗的回調
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error{
// Handle error.
NSLog(@"Handle error:%@", 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;
default:
break;
}
NSMutableString *mStr = [_appleIDInfoLabel.text mutableCopy];
[mStr appendString:@"\n"];
[mStr appendString:errorMsg];
[mStr appendString:@"\n"];
_appleIDInfoLabel.text = mStr;
}
// 告訴代理應該在哪個window 展示內容給用戶
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller{
NSLog(@"88888888888");
// 返回window
return self.view.window;
}
在授權登錄成功回調中,我們可以拿到以下幾類數據
- UserID:
Unique, stable, team-scoped user ID,蘋果用戶唯一標識符,該值在同一個開發者賬號下的所有App下是一樣的,開發者可以用該唯一標識符與自己后台系統的賬號體系綁定起來(這與國內的微信、QQ、微博等第三方登錄流程基本一致) - Verification data:
Identity token, code,驗證數據,用於傳給開發者后台服務器,然后開發者服務器再向蘋果的身份驗證服務端驗證,本次授權登錄請求數據的有效性和真實性,詳見Sign In with Apple REST API - Account information:
Name, verified email,蘋果用戶信息,包括全名、郵箱等,注意:如果玩家登錄時拒絕提供真實的郵箱賬號,蘋果會生成虛擬的郵箱賬號
3、驗證
關於驗證的這一步,需要傳遞授權碼給自己的服務端,自己的服務端調用蘋果API去校驗授權碼Generate and validate tokens。如果驗證成功,可以根據userIdentifier判斷賬號是否已存在,若存在,則返回自己賬號系統的登錄態,若不存在,則創建一個新的賬號,並返回對應的登錄狀態給App
附:官方示例代碼 Swift 版
附:What the Heck is Sign In with Apple?
附:蘋果授權登陸后端驗證
附:App Store審核指南的更新
附:Generate and validate tokens
附:SignInAppleDemo
