ios-unity/Classes_cocos/UIViewController+Wallet.mm

442 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// UIViewController+Wallet.m
// Unity-iPhone
//
// Created by zhl on 2022/9/1.
//
#import "UIViewController+Wallet.h"
#import "WebPageViewController.h"
#import <TikTokOpenSDK/TikTokOpenSDKAuth.h>
#include <string>
#include "WalletEvent.h"
#include "JcWallet.h"
#import <GoogleSignIn/GoogleSignIn.h>
#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 <LocalAuthentication/LocalAuthentication.h>
#import "NSDictionary+Customer.h"
@import SafariServices;
@import FBSDKLoginKit;
@import GoogleSignIn;
static NSString * const kClientID =
@"53206975661-qan0rnefniegjv53ohild375pv0p7ekd.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
NSLog(@"tiktok resp: %@", responseCode);
[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) signWithOAuth:(NSString *) funid jsondata: (NSString *) jsondata {
// convert jsondata to NSDictionary
NSError *error;
NSData *jsonData = [jsondata dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if (error) {
[self nativeCb:funid hasErr:YES dataStr: error.localizedDescription];
return;
}
NSString *url = [NSString stringWithFormat:@"%@?client_id=%@&response_type=%@&redirect_uri=%@&scope=%@&state=%@&nonce=%@", json[@"endpoint"], json[@"client_id"], json[@"response_type"], json[@"redirect_uri"], json[@"scopes"], funid, funid];
// check if json has "response_mode"
if (json[@"response_mode"] != nil) {
url = [NSString stringWithFormat:@"%@&response_mode=%@", url, json[@"response_mode"]];
}
NSString* webStringURL = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL* authURL = [NSURL URLWithString:webStringURL];
// Create a Safari View Controller with the auth URL
SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:authURL];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveDeepLinkNotification:)
name:@"kUnityOnOpenURL"
object:nil];
// Present the Safari View Controller
[self presentViewController:safariViewController animated:NO completion:nil];
}
- (void) receiveDeepLinkNotification: (NSNotification *) notification {
NSDictionary *userInfo = notification.userInfo;
// check if userInfo is nil
if (userInfo == nil) {
return;
}
NSURL *url = userInfo[@"url"];
NSString *sourceApplication = userInfo[@"sourceApplication"];
// check if url is nil or sourceApplication is nil
if (url == nil || [NSString isStringEmpty:sourceApplication]) {
return;
}
// check if scheme is equal to cfoauthcb
if (![url.scheme isEqualToString:@"cfoauthcb"]) {
return;
}
// check if sourceApplication is equal to com.apple.SafariViewService
if ([sourceApplication isEqualToString:@"com.apple.SafariViewService"]) {
NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:YES];
// check if url has token and state
if (components.queryItems == nil) {
return;
}
NSString *token = nil;
NSString *state = nil;
for (NSURLQueryItem *item in components.queryItems) {
if ([item.name isEqualToString:@"token"]) {
token = item.value;
} else if ([item.name isEqualToString:@"state"]) {
state = item.value;
}
}
if ([NSString isStringEmpty:token] || [token isEqualToString: @"undefined"]) {
if ([NSString isStringEmpty:[url fragment]]) {
return;
} else {
components.query = [url fragment];
for (NSURLQueryItem *item in components.queryItems) {
if ([item.name isEqualToString:@"token"]) {
token = item.value;
} else if ([item.name isEqualToString:@"id_token"]) {
token = item.value;
} else if ([item.name isEqualToString:@"state"]) {
state = item.value;
}
}
}
}
// send token to server
[self nativeCb:state hasErr:NO dataStr:token];
}
}
-(void)nativeCb:(NSString *)funid hasErr: (BOOL) hasErr dataStr:(NSString *) dataStr {
// check if had presentViewController, if had, then dismiss it
if (self.presentedViewController != nil) {
[self dismissViewControllerAnimated:YES completion:nil];
}
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;
}
NSString *jsonString = [NSDictionary toJSONString:json minify:YES];
// check if funid begin with "webpage_"
if ([funid hasPrefix:@"webpage_"]) {
funid = [funid substringFromIndex:8];
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
userInfo[@"funid"] = funid;
userInfo[@"data"] = jsonString;
[[NSNotificationCenter defaultCenter]
postNotificationName:@"CallPageNotification"
object: nil userInfo:userInfo];
return;
}
std::string sfunid = std::string([funid UTF8String], [funid lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
std::string sparam = std::string([jsonString UTF8String], [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
std::vector<std::string> sparams = {sparam};
cocos2d::nativeCallJS(sfunid, methodName, sparams);
// 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];
}
}
}
- (void)storageGameInfo:(NSString *)funid data:(NSString *)data {
}
@end