// // UIViewController+Wallet.m // Unity-iPhone // // Created by zhl on 2022/9/1. // #import "UIViewController+Wallet.h" #import "WebPageViewController.h" #import #include #include "WalletEvent.h" #include "JcWallet.h" #import #include "KeyChain/DataManager.h" #import "NSString+Customer.h" #import "NSData+Base64.h" #import "AppleSignIn.h" #include "cocos/scripting/js-bindings/manual/jsb_global.h" #import @import FBSDKLoginKit; @import GoogleSignIn; static NSString * const kClientID = @"53206975661-0d6q9pqljn84n9l63gm0to1ulap9cbk4.apps.googleusercontent.com"; static WebPageViewController *webpageVC = nil; @implementation UIViewController (Wallet) +(void)toWallet:(NSString *)url{ UIApplication *app = [UIApplication sharedApplication]; [app openURL:[NSURL URLWithString:url]]; } // save key to key chain -(void)saveKey:(NSString *) account key:(NSString *) key { // NSLog(@"saveKey::account:%@, key:%@", account, key); [[DataManager sharedInstanceWith: SynLock] saveKey: account key: key]; } // load key from key chain -(NSString *)loadKey:(NSString *) account { // NSLog(@"loadKey::account:%@", account); return [[DataManager sharedInstanceWith: SynLock] loadKey: account]; } -(void)signToGoogle:(NSString *) funid { GIDConfiguration *_configuration = [[GIDConfiguration alloc] initWithClientID:kClientID]; [GIDSignIn.sharedInstance signInWithConfiguration:_configuration presentingViewController:self completion:^(GIDGoogleUser *user, NSError *error) { if (error) { NSLog(@"Status: Authentication error: %@", error); [self nativeCb:funid hasErr:YES dataStr: error.localizedDescription]; return; } [self refreshTokenID: user funid:funid]; }]; } -(void)clientLogin:(NSString *) funid { NSString *clientKey = @"game_client_id"; NSString *clientID = [self loadKey:clientKey]; // if clientID is empty, then generate a new one with uuid if ([NSString isStringEmpty:clientID]) { NSString *pre_id = [[NSUUID UUID] UUIDString]; // cast pre_id to lower case std::string uuid = [[pre_id lowercaseString] UTF8String]; std::string clientIdStr = generate_clientid(uuid); clientID = [NSString stringWithUTF8String:clientIdStr.c_str()]; NSLog(@"new clientID: %@", clientID); [self saveKey:clientKey key:clientID]; } NSLog(@"clientID: %@", clientID); [self nativeCb:funid hasErr:NO dataStr: clientID]; } -(void) refreshTokenID:(GIDGoogleUser *)user funid:(NSString*) funid{ [user.authentication doWithFreshTokens:^(GIDAuthentication * _Nullable authentication, NSError * _Nullable error) { if (error) { [self nativeCb:funid hasErr:YES dataStr: error.localizedDescription]; return; } if (authentication == nil) { [self nativeCb:funid hasErr:YES dataStr: @"empty authenication"]; return; } NSString *idToken = authentication.idToken; // Send ID token to backend (example below). NSLog(@"idToken: %@", idToken); [self nativeCb:funid hasErr:NO dataStr:idToken]; }]; } -(void)signWithGoogle:(NSString *)funid { [GIDSignIn.sharedInstance restorePreviousSignInWithCompletion:^(GIDGoogleUser * _Nullable user, NSError * _Nullable error) { if (error) { // Show the app's signed-out state. [self signToGoogle: funid]; } else { // Show the app's signed-in state. [self refreshTokenID: user funid:funid]; } }]; } -(void)signOutGoogle:(NSString *)funid { [GIDSignIn.sharedInstance signOut]; } #pragma mark- - Sign In With Apple - (void) signWithApple:(NSString *)funid{ if (@available(iOS 13.0, *)) { [[AppleSignIn sharedInstance] signIn:funid presentingViewController:self completion:^(NSString *idToken, NSError *error){ if (error != nil) { [self nativeCb:funid hasErr:YES dataStr: error.localizedDescription]; } else { NSLog(@"signWithApple: %@", idToken); [self nativeCb:funid hasErr:NO dataStr:idToken]; } }]; } else { NSLog(@"system is lower"); [self nativeCb:funid hasErr:YES dataStr: @"system is lower"]; } } #pragma mark -- Sign In with TikTok - (void) signWithTikTok:(NSString *) funid { /* STEP 1: Create the request and set permissions */ NSArray *scopes = [NSArray arrayWithObjects: @"user.info.basic", @"video.list", nil]; // list your scopes; NSOrderedSet *scopesSet = [NSOrderedSet orderedSetWithArray:scopes]; TikTokOpenSDKAuthRequest *request = [[TikTokOpenSDKAuthRequest alloc] init]; request.permissions = scopesSet; /* STEP 2: Send the request */ // __weak typeof(self) ws = self; [request sendAuthRequestViewController:self completion:^(TikTokOpenSDKAuthResponse *_Nonnull resp) { // __strong typeof(ws) sf = ws; /* STEP 3: Parse and handle the response */ if (resp.errCode == 0) { NSString *responseCode = resp.code; // Upload response code to your server and obtain user access token [self nativeCb:funid hasErr:NO dataStr:responseCode]; NSLog(@"tiktok resp: %@", responseCode); } else { // User authorization failed. Handle errors NSLog(@"tiktok resp error: %@", resp.description); [self nativeCb:funid hasErr:YES dataStr: resp.description]; } }]; } #pragma mark -- Sign In with Facebook - (void) signWithFacebook:(NSString *) funid { FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init]; if ([FBSDKAccessToken currentAccessToken]) { NSLog(@"Token is available, no need login again : %@",[[FBSDKAccessToken currentAccessToken]tokenString]); [self nativeCb:funid hasErr:NO dataStr: [[FBSDKAccessToken currentAccessToken]tokenString]]; } else { [loginManager logInWithPermissions:@[@"public_profile", @"email"] fromViewController:self handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { if (error) { [self nativeCb:funid hasErr:YES dataStr: error.description]; } else if (result.isCancelled) { [self nativeCb:funid hasErr:YES dataStr: @"user cancel"]; } else { if ([FBSDKAccessToken currentAccessToken]) { NSString *token = [[FBSDKAccessToken currentAccessToken]tokenString]; NSLog(@"Token is available : %@", token); [self nativeCb:funid hasErr:NO dataStr: token]; } else { [self nativeCb:funid hasErr:YES dataStr: @"no token"]; } } }]; } } #pragma mark -- Sign In with Twitter - (void) signWithTwitter:(NSString *) funid { } -(void)nativeCb:(NSString *)funid hasErr: (BOOL) hasErr dataStr:(NSString *) dataStr { if ([NSString isStringEmpty:funid]) { NSLog(@"nativeCallBack with empty funid: %@", funid); return; } std::string methodName = "nativeCallBack"; NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; if (hasErr) { json[@"errcode"] = @1; json[@"errmessage"] = dataStr; } else { json[@"errcode"] = @0; json[@"data"] = dataStr; } NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&error]; NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; std::string sfunid = std::string([funid UTF8String], [funid lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); if (error) { NSLog(@"Got an error: %@", error); NSString *errorStr = [NSString stringWithFormat:@"{\"errcode\": 1, \"errmessage\": \"%@\"}", error]; std::string sparam = std::string([errorStr UTF8String], [errorStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); cocos2d::nativeCallBack(sfunid.c_str(), methodName.c_str(), sparam.c_str()); return; } std::string sparam = std::string([jsonString UTF8String], [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); cocos2d::nativeCallBack(sfunid.c_str(), methodName.c_str(), sparam.c_str()); } #pragma mark -- show webpage -(void)showPage:(NSString *)url{ if (webpageVC == nil) { webpageVC = [WebPageViewController new]; } webpageVC.url = url; self.definesPresentationContext = YES; webpageVC.modalPresentationStyle = UIModalPresentationOverCurrentContext; [self presentViewController:webpageVC animated:YES completion:nil]; } #pragma mark -- biometrics -(BOOL)checkTouchIDFaceID{ LAContext *LAContent = [[LAContext alloc] init]; NSError *authError = nil; BOOL isCanEvaluatePolicy = [LAContent canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError]; NSLog(@"isCanEvaluatePolicy: %d", isCanEvaluatePolicy); if (authError) { NSLog(@"check TouchID or FaceID fail!\n error : %@",authError.localizedDescription); return NO; } else { if (isCanEvaluatePolicy) { if (@available(iOS 11.0, *)) { switch (LAContent.biometryType) { case LABiometryTypeNone: { NSLog(@"The device does not support biometry."); return NO; } case LABiometryTypeTouchID: { NSLog(@"The device supports Touch ID."); return YES; } case LABiometryTypeFaceID: { NSLog(@"The device supports Face ID."); return YES; } default: return NO; } } else { // Fallback on earlier versions NSLog(@"The device supports Face ID."); return YES; } } else { return NO; } } } - (NSString *) generateAccountKey:(NSString *) account { NSString *key = [NSString stringWithFormat:@"%@%@", account, @"_pass"]; return key; } - (void)storagePass:(NSString *)funid account:(NSString *)account pass:(NSString *)pass { // NSLog(@"storagePass: %@, %@", account, pass); std::string passStr = std::string([pass UTF8String], [pass lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); std::string keyStr = std::string([account UTF8String], [account lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); keyStr = keyStr + "0x741482aE1480E552735E44Ff3A733448AcBbeD8d"; std::string passEncrypt = encrypt_aes(passStr, keyStr); NSString *passEncryptStr = [NSString stringWithCString:passEncrypt.c_str() encoding:NSUTF8StringEncoding]; NSString *accountKey = [self generateAccountKey:account]; [self saveKey:accountKey key:passEncryptStr]; [self nativeCb:funid hasErr:NO dataStr: @"success"]; } -(void)passStorageState: (NSString *) funid account:(NSString *) account { BOOL hasTouchID = [self checkTouchIDFaceID]; NSString *accountKey = [self generateAccountKey:account]; NSString *val = [self loadKey: accountKey]; NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; json[@"biometrics"] = hasTouchID ? @1 : @0; json[@"haspass"] = [NSString isStringEmpty:val] ? @0 : @1; NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&error]; if (error) { [self nativeCb:funid hasErr:YES dataStr: @"serialization json error"]; } else { NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; [self nativeCb:funid hasErr:NO dataStr: jsonString]; } } -(void)authGetStoragePass: (NSString *) funid account: (NSString *) account{ LAContext *context = [LAContext new]; NSError *error = nil; BOOL isUseTouchID = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error]; if (error) { [self nativeCb:funid hasErr:YES dataStr:error.localizedDescription]; } else { if (isUseTouchID) { NSLog(@"had biometrics"); [context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:@"Access keychain password" reply:^(BOOL success, NSError * _Nullable error) { if (success) { NSString *accountKey = [self generateAccountKey:account]; NSString *val = [self loadKey: accountKey]; if ([NSString isStringEmpty:val]) { [self nativeCb:funid hasErr:YES dataStr:@"no pass"]; } else { std::string keyStr = std::string([account UTF8String], [account lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); keyStr = keyStr + "0x741482aE1480E552735E44Ff3A733448AcBbeD8d"; std::string passEncrypt = std::string([val UTF8String], [val lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); std::string passDecrypt = decrypt_aes(passEncrypt, keyStr); NSString *passDecryptStr = [NSString stringWithCString:passDecrypt.c_str() encoding:NSUTF8StringEncoding]; [self nativeCb:funid hasErr:NO dataStr: passDecryptStr]; } }else{ NSLog(@"%ld||%@",error.code, error.localizedDescription); [self nativeCb:funid hasErr:YES dataStr:error.localizedDescription]; } }]; }else{ NSLog(@"no biometrics"); NSLog(@"%ld||%@",error.code, error.localizedDescription); [self nativeCb:funid hasErr:YES dataStr:error.localizedDescription]; } } } @end