Files
PojavLauncher_iOS/Natives/authenticator/MicrosoftAuthenticator.m
T
khanhduytran0 2a9d3909f8 Internal, renderer and UI changes + 1.19-pre1 support
Minecraft 1.19 support:
- Since 1.19-pre1, LWJGL 3.3.1 is used. However, LWJGL 3.2.3 still works with it, at least for this Minecraft release.
- Added a hack to skip calling to ObjC codes (skipped an asset file to skip setting the icon)
- JNA version replacing: now only change to 5.8.0 if version < 5.8.0

Internal changes:
- Cleanup: moved global functions to a new place
- Handle demo user correctly: actually authenticate the game with demo's access token and use the Xbox gamertag as the username
- Start moving strings to Localizable.strings (WIP)
- Re-wrote the Microsoft authentication and account management to Objective-C
- Assets downloading: added a tiny sleep call so that the UI won’t be frozen when too many tasks are executed at the same time

Renderers changes
- gl4es: `init_constructor` is now enabled, therefore moved the preload part to before `eglInitialize` (or EGL will be de-init after gl4es's hardware test).
- gl4es-extra (aka holy gl4es 1.1.4) is now used to replace the regular gl4es. This fixes VBO issue on version < 1.13 and some extra stuff. https://github.com/PojavLauncherTeam/gl4es-114-extra/commit/d6cf5c4
- Removed gl4es 1.1.5 and `Disable gl4es shaderconv` as it's unnecessary for now
- Removed vgpu, as it requires some missing extensions when come to shaders

User interface:
- The account selection list now displays the skin head (or the Xbox gamerpic if demo) and the account type
2022-05-22 11:31:14 +07:00

220 lines
10 KiB
Objective-C

#import "AFNetworking.h"
#import "BaseAuthenticator.h"
#import "../ios_uikit_bridge.h"
#import "../utils.h"
typedef void(^XSTSCallback)(NSString *xsts, NSString *uhs);
@implementation MicrosoftAuthenticator
- (void)acquireAccessToken:(NSString *)authcode refresh:(BOOL)refresh callback:(Callback)callback {
viewController.title = NSLocalizedString(@"login.msa.progress.acquireAccessToken", nil);
NSDictionary *data = @{
@"client_id": @"00000000402b5328",
(refresh ? @"refresh_token" : @"code"): authcode,
@"grant_type": refresh ? @"refresh_token" : @"authorization_code",
@"redirect_url": @"https://login.live.com/oauth20_desktop.srf",
@"scope": @"service::user.auth.xboxlive.com::MBI_SSL"
};
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
[manager GET:@"https://login.live.com/oauth20_token.srf" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
self.authData[@"msaRefreshToken"] = response[@"refresh_token"];
[self acquireXBLToken:response[@"access_token"] callback:callback];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
callback(NO);
NSLog(@"[MSA] Error: %@", error);
showDialog(viewController, @"Error", error.localizedDescription);
}];
}
- (void)acquireXBLToken:(NSString *)accessToken callback:(Callback)callback {
viewController.title = NSLocalizedString(@"login.msa.progress.acquireXBLToken", nil);
NSDictionary *data = @{
@"Properties": @{
@"AuthMethod": @"RPS",
@"SiteName": @"user.auth.xboxlive.com",
@"RpsTicket": accessToken
},
@"RelyingParty": @"http://auth.xboxlive.com",
@"TokenType": @"JWT"
};
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
manager.requestSerializer = AFJSONRequestSerializer.serializer;
[manager POST:@"https://user.auth.xboxlive.com/user/authenticate" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
Callback innerCallback = ^(BOOL success) {
if (success == NO) {
callback(NO);
return;
}
// Obtain XSTS for authenticating to Minecraft
[self acquireXSTSFor:@"rp://api.minecraftservices.com/" token:response[@"Token"] callback:^(NSString *xsts, NSString *uhs){
if (xsts == nil) {
callback(NO);
return;
}
[self acquireMinecraftToken:uhs xstsToken:xsts callback:callback];
}];
};
// Obtain XSTS for getting the Xbox gamertag
[self acquireXSTSFor:@"http://xboxlive.com" token:response[@"Token"] callback:^(NSString *xsts, NSString *uhs){
if (xsts == nil) {
callback(NO);
return;
}
[self acquireXboxProfile:uhs xstsToken:xsts callback:innerCallback];
}];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
callback(NO);
NSLog(@"[MSA] Error: %@", error);
showDialog(viewController, @"Error", error.localizedDescription);
}];
}
- (void)acquireXSTSFor:(NSString *)replyingParty token:(NSString *)xblToken callback:(XSTSCallback)callback {
viewController.title = NSLocalizedString(@"login.msa.progress.acquireXSTS", nil);
NSDictionary *data = @{
@"Properties": @{
@"SandboxId": @"RETAIL",
@"UserTokens": @[
xblToken
]
},
@"RelyingParty": replyingParty,
@"TokenType": @"JWT",
};
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
manager.requestSerializer = AFJSONRequestSerializer.serializer;
[manager POST:@"https://xsts.auth.xboxlive.com/xsts/authorize" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
NSString *uhs = response[@"DisplayClaims"][@"xui"][0][@"uhs"];
callback(response[@"Token"], uhs);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
callback(nil, nil);
NSString *errorString;
NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil];
switch ((int)([errorDict[@"XErr"] longValue]-2148916230l)) {
case 3:
errorString = @"login.msa.error.xsts.noxboxacc";
break;
case 5:
errorString = @"login.msa.error.xsts.noxbox";
break;
case 6:
case 7:
errorString = @"login.msa.error.xsts.krverify";
break;
case 8:
errorString = @"login.msa.error.xsts.underage";
break;
default:
errorString = [NSString stringWithFormat:@"%@\n\nUnknown XErr code, response:\n%@", error.localizedDescription, errorDict];
break;
}
NSLog(@"[MSA] Error: %@", errorString);
showDialog(viewController, NSLocalizedString(@"Error", nil), NSLocalizedString(errorString, nil));
}];
}
- (void)acquireXboxProfile:(NSString *)xblUhs xstsToken:(NSString *)xblXsts callback:(Callback)callback {
viewController.title = NSLocalizedString(@"login.msa.progress.acquireXboxProfile", nil);
NSDictionary *headers = @{
@"x-xbl-contract-version": @"2",
@"Authorization": [NSString stringWithFormat:@"XBL3.0 x=%@;%@", xblUhs, xblXsts]
};
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
[manager GET:@"https://profile.xboxlive.com/users/me/profile/settings?settings=PublicGamerpic,Gamertag" parameters:nil headers:headers progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
self.authData[@"profilePicURL"] = [NSString stringWithFormat:@"%@&h=120&w=120", response[@"profileUsers"][0][@"settings"][0][@"value"]];
self.authData[@"xboxGamertag"] = response[@"profileUsers"][0][@"settings"][1][@"value"];
callback(YES);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
callback(NO);
NSLog(@"[MSA] Error: %@", error);
showDialog(viewController, @"Error", error.localizedDescription);
}];
}
- (void)acquireMinecraftToken:(NSString *)xblUhs xstsToken:(NSString *)xblXsts callback:(Callback)callback {
viewController.title = NSLocalizedString(@"login.msa.progress.acquireMCToken", nil);
NSDictionary *data = @{
@"identityToken": [NSString stringWithFormat:@"XBL3.0 x=%@;%@", xblUhs, xblXsts]
};
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
manager.requestSerializer = AFJSONRequestSerializer.serializer;
[manager POST:@"https://api.minecraftservices.com/authentication/login_with_xbox" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
self.authData[@"accessToken"] = response[@"access_token"];
[self checkMCProfile:response[@"access_token"] callback:callback];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
callback(NO);
NSLog(@"[MSA] Error: %@", error);
showDialog(viewController, @"Error", error.localizedDescription);
}];
}
- (void)checkMCProfile:(NSString *)mcAccessToken callback:(Callback)callback {
self.authData[@"expiresAt"] = @((long)[NSDate.date timeIntervalSince1970] + 86400);
viewController.title = NSLocalizedString(@"login.msa.progress.checkMCProfile", nil);
NSDictionary *headers = @{
@"Authorization": [NSString stringWithFormat:@"Bearer %@", mcAccessToken]
};
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
manager.requestSerializer = AFJSONRequestSerializer.serializer;
[manager GET:@"https://api.minecraftservices.com/minecraft/profile" parameters:nil headers:headers progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
NSString *uuid = response[@"id"];
self.authData[@"profileId"] = [NSString stringWithFormat:@"%@-%@-%@-%@-%@",
[uuid substringWithRange:NSMakeRange(0, 8)],
[uuid substringWithRange:NSMakeRange(8, 4)],
[uuid substringWithRange:NSMakeRange(12, 4)],
[uuid substringWithRange:NSMakeRange(16, 4)],
[uuid substringWithRange:NSMakeRange(20, 12)]
];
self.authData[@"profilePicURL"] = [NSString stringWithFormat:@"https://mc-heads.net/head/%@/120", self.authData[@"profileId"]];
self.authData[@"oldusername"] = self.authData[@"username"];
self.authData[@"username"] = response[@"name"];
callback([super saveChanges]);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil];
if ([errorDict[@"error"] isEqualToString:@"NOT_FOUND"]) {
// If there is no profile, use the Xbox gamertag as username with Demo mode
self.authData[@"profileId"] = @"00000000-0000-0000-0000-000000000000";
self.authData[@"username"] = [NSString stringWithFormat:@"Demo.%@", self.authData[@"xboxGamertag"]];
showDialog(viewController, NSLocalizedString(@"Notice", nil), NSLocalizedString(@"login.msa.notice.demomode", nil));
callback([super saveChanges]);
return;
}
callback(NO);
NSLog(@"[MSA] Error: %@", errorDict);
showDialog(viewController, @"Error", [[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding]);
}];
}
- (void)loginWithCallback:(Callback)callback {
[self acquireAccessToken:self.authData[@"input"] refresh:NO callback:callback];
}
- (void)refreshTokenWithCallback:(Callback)callback {
if ([NSDate.date timeIntervalSince1970] > [self.authData[@"expiresAt"] longValue]) {
[self acquireAccessToken:self.authData[@"msaRefreshToken"] refresh:YES callback:callback];
} else {
callback(YES);
}
}
@end