443 lines
17 KiB
Plaintext
443 lines
17 KiB
Plaintext
//
|
||
// 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 {
|
||
// UnitySendMessage("WalletPanel1", "onNativeCallback", "2222222222");
|
||
[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
|