| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- #import "AppleAuthWrapper.h"
- #import <UIKit/UIKit.h>
- #import <AuthenticationServices/AuthenticationServices.h>
- #import "platform/apple/JsbBridgeWrapper.h"
- #include "application/ApplicationManager.h"
- #include "application/BaseApplication.h"
- #include "base/Scheduler.h"
- #include "engine/BaseEngine.h"
- @interface AppleAuthWrapper () <ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding>
- @property(nonatomic, strong) ASAuthorizationController *controller API_AVAILABLE(ios(13.0));
- @property(nonatomic, strong) UIWindow *anchor;
- @property(nonatomic, copy) NSString *requestID;
- @property(nonatomic, copy) NSString *nonce;
- @property(nonatomic, copy) NSString *state;
- @end
- @implementation AppleAuthWrapper
- + (instancetype)shared {
- static AppleAuthWrapper *instance;
- static dispatch_once_t once;
- dispatch_once(&once, ^{ instance = [AppleAuthWrapper new]; });
- return instance;
- }
- - (void)start {
- // Capture-free global block required by Creator 3.8.8's MRC bridge.
- static OnScriptEventListener const listener = ^(NSString *payload) {
- dispatch_async(dispatch_get_main_queue(), ^{ [[AppleAuthWrapper shared] login:payload]; });
- };
- [[JsbBridgeWrapper sharedInstance] addScriptEventListener:@"appleauth.request" listener:listener];
- }
- - (void)reply:(NSString *)requestID data:(NSDictionary *)data {
- NSData *json = [NSJSONSerialization dataWithJSONObject:@{@"requestId": requestID, @"data": data} options:0 error:nil];
- NSString *payload = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];
- auto app = CC_CURRENT_APPLICATION();
- if (!app || !app->getEngine() || !payload) return;
- app->getEngine()->getScheduler()->performFunctionInCocosThread([payload]() {
- [[JsbBridgeWrapper sharedInstance] dispatchEventToScript:@"appleauth.response" arg:payload];
- });
- }
- - (void)fail:(NSString *)requestID reason:(NSString *)reason {
- [self reply:requestID data:@{@"status": @"error", @"isCancel": @NO, @"reason": reason}];
- }
- - (void)clear API_AVAILABLE(ios(13.0)) {
- self.controller = nil; self.anchor = nil; self.requestID = nil; self.nonce = nil; self.state = nil;
- }
- - (void)login:(NSString *)payload {
- id r = [NSJSONSerialization JSONObjectWithData:[payload dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
- if (![r isKindOfClass:NSDictionary.class] || ![r[@"requestId"] isKindOfClass:NSString.class]) return;
- NSString *requestID = r[@"requestId"];
- if (@available(iOS 13.0, *)) {
- if (self.controller) { [self fail:requestID reason:@"Apple 登录正在进行中,请先关闭已有授权窗口。"]; return; }
- UIWindow *window = UIApplication.sharedApplication.delegate.window;
- if (!window || UIApplication.sharedApplication.applicationState != UIApplicationStateActive) {
- [self fail:requestID reason:@"请在应用前台、系统授权弹窗关闭后重试。"]; return;
- }
- @try {
- self.requestID = requestID;
- self.anchor = window;
- self.state = NSUUID.UUID.UUIDString;
- self.nonce = [r[@"nonce"] isKindOfClass:NSString.class] && [r[@"nonce"] length] ? r[@"nonce"] : NSUUID.UUID.UUIDString;
- ASAuthorizationAppleIDRequest *request = [[ASAuthorizationAppleIDProvider new] createRequest];
- request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
- request.nonce = self.nonce;
- request.state = self.state;
- self.controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
- self.controller.delegate = self;
- self.controller.presentationContextProvider = self;
- [self.controller performRequests];
- } @catch (NSException *exception) {
- [self fail:requestID reason:exception.reason ?: @"Apple 授权调用失败。"];
- [self clear];
- }
- } else { [self fail:requestID reason:@"通过 Apple 登录需要 iOS 13 或更新版本。"]; }
- }
- - (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)) {
- return self.anchor;
- }
- - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (controller != self.controller) return;
- if (![authorization.credential isKindOfClass:ASAuthorizationAppleIDCredential.class]) {
- [self fail:self.requestID reason:@"Apple 返回了不支持的凭证类型。"]; [self clear]; return;
- }
- ASAuthorizationAppleIDCredential *credential = (ASAuthorizationAppleIDCredential *)authorization.credential;
- NSString *token = credential.identityToken ? [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding] : nil;
- NSString *code = credential.authorizationCode ? [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding] : nil;
- if (!token.length || !code.length || ![credential.state isEqualToString:self.state]) {
- [self fail:self.requestID reason:@"Apple 凭证缺失或 state 校验失败。"]; [self clear]; return;
- }
- NSString *name = credential.fullName ? [NSPersonNameComponentsFormatter localizedStringFromPersonNameComponents:credential.fullName style:NSPersonNameComponentsFormatterStyleDefault options:0] : @"";
- [self reply:self.requestID data:@{@"status": @"connected", @"isCancel": @NO,
- @"identityToken": token, @"authorizationCode": code, @"userId": credential.user,
- @"name": name ?: @"", @"email": credential.email ?: @"", @"nonce": self.nonce}];
- [self clear];
- });
- }
- - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (controller != self.controller) return;
- BOOL cancelled = [error.domain isEqualToString:ASAuthorizationErrorDomain] && error.code == ASAuthorizationErrorCanceled;
- [self reply:self.requestID data:@{@"status": cancelled ? @"cancelled" : @"error", @"isCancel": @(cancelled),
- @"reason": error.localizedDescription ?: @"Apple 授权失败。"}];
- [self clear];
- });
- }
- @end
|