Sign In AppleID

第一步:導(dǎo)入庫文件

#import <AuthenticationServices/AuthenticationServices.h>

第二步:繪制登錄按鈕,也可以自定義

-(void)setupUI{
    
    if (@available(iOS 13.0, *)) {
       // Sign In With Apple Button
       ASAuthorizationAppleIDButton *appleIDButton = [ASAuthorizationAppleIDButton new];
           
       appleIDButton.frame =  CGRectMake(.0, .0, CGRectGetWidth(self.view.frame) - 40.0, 100.0);
       CGPoint origin = CGPointMake(20.0, CGRectGetMidY(self.view.frame));
       CGRect frame = appleIDButton.frame;
       frame.origin = origin;
       appleIDButton.frame = frame;
       appleIDButton.cornerRadius = CGRectGetHeight(appleIDButton.frame) * 0.25;
       [self.view addSubview:appleIDButton];
       [appleIDButton addTarget:self action:@selector(handleAuthrization:) forControlEvents:UIControlEventTouchUpInside];
       
    }
    
}

第三步:發(fā)起授權(quán)

//! 處理授權(quán)
- (void)handleAuthrization:(UIButton *)sender {
    if (@available(iOS 13.0, *)) {
        
        // 基于用戶的Apple ID授權(quán)用戶,生成用戶授權(quán)請(qǐng)求的一種機(jī)制
        ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
        
        // 創(chuàng)建新的AppleID 授權(quán)請(qǐng)求
        ASAuthorizationAppleIDRequest *request = appleIDProvider.createRequest;
        // 在用戶授權(quán)期間請(qǐng)求的聯(lián)系信息
        request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
       
        /*
        // 為了執(zhí)行鑰匙串憑證分享生成請(qǐng)求的一種機(jī)制,暫時(shí)好像沒啥用
        ASAuthorizationPasswordProvider *passwordProvider = [[ASAuthorizationPasswordProvider alloc] init];
        ASAuthorizationPasswordRequest *passwordRequest = [passwordProvider createRequest];
        */
        
        // 由ASAuthorizationAppleIDProvider創(chuàng)建的授權(quán)請(qǐng)求 管理授權(quán)請(qǐng)求的控制器
        ASAuthorizationController *controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
        
        // 設(shè)置授權(quán)控制器通知授權(quán)請(qǐng)求的成功與失敗的代理
        controller.delegate = self;
        
        // 設(shè)置提供 展示上下文的代理,在這個(gè)上下文中 系統(tǒng)可以展示授權(quán)界面給用戶
        controller.presentationContextProvider = self;
        
        // 在控制器初始化期間啟動(dòng)授權(quán)流
        [controller performRequests];
        
    }
}

第四步:授權(quán)結(jié)果處理

#pragma mark - Delegate
//! 授權(quán)成功地回調(diào)
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization  API_AVAILABLE(ios(13.0)){
    
    NSLog(@"authorization.credential:%@", authorization.credential);
    
    NSMutableString *mStr = [NSMutableString string];
    if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
        // 用戶登錄使用ASAuthorizationAppleIDCredential
        ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
        
        ///憑證信息
        //用戶唯一ID
        NSString *user = appleIDCredential.user;
        //授權(quán)驗(yàn)證信息
        NSData *token = appleIDCredential.identityToken;
        
        //用戶名,郵箱信息(只有第一次授權(quán)才會(huì)帶回來,以后授權(quán)成功都是返回空)
        NSString *familyName = appleIDCredential.fullName.familyName;
        NSString *givenName = appleIDCredential.fullName.givenName;
        NSString *email = appleIDCredential.email;
        
    } else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {
        
        // 這個(gè)獲取的是iCloud記錄的賬號(hào)密碼,需要輸入框支持iOS 12 記錄賬號(hào)密碼的新特性
        //這個(gè)回調(diào)我是沒有測(cè)試出來過,不知道怎么搞
        // 用戶登錄使用現(xiàn)有的密碼憑證
        ASPasswordCredential *passwordCredential = authorization.credential;
        // 密碼憑證對(duì)象的用戶標(biāo)識(shí) 用戶的唯一標(biāo)識(shí)
        NSString *user = passwordCredential.user;
        // 注意 存儲(chǔ)用戶標(biāo)識(shí)信息需要使用鑰匙串來存儲(chǔ) 這里筆者簡(jiǎn)單期間 使用NSUserDefaults 做的簡(jiǎn)單示例
        [[NSUserDefaults standardUserDefaults] setObject:user forKey:@"userIdentifier"];
        // 密碼憑證對(duì)象的密碼
        NSString *password = passwordCredential.password;
        
    } else {

        NSLog(@"授權(quán)信息均不符");
    
    }
    
}

//! 授權(quán)失敗的回調(diào)
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error  API_AVAILABLE(ios(13.0)){
    
    NSLog(@"%s", __FUNCTION__);
    NSLog(@"錯(cuò)誤信息:%@", error);
    NSString *errorMsg = nil;
    switch (error.code) {
        case ASAuthorizationErrorCanceled:
            errorMsg = @"用戶取消了授權(quán)請(qǐng)求";
            break;
        case ASAuthorizationErrorFailed:
            errorMsg = @"授權(quán)請(qǐng)求失敗";
            break;
        case ASAuthorizationErrorInvalidResponse:
            errorMsg = @"授權(quán)請(qǐng)求響應(yīng)無效";
            break;
        case ASAuthorizationErrorNotHandled:
            errorMsg = @"未能處理授權(quán)請(qǐng)求";
            break;
        case ASAuthorizationErrorUnknown:
            errorMsg = @"授權(quán)請(qǐng)求失敗未知原因";
            break;
    }
    NSLog(@"error:%@", errorMsg);
    
}


//告訴代理應(yīng)該在哪個(gè)window 展示內(nèi)容給用戶
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller  API_AVAILABLE(ios(13.0)){
    
    NSLog(@"調(diào)用展示window方法:%s", __FUNCTION__);
    // 返回window
    return self.view.window;
}

在授權(quán)登錄成功回調(diào)中,我們可以拿到以下幾類數(shù)據(jù)

  • UserID:Unique, stable, team-scoped user ID,蘋果用戶唯一標(biāo)識(shí)符,該值在同一個(gè)開發(fā)者賬號(hào)下的所有App下是一樣的,開發(fā)者可以用該唯一標(biāo)識(shí)符與自己后臺(tái)系統(tǒng)的賬號(hào)體系綁定起來(這與國內(nèi)的微信、QQ、微博等第三方登錄流程基本一致)
  • Verification data:Identity token, code,驗(yàn)證數(shù)據(jù),用于傳給開發(fā)者后臺(tái)服務(wù)器,然后開發(fā)者服務(wù)器再向蘋果的身份驗(yàn)證服務(wù)端驗(yàn)證,本次授權(quán)登錄請(qǐng)求數(shù)據(jù)的有效性和真實(shí)性,詳見Sign In with Apple REST API
  • Account information:Name, verified email,蘋果用戶信息,包括全名、郵箱等,注意:如果玩家登錄時(shí)拒絕提供真實(shí)的郵箱賬號(hào),蘋果會(huì)生成虛擬的郵箱賬號(hào),而且記錄過的蘋果賬號(hào)再次登錄這些參數(shù)拿不到

驗(yàn)證
關(guān)于驗(yàn)證的這一步,需要傳遞授權(quán)碼給自己的服務(wù)端,自己的服務(wù)端調(diào)用蘋果API去校驗(yàn)授權(quán)碼Generate and validate tokens。如果驗(yàn)證成功,可以根據(jù)userIdentifier判斷賬號(hào)是否已存在,若存在,則返回自己賬號(hào)系統(tǒng)的登錄態(tài),若不存在,則創(chuàng)建一個(gè)新的賬號(hào),并返回對(duì)應(yīng)的登錄狀態(tài)給App

  • 推薦驗(yàn)證步驟為:
  • 服務(wù)端拿authorizationCode去蘋果后臺(tái)驗(yàn)證,驗(yàn)證地址https://appleid.apple.com/auth/token,蘋果返回id_token,與客戶端獲取的identityToken值一樣,格式如下
{
    "access_token": "一個(gè)token",
    "token_type": "Bearer",
    "expires_in": 3600,
    "refresh_token": "一個(gè)token",
    "id_token": "結(jié)果是JWT,字符串形式,identityToken"
}

另外授權(quán)code是有時(shí)效性的,且使用一次即失效

  • 服務(wù)器拿到相應(yīng)結(jié)果后,其中id_tokenJWT數(shù)據(jù),解碼id_token,得到如下內(nèi)容
{
    "iss":"https://appleid.apple.com",
    "aud":"這個(gè)是你的app的bundle identifier",
    "exp":1567482337,
    "iat":1567481737,
    "sub":"這個(gè)字段和客戶端獲取的user字段是完全一樣的",
    "c_hash":"8KDzfalU5kygg5zxXiX7dA",
    "auth_time":1567481737
}

其中aud與你appbundleID一致,sub就是授權(quán)用戶的唯一標(biāo)識(shí),與手機(jī)端獲得的user一致,服務(wù)器端通過對(duì)比sub字段信息是否與手機(jī)端上傳的user信息一致來確定是否成功登錄
token的有效期是10分鐘,具體后端驗(yàn)證參考附錄

第五步:已授權(quán)狀態(tài)獲取與監(jiān)聽

1.獲取已授權(quán)用戶當(dāng)前狀態(tài)

- (void)authorizationCredentialState{
    
    if (@available(iOS 13.0, *)) {
        
        // 基于用戶的Apple ID 生成授權(quán)用戶請(qǐng)求的機(jī)制
        ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
        
        // 注意 存儲(chǔ)用戶標(biāo)識(shí)信息需要使用鑰匙串來存儲(chǔ) 這里筆者簡(jiǎn)單期間 使用NSUserDefaults 做的簡(jiǎn)單示例
        NSString *userIdentifier = [[NSUserDefaults standardUserDefaults] objectForKey:@"userIdentifier"];
        
        if (userIdentifier) {
            
            __block NSString *errorMsg = nil;
            //Returns the credential state for the given user in a completion handler.
            // 在回調(diào)中返回用戶的授權(quán)狀態(tài)
            [appleIDProvider getCredentialStateForUserID:userIdentifier completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) {
                
                switch (credentialState) {
                        // 蘋果證書的授權(quán)狀態(tài)
                    case ASAuthorizationAppleIDProviderCredentialRevoked:
                        // 蘋果授權(quán)憑證失效,退出登錄,重新授權(quán)
                        errorMsg = @"蘋果授權(quán)憑證失效";
                        break;
                    case ASAuthorizationAppleIDProviderCredentialAuthorized:
                        // 蘋果授權(quán)憑證狀態(tài)良好
                        errorMsg = @"蘋果授權(quán)憑證狀態(tài)良好";
                        break;
                    case ASAuthorizationAppleIDProviderCredentialNotFound:
                        // 未發(fā)現(xiàn)蘋果授權(quán)憑證,退出登錄,重新授權(quán)
                        errorMsg = @"未發(fā)現(xiàn)蘋果授權(quán)憑證";
                        break;
                        // 可以引導(dǎo)用戶重新登錄
                    case ASAuthorizationAppleIDProviderCredentialTransferred:
                        errorMsg = @"蘋果授權(quán)信息變動(dòng)";
                        break;
                }
            }];  
        }
    }   
}

2.使用通知的方式監(jiān)聽授權(quán)狀態(tài)變化

//! 添加蘋果登錄的狀態(tài)通知
- (void)observeAppleSignInState {
    if (@available(iOS 13.0, *)) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(handleSignInWithAppleStateChanged:) name:ASAuthorizationAppleIDProviderCredentialRevokedNotification object:nil];
    }
}

//! 觀察SignInWithApple狀態(tài)改變
- (void)handleSignInWithAppleStateChanged:(id)noti {
    
    NSLog(@"%s", __FUNCTION__);
    NSLog(@"%@", noti);
}

- (void)dealloc {
    
    if (@available(iOS 13.0, *)) {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:ASAuthorizationAppleIDProviderCredentialRevokedNotification object:nil];
    }
}

參考:http://www.itdecent.cn/p/e1284bd8c72a
附:官方示例代碼 Swift 版
附:What the Heck is Sign In with Apple?
附:Sign In with Apple 從登陸到服務(wù)器驗(yàn)證
附:蘋果授權(quán)登陸后端驗(yàn)證
附:[官方文檔] Generate and validate tokens
附:[官方文檔] App Store審核指南
附:SignInAppleDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容