Sign In With Apple (a) (rpm)

This switched: Sign the Apple With the In (a)

Introduction
I recently learned about the new features of iOS13 Sign In With Apple. Outputs two articles, to share with you. This is the first article, the main content for presentation Sign In With Apple and related nouns, and basic use on the iOS.

A, Sign In With Apple Introduction

Apple is so official introduction to Sign In With Apple

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

Sign In With Apple is a fast, easy way to log in on the app and website.

Sign In with Apple makes it easy for users to sign in to your apps and websites using their Apple ID. Instead of filling out forms, verifying email addresses, and choosing new passwords, they can use Sign In with Apple to set up an account and start using your app right away. All accounts are protected with two-factor authentication for superior security, and Apple will not track users’ activity in your app or website.

For users, Sign In With Apple so they can easily log on apps and websites using Apple ID. Without the need to fill out a form, verification message, select a new password. Users can create new user can immediately begin using your app using the Sign In With Apple. To improve security, the dual-factor authentication to protect the security of the account. And Apple does not track user behavior information in the app and website.

Below briefly explain the dual-factor authentication and Development Sign In With Apple precautions.

1. dual-factor authentication

Here I give an example to explain the dual-factor authentication. such as:

Premise: We have two Apple devices A, B. We already logged on the device A through Apple account QiShare, B equipment are not logged on Apple account.

Requirements: We must also log QiShare account on B

Step: After entering the correct password on the account QiShare B equipment and then we will be prompted on the device B, you need to enter a verification code will be displayed at what position B device to log in QiShare account, agree on the A device. And this authentication code displayed on device A.

I understand the double factor here is that:

1.1 and the corresponding correct password AppleID;
1.2 requires agreed login request has been logged and a verification code on the device account password.

Goes on to describe the top, in order to visually represent information, we can look at a set of graphs below, you should be able to understand what I want to express in the.

  1. QiShare login account on the device B, screenshot omitted.
  2. Tip At this point, the account has been logged QiShare A device as follows:

twoFactorAPrompt

  1. To log QiShare account device B Tips:

twoFactorBPrompt

  1. Account has been logged QiShare device A prompt verification code:

twoFactorAVerificationCode

  1. Tip device B whether to trust the browser, if the browser trust, since you no longer need to log in each account, you need to agree on the A device.

twoFactorBelieveSafari

More details can be viewed: Two-factor authentication for the Apple ID

2. Develop SignInWithApple Notes:

Sign In With Apple 是iOS13 新增的功能,需要使用: MacOS 10.14.4或之后的Mac上的Xcode 11开发。

Xcode 11 includes SDKs for iOS 13, macOS Catalina 10.15, watchOS 6, and tvOS 13. Xcode 11 supports on-device debugging for iOS 8 and later, tvOS 9 and later, and watchOS 2 and later. Xcode 11 requires a Mac running macOS Mojave 10.14.4 or later.

Xcode 11 包含支持iOS13、macOS Catalina 10.15, watchOS 6, and tvOS 13的SDK,Xcode11 支持iOS8、tvOS2 或之后的设备,Xcode11 需要运行在MacOS 10.14.4或之后的Mac上。

Sign In With Apple 是跨平台的,可以支持iOS、macOS、watchOS、tvOS、JS。

二、 iOS Sign In With Apple 流程

使用Sign In With Apple 的流程为:

  1. 设置ASAuthorizationAppleIDButton相关布局,添加相应地授权处理;
  2. 获取授权码;
  3. 验证;
  4. 处理Sign In With Apple授权状态变化;

下边笔者展开描述下iOS 使用Sign In With Apple的准备工作、可能遇到的问题及流程。

1. iOS 使用Sign In With Apple的准备工作:

1.1在Xcode11 Signing & capabilities 中添加 Sign In With Apple

2. iOS 使用Sign In With Apple可能遇到的问题:

2.1 开启双重因子验证的方式:

  • 双重因子验证的开启:设置 -> 密码与安全性 -> 双重因子验证;
    如果不开启双重因子验证,那么当我们在调用苹果官方授权接口的时候,系统也会提示我们需要去打开双重因子验证。

2.2 停止App 使用Sign In With Apple 的方式:

  • 停止App 使用Sign In With Apple:设置 -> 密码与安全性 -> 使用您AppleID的App -> 找到对应的App - > “停止以Apple ID使用 Bundle ID...”;

3. iOS使用Sign In With Apple 的开发流程:

在介绍布局之前先看下,笔者先给大家看下界面效果:

Sign In With Apple

界面比较简单,界面上方为UITextView用于展示授权状态授权信息,"Sign In With Apple Button" 是用的官方的ASAuthorizationAppleIDButton。中间的“移除键盘”按钮用于移除键盘。

3.1设置ASAuthorizationAppleIDButton相关布局,添加相应地授权处理;

- (void)setupUI {
    
    // 用于展示Sign In With Apple 登录过程的信息
    _appleIDInfoTextView = [[UITextView alloc] initWithFrame:CGRectMake(.0, 40.0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) * 0.4) textContainer:nil];
    _appleIDInfoTextView.font = [UIFont systemFontOfSize:32.0];
    [self.view addSubview:_appleIDInfoTextView];
    
    // 移除键盘Button
    UIButton *removeKeyboardBtn = [[UIButton alloc] init];
    removeKeyboardBtn.backgroundColor = [UIColor grayColor];
    [removeKeyboardBtn setTitle:@"移除键盘" forState:UIControlStateNormal];
    removeKeyboardBtn.frame = CGRectMake(CGRectGetMidX(_appleIDInfoTextView.frame) - 50.0, CGRectGetMaxY(_appleIDInfoTextView.frame), 100.0, 40.0);
    [removeKeyboardBtn addTarget:self action:@selector(removeFirstResponder:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:removeKeyboardBtn];

    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];
    }
    
    NSMutableString *mStr = [NSMutableString string];
    [mStr appendString:@"显示Sign In With Apple 登录信息\n"];
    _appleIDInfoTextView.text = [mStr copy];
}

#pragma mark - Actions

//! 处理授权
- (void)handleAuthrization:(UIButton *)sender {
    if (@available(iOS 13.0, *)) {
        // A mechanism for generating requests to authenticate users based on their Apple ID.
        // 基于用户的Apple ID授权用户,生成用户授权请求的一种机制
        ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
        // Creates a new Apple ID authorization request.
        // 创建新的AppleID 授权请求
        ASAuthorizationAppleIDRequest *request = appleIDProvider.createRequest;
        // The contact information to be requested from the user during authentication.
        // 在用户授权期间请求的联系信息
        request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
        // A controller that manages authorization requests created by a provider.
        // 由ASAuthorizationAppleIDProvider创建的授权请求 管理授权请求的控制器
        ASAuthorizationController *controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
        // A delegate that the authorization controller informs about the success or failure of an authorization attempt.
        // 设置授权控制器通知授权请求的成功与失败的代理
        controller.delegate = self;
        // A delegate that provides a display context in which the system can present an authorization interface to the user.
        // 设置提供 展示上下文的代理,在这个上下文中 系统可以展示授权界面给用户
        controller.presentationContextProvider = self;
        // starts the authorization flows named during controller initialization.
        // 在控制器初始化期间启动授权流
        [controller performRequests];
    }
}

关于ASAuthorizationAppleIDButton的设计规范,可以查看:Human Interface Guidelines 之 Sign In with Apple

3.2 获取授权码

获取授权码这部分主要看2个代理ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding,及相应的代理方法中的实现。

ASAuthorizationControllerDelegate
An interface for providing information about the outcome of an authorization request.
提供关于授权请求结果信息的接口

ASAuthorizationControllerPresentationContextProviding:
An interface the controller uses to ask a delegate for a presentation context.
控制器的代理找一个展示授权控制器的上下文的接口

下边为实现代理方法的代码:

#pragma mark - Delegate

//! 授权成功地回调
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization  API_AVAILABLE(ios(13.0)){
    
    NSLog(@"%s", __FUNCTION__);
    NSLog(@"%@", controller);
    NSLog(@"%@", authorization);
    
    NSLog(@"authorization.credential:%@", authorization.credential);
    
    NSMutableString *mStr = [NSMutableString string];
    mStr = [_appleIDInfoTextView.text mutableCopy];
    
    if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
        // 用户登录使用ASAuthorizationAppleIDCredential
        ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
        NSString *user = appleIDCredential.user;
        //  需要使用钥匙串的方式保存用户的唯一信息 这里暂且处于测试阶段 是否的NSUserDefaults
        [[NSUserDefaults standardUserDefaults] setValue:user forKey:QiShareCurrentIdentifier];
        [mStr appendString:user?:@""];
        NSString *familyName = appleIDCredential.fullName.familyName;
        [mStr appendString:familyName?:@""];
        NSString *givenName = appleIDCredential.fullName.givenName;
        [mStr appendString:givenName?:@""];
        NSString *email = appleIDCredential.email;
        [mStr appendString:email?:@""];
        NSLog(@"mStr:%@", mStr);
        [mStr appendString:@"\n"];
        _appleIDInfoTextView.text = mStr;
        
    } else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {
        // 用户登录使用现有的密码凭证
        ASPasswordCredential *passwordCredential = authorization.credential;
        // 密码凭证对象的用户标识 用户的唯一标识
        NSString *user = passwordCredential.user;
        // 密码凭证对象的密码
        NSString *password = passwordCredential.password;
        [mStr appendString:user?:@""];
        [mStr appendString:password?:@""];
        [mStr appendString:@"\n"];
        NSLog(@"mStr:%@", mStr);
        _appleIDInfoTextView.text = mStr;
    } else {
        NSLog(@"授权信息均不符");
        mStr = [@"授权信息均不符" mutableCopy];
        _appleIDInfoTextView.text = mStr;
    }
}

//! 授权失败的回调
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error  API_AVAILABLE(ios(13.0)){
    
    NSLog(@"%s", __FUNCTION__);
    NSLog(@"错误信息:%@", 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;
    }
    
    NSMutableString *mStr = [_appleIDInfoTextView.text mutableCopy];
    [mStr appendString:errorMsg];
    [mStr appendString:@"\n"];
    _appleIDInfoTextView.text = [mStr copy];
    
    if (errorMsg) {
        return;
    }
    
    if (error.localizedDescription) {
        NSMutableString *mStr = [_appleIDInfoTextView.text mutableCopy];
        [mStr appendString:error.localizedDescription];
        [mStr appendString:@"\n"];
        _appleIDInfoTextView.text = [mStr copy];
    }
    NSLog(@"controller requests:%@", controller.authorizationRequests);
    /* // 取消授权的时候也会调用这里
     ((ASAuthorizationAppleIDRequest *)(controller.authorizationRequests[0])).requestedScopes
     <__NSArrayI 0x2821e2520>(
     full_name,
     email
     )
     */
}


//! Tells the delegate from which window it should present content to the user.
//! 告诉代理应该在哪个window 展示内容给用户
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller  API_AVAILABLE(ios(13.0)){
    
    NSLog(@"调用展示window方法:%s", __FUNCTION__);
    // 返回window
    return self.view.window;
}

授权登录成功后调试的时候查看到的用户信息相关内容:

 po appleIDCredential.authorizationCode
<63636435 32316262 32666464 30346130 62616366 65336439 32636564 34383666 622e302e 6d727371 7a2e5853 47686543 5f354f6e 48786838 32766670 50484377>

(lldb) po appleIDCredential.user
0012xx.d81c1988bb054e91beec303a4xxxxxxx.0xx6

(lldb) po appleIDCredential.fullName
<NSPersonNameComponents: 0x281bf2070> {givenName = YW, familyName = W, middleName = (null), namePrefix = (null), nameSuffix = (null), nickname = (null) phoneticRepresentation = (null) }

(lldb) po appleIDCredential.email
[email protected]

已经使用Sign In With Apple登录过app的用户

执行已经登录过的场景。如果设备中存在iCloud Keychain 凭证或者AppleID 凭证提示用户直接使用TouchID或FaceID登录即可。

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    [self perfomExistingAccountSetupFlows];
}

//! Prompts the user if an existing iCloud Keychain credential or Apple ID credential is found.
//! 如果存在iCloud Keychain 凭证或者AppleID 凭证提示用户
- (void)perfomExistingAccountSetupFlows {
    if (@available(iOS 13.0, *)) {
        // A mechanism for generating requests to authenticate users based on their Apple ID.
        // 基于用户的Apple ID授权用户,生成用户授权请求的一种机制
        ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
        // An OpenID authorization request that relies on the user’s Apple ID.
        // 授权请求依赖于用于的AppleID
        ASAuthorizationAppleIDRequest *authAppleIDRequest = [appleIDProvider createRequest];
        // A mechanism for generating requests to perform keychain credential sharing.
        // 为了执行钥匙串凭证分享生成请求的一种机制
        ASAuthorizationPasswordRequest *passwordRequest = [[ASAuthorizationPasswordProvider new] createRequest];
        
        NSMutableArray <ASAuthorizationRequest *>* mArr = [NSMutableArray arrayWithCapacity:2];
        if (authAppleIDRequest) {
            [mArr addObject:authAppleIDRequest];
        }
        if (passwordRequest) {
            [mArr addObject:passwordRequest];
        }
        // ASAuthorizationRequest:A base class for different kinds of authorization requests.
        // ASAuthorizationRequest:对于不同种类授权请求的基类
        NSArray <ASAuthorizationRequest *>* requests = [mArr copy];

        // A controller that manages authorization requests created by a provider.
        // 由ASAuthorizationAppleIDProvider创建的授权请求 管理授权请求的控制器
        // Creates a controller from a collection of authorization requests.
        // 从一系列授权请求中创建授权控制器
        ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:requests];
        // A delegate that the authorization controller informs about the success or failure of an authorization attempt.
        // 设置授权控制器通知授权请求的成功与失败的代理
        authorizationController.delegate = self;
        // A delegate that provides a display context in which the system can present an authorization interface to the user.
        // 设置提供 展示上下文的代理,在这个上下文中 系统可以展示授权界面给用户
        authorizationController.presentationContextProvider = self;
        // starts the authorization flows named during controller initialization.
        // 在控制器初始化期间启动授权流
        [authorizationController performRequests];
    }
}

3.3 Verification

关于验证的这一步,服务端同学LY和笔者都认为是需要传递授权码给自己的服务端,自己的服务端调用苹果APIGenerate and validate tokens去校验授权码。但是相关的API 调用不通。总是提示grant_type 有问题。不过我们服务端同学LY在PC端使用Sign In With Apple做过相应地测试,发现苹果提供的授权码校验的API是通的。当然相关内容也可能是笔者理解有误,大家如果有不同的理解,敬请讨论。

{
    "error": "unsupported_grant_type"
}

所以在这一步,笔者还没有较好的解决办法。大家需要使用相关功能的,可以根据业务场景,考虑下其他的处理方式。

3.4监听授权状态变化

监听授权状态改变,并且做出相应处理。授权状态有:

ASAuthorizationAppleIDProviderCredentialRevoked:授权状态失效(用户停止使用AppID 登录App)、
ASAuthorizationAppleIDProviderCredentialAuthorized:已授权(已使用AppleID 登录过App)、
ASAuthorizationAppleIDProviderCredentialNotFound:授权凭证缺失(可能是使用AppleID 登录过App)

处理改变有2种处理方式,一种是通过通知的方式,另一种是监听当前的appleIDCredential.user 的授权状态。

3.4.1 监听appleIDCredential.user 的授权状态,这部分代码可以放到AppDelegate的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中,判断是否需要展示出登录控制器。

#pragma mark - Private functions
//! 观察授权状态
- (void)observeAuthticationState {
    
    if (@available(iOS 13.0, *)) {
        // A mechanism for generating requests to authenticate users based on their Apple ID.
        // 基于用户的Apple ID 生成授权用户请求的机制
        ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
        // 注意 存储用户标识信息需要使用钥匙串来存储 这里笔者简单期间 使用NSUserDefaults 做的简单示例
        NSString *userIdentifier = [[NSUserDefaults standardUserDefaults] valueForKey:QiShareCurrentIdentifier];
        
        if (userIdentifier) {
            NSString* __block errorMsg = nil;
            //Returns the credential state for the given user in a completion handler.
            // 在回调中返回用户的授权状态
            [appleIDProvider getCredentialStateForUserID:userIdentifier completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) {
                switch (credentialState) {
                        // 苹果证书的授权状态
                    case ASAuthorizationAppleIDProviderCredentialRevoked:
                        // 苹果授权凭证失效
                        errorMsg = @"苹果授权凭证失效";
                        break;
                    case ASAuthorizationAppleIDProviderCredentialAuthorized:
                        // 苹果授权凭证状态良好
                        errorMsg = @"苹果授权凭证状态良好";
                        break;
                    case ASAuthorizationAppleIDProviderCredentialNotFound:
                        // 未发现苹果授权凭证
                        errorMsg = @"未发现苹果授权凭证";
                        // 可以引导用户重新登录
                        break;
                }
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"SignInWithApple授权状态变化情况");
                    NSLog(@"%@", errorMsg);
                });
            }];
            
        }
    }
}

3.4.2使用通知的方式检测是否授权应用支持Sign In With Apple变化情况。如下的代码可以根据自己的业务场景去考虑放置的位置。


//! 添加苹果登录的状态通知
- (void)observeAppleSignInState {
    if (@available(iOS 13.0, *)) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(handleSignInWithAppleStateChanged:) name:ASAuthorizationAppleIDProviderCredentialRevokedNotification object:nil];
    }
}

//! 观察SignInWithApple状态改变
- (void)handleSignInWithAppleStateChanged:(id)noti {
    
    NSLog(@"%s", __FUNCTION__);
    NSLog(@"%@", noti);
}

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

相关示意图如下:

  1. 首次使用AppleID登录或者停止使用AppleID登录后再次使用Sign In With Apple的提示如下:

After the first use AppleID Login or stop using the AppleID log in using Sign In With Apple again

  1. 使用Sign In With Apple登录成功的截图如下:

Sign In With Apple using the login is successful

  1. 使用过AppleID登录过App,进入应用的时候会提示使用TouchID登录的场景如下:

Used AppleID logged App, the application will be prompted to enter when using the following scene TouchID Login

  1. 使用Sign In With Apple登录成功的截图如下:

Sign In With Apple using the login is successful

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 将在今年夏天可以用于beta版测试。对于支持三方登录的apps,在今年晚些时候iOS13的新设备出售的时候,Sign In With Apple 将被要求作为一种登录选择。
https://developer.apple.com/news/?id=06032019j

笔者对这段话的理解是,对于支持三方登录的应用,需提供Sign In With Apple选项登录。

Demo

QiSignInWithApple

Swift版官方Sign In With Apple

参考学习网址

Guess you like

Origin www.cnblogs.com/ITCoderW/p/11231257.html
rpm