iOS 实现苹果第三方登录

Sign in with Apple

Provide users the ability to sign in to your apps and websites using their Apple ID.

Overview

Sign in with Apple gives your users a fast and safe way to sign in to your apps and websites using their Apple ID. Incorporating Sign in with Apple eliminates the need for additional sign-up steps, allowing users to engage and focus on your app or website.

To support Sign in with Apple for websites, you must integrate Sign in with Apple JS. Use the Sign in with Apple REST API to communicate with Apple servers. For native iOS, macOS, tvOS, and watchOS apps, use the AuthenticationServices framework.

简介

Sign In with Apple登录支持的最低iOS系统要求是iOS13,凡是包含第三方登录功能的App,必须带有苹果登录功能,且样式需要满足苹果要求,并需要把苹果登录放在所有第三方登录的前面,也就是说要放在第一个位置上,上架应用被要求添加苹果登录功能的最终截止时间不详。不过可以肯定的是,在7月30日我提交了审核,8月1日被拒了,原因就是没有添加苹果登录功能。

image.png
image.png

苹果提供文档之Implementing User Authentication with Sign in with Apple
苹果提供文档之Sign in with Apple
苹果提供文档之Human Interface Guidelines

实现

  • 1、登录苹果开发者后台,把需要添加苹果登录Identifiers的项目打开登录开关
image.png

保存后,下载.p8文件,文件下载完要保存好,因为只提供一次下载机会

  • 2、添加key
image.png

添加完可以直接在下面的页面看到keyID和teamID,都复制一下,后面需要用到

image.png
  • 3、打开项目,按照下图中1、2步添加Sign In with Apple
image.png
  • 4、到登录页面添加代码
    导入系统头文件#import
    实现两个代理方法ASAuthorizationControllerDelegateASAuthorizationControllerPresentationContextProviding
    添加苹果登录按钮,按钮ASAuthorizationAppleIDButton是系统提供的,只要实现了两个代理就能轻松获取登录用户的信息,代码如下:

按钮布局

if (@available(iOS 13.0, *)) {
                    
    ASAuthorizationAppleIDButton *appleButon = [[ASAuthorizationAppleIDButton alloc]initWithAuthorizationButtonType:ASAuthorizationAppleIDButtonTypeSignIn authorizationButtonStyle:ASAuthorizationAppleIDButtonStyleBlack];
    appleButon.cornerRadius = 30.f;
    [appleButon addTarget:self action:@selector(appleSignAction) forControlEvents:UIControlEventTouchUpInside];
    [view addSubview:appleButon];
    [appleButon mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.mas_equalTo(0);
        make.height.mas_equalTo(view.mas_width);
    }];
                    
}

代理方法

- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization NS_SWIFT_NAME(authorizationController(controller:didCompleteWithAuthorization:)) API_AVAILABLE(ios(13.0)){
    
    if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]])       {
        ASAuthorizationAppleIDCredential *credential = authorization.credential;
        
        NSLog(@"credential = %@",credential);
        
        NSString *state = credential.state;
        NSString *userID = credential.user;
        NSPersonNameComponents *fullName = credential.fullName;
        NSString *email = credential.email;
        NSString *authorizationCode = [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding]; // 验证 token
        NSString *identityToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding]; // 用户 token
        ASUserDetectionStatus realUserStatus = credential.realUserStatus;
        NSArray *authorizedScopes = credential.authorizedScopes;
        
        NSLog(@"state: %@\nuserID: %@\nfullName: %@\nemail: %@\nauthorizationCode: %@\nidentityToken: %@\nrealUserStatus: %@\nauthorizedScopes: %@",
              state,
              userID,
              fullName,
              email,
              authorizationCode,
              identityToken,
              @(realUserStatus),
              authorizedScopes);
    
}

- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error  NS_SWIFT_NAME(authorizationController(controller:didCompleteWithError:)) API_AVAILABLE(ios(13.0)){
    
    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;
    }
    
}

- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)){
    return self.window;
}

-(void)appleSignAction{
    
    if (@available(iOS 13.0, *)) {
        
        ASAuthorizationAppleIDProvider *provider = [[ASAuthorizationAppleIDProvider alloc] init];
        ASAuthorizationAppleIDRequest *request = [provider createRequest];
        request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
        
        ASAuthorizationController *vc = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
        vc.delegate = self;
        vc.presentationContextProvider = self;
        [vc performRequests];
        
    }
    
}

后端验证

  • 1、前端获取到Apple ID的用户信息后,需要把code传给后端,后端再调用苹果的验证接口进行验证,通过验证接口返回的数据进行校验(此处省略一万字)...code是什么呢?code就是前端获取到的authorizationCode

  • 2、苹果验证接口:https://appleid.apple.com/auth/token

必传参数

client_id:app的 bundle identifier
code:手机端获取到的 authorizationCode
grant_type:传入固定字符串 authorization_code
client_secret:秘钥

  • 3、秘钥client_secret的生成

使用ruby代码生成client_secret,对于ruby应该不会陌生,就算ruby代码不会写(我也不会写,代码是copy的),至少也知道这个东西,毕竟使用pod管理代码的前提是必须安装ruby环境

ruby代码如下:

require "jwt"

key_file = "Path to the private key"
team_id = "Your Team ID"
client_id = "Your App Bundle ID"
key_id = "The Key ID of the private key"
validity_period = 180 # In days. Max 180 (6 months) according to Apple docs.

private_key = OpenSSL::PKey::EC.new IO.read key_file

token = JWT.encode(
  {
    iss: team_id,
    iat: Time.now.to_i,
    exp: Time.now.to_i + 86400 * validity_period,
    aud: "https://appleid.apple.com",
    sub: client_id
  },
  private_key,
  "ES256",
  header_fields=
  {
    kid: key_id 
  }
)
puts token

参数说明

key_file:苹果开发者中心下载的.p8文件(实现-1步骤中提到的)保存的路径
team_id:开发者账号的teamID(实现-2步骤中提到的)
client_id:项目的bundleID
key_id:苹果开发者中心创建的keyID(实现-2步骤中提到的)

在上面的ruby中,有看到引入jwt,那么还需要你的Mac安装jwt环境。
打开终端,输入命令:sudo gem install jwt安装jwt环境,安装完成后,桌面创建一个文件夹,并在终端cd到该文件目录下,然后输入命令:touch secret_gen.rb创建.rb文件,创建完成双击打开该文件,把上面的ruby代码复制黏贴进去,并修改key_fileteam_idclient_idkey_id这四个keyvalue,保存文件,秘钥client_secret就生成了。

其实client_secret大可不必由前端来生成,完全可以由后端同事自己去搞,奈何实在是太想装逼的我想要嘚瑟一把(此处省略一万字)...

好了,到此为止,后端完成了苹果的验证之后,后续业务该怎么玩怎么玩了,其实关于Sign In with Apple登录的文章一搜一大把,说来说去主要重点就是这么点东西,可能部分细节我这里没有实现,但是重点就是这些。

全剧终

2020-8-26 补充 Apple登录成功案例之一

(深沉厚重的背景独白,自己配音)事情发生在2020年7月30号的早上7点09分,还记得那是第一次因为Apple登录的问题被苹果拒绝审核通过……刹那间,仿佛过了许多个日日夜夜……咳咳,不扯淡了,哈哈哈哈

  • 时间是7月30号第一次被拒,原因是未添加Apple第三方登录,由于是初次实现该功能,所以用的时间比较长吧,到了8月4号,已经完全实现了Apple登录功能,只不过需要和后台对接的相关处理还没搞定,也是因为后台的功能还没有实现,呵!垃圾……
时间.png

不小心暴露了上班时间,好吧,我们公司是6.30下班的

  • 页面显示
登录页面.png

看到了吧,登录页面就是上面这个样子的了

补充信息页面.png

登录成功后需要用户补充邮箱和手机号码

image.png

最后的提审时间是8月25号上午11点46分

image.png

8月25号晚上23点45分通过审核

你可能感兴趣的:(iOS 实现苹果第三方登录)