Apple ログインは、サードパーティ ログインの一種、つまり Apple ID を使用したログインとみなすことができます。前提条件として、すでに Apple 開発者アカウントを持っているため、トピックに直接進みます。
アプリ ID を追加し
、バンドル ID を入力します
。「Apple でサインイン」にチェックを入れて
キーを作成します。Apple
は公開キーと秘密キーのペアを OAuth クライアント シークレットとして使用します。クライアント シークレットは実際には署名された JWT です。次のステップApple に新しい秘密キーを登録することです。作成が完了すると、キーファイル
が生成されます。Key ID
ダウンロードしたキー ファイルは実際にはファイルです.p8
。ダブルクリックして、このファイル内の必要なキーを開きます。このファイルは非常に重要なので、一度しかダウンロードできません。正しく保存してください。!!
クライアントシークレット(Client Secret)を生成します。
Apple では、静的なクライアント キーではなく、秘密キーからクライアント キーを自分で取得することを要求しています。これらは、P-256 曲線と SHA256 ハッシュを使用した楕円曲線アルゴリズムを使用した JWT 標準を使用します。つまり、ES256JWT アルゴリズムが使用されます。一部の JWT ライブラリは楕円曲線メソッドをサポートしていないため、試し始める前にライブラリが楕円曲線をサポートしていることを確認してください。RubyJWT ライブラリはこのアルゴリズムをサポートしているため、それを使用してシークレットを生成します。
まず、Ruby がインストールされていることを確認し、コマンド ラインから次のコマンドを実行して JWT gem をインストールします。
gem install jwt
上記で準備したBundle ID
、 、Team ID
に従って、 (クライアント キー)を生成できます。ファイルを作成し、名前を付け、サフィックスをieに変更するだけです。テンプレートの内容は以下の通りです。Key ID
AuthKey_xxx.p8
client_secret
txt
secret_gen
.rb
secret_gen.rb
require 'jwt'
key_file = ''
team_id = ''
client_id = ''
key_id = ''
ecdsa_key = OpenSSL::PKey::EC.new IO.read key_file
headers = {
'kid' => key_id
}
claims = {
'iss' => team_id,
'iat' => Time.now.to_i,
'exp' => Time.now.to_i + 86400*180,
'aud' => 'https://appleid.apple.com',
'sub' => client_id,
}
token = JWT.encode claims, ecdsa_key, 'ES256', headers
puts token
このコードは、ES256 アルゴリズムを使用してクレームの少ない JWT を生成します。JWT は、Apple が許可する最大有効期間である 6 か月で期限切れになります。ユーザーが認証されるたびに新しいクライアント シークレット JWT を生成する場合は、より短い有効期限を使用する必要がありますが、これによりキーを 1 回生成すれば、サンプル アプリケーションで簡単に使用できるようになります。
ここでは、準備した 4 つのデータを使用します。これでコマンド ラインから実行できるようになり、JWT が出力されます。
ruby xxx/xx/xx/secret_gen.rb
これはeyJra.....RuAQ
必要なクライアント キーであり、キーが有効であることを確認します。
プロジェクトで Apple サインインを構成する
まずヘッダー ファイルをインポートし#import <AuthenticationServices/AuthenticationServices.h>
、次に Apple 認証ページを呼び出します。
if (@available(iOS 13.0, *)) {
ASAuthorizationAppleIDProvider *appleIdProvider = [[ASAuthorizationAppleIDProvider alloc] init];
ASAuthorizationAppleIDRequest *authAppleIDRequest = [appleIdProvider createRequest];
/*
//慎用 ASAuthorizationPasswordRequest
//当启用ASAuthorizationPasswordRequest且停止使用Apple ID(真机-设置-账户-密码与安全性-使用您Apple ID的App-App列表-停止使用 Apple ID, 如果KeyChain里面没有登录信息且重新使用苹果授权登录(Sign in with Apple)会报未知错误
ASAuthorizationPasswordRequest* authPasswordRequest = [[[ASAuthorizationPasswordProvider alloc] init] createRequest];
NSMutableArray <ASAuthorizationRequest*> * array = [NSMutableArray arrayWithCapacity:2];
if(authAppleIDRequest) [array addObject:authAppleIDRequest];
if(authPasswordRequest) [array addObject:authPasswordRequest];
NSArray <ASAuthorizationRequest*> * requests = [array copy];
ASAuthorizationController* authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:requests];
*/
authAppleIDRequest.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
//由ASAuthorizationAppleIDProvider创建的授权请求 管理授权请求的控制器
ASAuthorizationController* authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[authAppleIDRequest]];
//设置授权控制器通知授权请求的成功与失败的代理
authorizationController.delegate = self;
//设置提供 展示上下文的代理,在这个上下文中 系统可以展示授权界面给用户
authorizationController.presentationContextProvider = self;
//在控制器初始化期间启动授权流
[authorizationController performRequests];
}
プロキシコールバックASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding
//授权成功
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0))
{
NSString *userID = nil;
NSString *userName = nil;
NSString *userEmail = nil;
EVAppleLoginCredentialModel *model = [[EVAppleLoginCredentialModel alloc] init];
if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
ASAuthorizationAppleIDCredential *credential = authorization.credential;
//苹果用户唯一标识符,该值在同一个开发者账号下的所有App下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来。
userID = credential.user;
//苹果用户信息 如果授权过,无法再次获取该信息
userEmail = credential.email;
NSPersonNameComponents *fullName = credential.fullName;
userName = [NSString stringWithFormat:@"%@%@", fullName.familyName, fullName.givenName];
//用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupported、unknown、likelyReal
ASUserDetectionStatus realUserStatus = credential.realUserStatus;
//服务器验证需要使用的参数
NSString *authorizationCode = [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding];
NSString *identityToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding];
NSLog(@"authorization [ASAuthorizationAppleIDCredential] successfully");
NSLog(@"authorization userID: %@", userID);
NSLog(@"authorization userName: %@", userName);
NSLog(@"authorization userEmail: %@", userEmail);
NSLog(@"authorization realUserStatus: %@", @(realUserStatus));
NSLog(@"authorization authorizationCode: %@", authorizationCode);
NSLog(@"authorization identityToken: %@", identityToken);
} else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {
//用户登录使用现有的密码凭证
ASPasswordCredential *passwordCredential = authorization.credential;
//密码凭证对象的用户标识 用户的唯一标识和密码
userID = passwordCredential.user;
NSString *password = passwordCredential.password;
NSLog(@"authorization [ASPasswordCredential] successfully");
NSLog(@"authorization userID: %@", userID);
NSLog(@"authorization password: %@", password);
model.user = passwordCredential.user;
userName = @"";
userEmail = @"";
} else {
assert(0);
}
//数据校验通过后,将这些数据发送给服务器进行验证,等待服务器的回应
//如果用户选择隐藏邮箱的,email获取不到,其他数据正常,服务端匹配标准userID
if (![ZJUtils isEmpty:userID]) {
//在授权成功的回调中拿到服务器所需要的参数传给后台
//至此我们所需要做的已经完成了,看后台的验证就行了
if (self.appleLoginBlock) {
self.appleLoginBlock(0, @"苹果授权成功", model);
}
} else {
//授权数据异常
if (self.appleLoginBlock) {
self.appleLoginBlock(-1, @"苹果授权失败", nil);
}
}
}
//授权失败
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0))
{
NSString *errorMsg = nil;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
switch (error.code) {
case ASAuthorizationErrorCanceled:
errorMsg = @"用户取消了授权请求";
break;
case ASAuthorizationErrorFailed:
errorMsg = @"授权请求失败";
break;
case ASAuthorizationErrorInvalidResponse:
errorMsg = @"授权请求响应无效";
break;
case ASAuthorizationErrorNotHandled:
errorMsg = @"未能处理授权请求";
break;
case ASAuthorizationErrorUnknown:
errorMsg = @"授权请求失败未知原因";
break;
}
NSLog(@"苹果授权状态:%@", errorMsg);
}
#pragma mark ASAuthorizationControllerPresentationContextProviding
- (ASPresentationAnchor) presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0))
{
return self.view.window;
}
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization
コールバックでは、authorizationCode
検証としてパラメーターを取得します。
postman を開いてテストしてみましょう:
https://appleid.apple.com/auth/token?client_id=xxx&client_secret=xxx&code=xxx&grant_type=authorization_code&redirect_uri=https://appleid.apple.com
この時点で、基本的には完了です。実際、Apple ログインの作業のほとんどは、生成などを含め、バックグラウンドで行われます。クライアントは、Apple によってclient_secret
承認されたデータをバックエンドに取得するだけでよく、バックエンドはそれを行います。検証。