Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.

Commit e935494

Browse files
author
Test User
committed
Add Ely.by authentication support
1 parent b42a665 commit e935494

File tree

6 files changed

+169
-3
lines changed

6 files changed

+169
-3
lines changed

Natives/AccountListViewController.m

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#import <AuthenticationServices/AuthenticationServices.h>
22

33
#import "authenticator/BaseAuthenticator.h"
4+
#import "authenticator/ElyByAuthenticator.h"
45
#import "AccountListViewController.h"
56
#import "AFNetworking.h"
67
#import "LauncherPreferences.h"
@@ -144,6 +145,10 @@ - (void)actionAddAccount:(UITableViewCell *)sender {
144145
[self actionLoginMicrosoft:sender];
145146
}];
146147
[picker addAction:actionMicrosoft];
148+
UIAlertAction *actionElyBy = [UIAlertAction actionWithTitle:localize(@"login.option.elyby", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
149+
[self actionLoginElyBy:sender];
150+
}];
151+
[picker addAction:actionElyBy];
147152
UIAlertAction *actionLocal = [UIAlertAction actionWithTitle:localize(@"login.option.local", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
148153
[self actionLoginLocal:sender];
149154
}];
@@ -192,6 +197,47 @@ - (void)actionLoginLocal:(UIView *)sender {
192197
[self presentViewController:controller animated:YES completion:nil];
193198
}
194199

200+
- (void)actionLoginElyBy:(UIView *)sender {
201+
UIAlertController *controller = [UIAlertController alertControllerWithTitle:localize(@"Sign in", nil) message:localize(@"login.option.elyby", nil) preferredStyle:UIAlertControllerStyleAlert];
202+
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
203+
textField.placeholder = localize(@"login.alert.field.username", nil);
204+
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
205+
textField.borderStyle = UITextBorderStyleRoundedRect;
206+
}];
207+
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
208+
textField.placeholder = localize(@"login.alert.field.password", nil);
209+
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
210+
textField.borderStyle = UITextBorderStyleRoundedRect;
211+
textField.secureTextEntry = YES;
212+
}];
213+
[controller addAction:[UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
214+
NSArray *textFields = controller.textFields;
215+
UITextField *usernameField = textFields[0];
216+
UITextField *passwordField = textFields[1];
217+
218+
if (usernameField.text.length == 0 || passwordField.text.length == 0) {
219+
controller.message = localize(@"login.error.fields.empty", nil);
220+
[self presentViewController:controller animated:YES completion:nil];
221+
return;
222+
}
223+
224+
self.modalInPresentation = YES;
225+
self.tableView.userInteractionEnabled = NO;
226+
[self addActivityIndicatorTo:sender];
227+
228+
ElyByAuthenticator *auth = [[ElyByAuthenticator alloc] initWithInput:usernameField.text];
229+
auth.authData[@"password"] = passwordField.text;
230+
231+
id callback = ^(id status, BOOL success) {
232+
[self callbackMicrosoftAuth:status success:success forCell:sender];
233+
};
234+
235+
[auth loginWithCallback:callback];
236+
}]];
237+
[controller addAction:[UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
238+
[self presentViewController:controller animated:YES completion:nil];
239+
}
240+
195241
- (void)actionLoginMicrosoft:(UITableViewCell *)sender {
196242
NSURL *url = [NSURL URLWithString:@"https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_url=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"];
197243

Natives/authenticator/BaseAuthenticator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ typedef void(^Callback)(id status, BOOL success);
2626
+ (void)clearTokenDataOfProfile:(NSString *)profile;
2727

2828
@end
29+
30+
@interface ElyByAuthenticator : BaseAuthenticator
31+
@end

Natives/authenticator/BaseAuthenticator.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#import <Security/Security.h>
22
#import "BaseAuthenticator.h"
3+
#import "ElyByAuthenticator.h"
34
#import "../LauncherPreferences.h"
45
#import "../ios_uikit_bridge.h"
56
#import "../utils.h"
@@ -31,6 +32,9 @@ + (id)loadSavedName:(NSString *)name {
3132

3233
if ([authData[@"expiresAt"] longValue] == 0) {
3334
return [[LocalAuthenticator alloc] initWithData:authData];
35+
} else if (authData[@"clientToken"] != nil) {
36+
// Если есть clientToken, значит это аккаунт ely.by
37+
return [[ElyByAuthenticator alloc] initWithData:authData];
3438
} else {
3539
return [[MicrosoftAuthenticator alloc] initWithData:authData];
3640
}
@@ -58,6 +62,7 @@ - (BOOL)saveChanges {
5862
NSError *error;
5963

6064
[self.authData removeObjectForKey:@"input"];
65+
[self.authData removeObjectForKey:@"password"];
6166

6267
NSString *newPath = [NSString stringWithFormat:@"%s/accounts/%@.json", getenv("POJAV_HOME"), self.authData[@"username"]];
6368
if (self.authData[@"oldusername"] != nil && ![self.authData[@"username"] isEqualToString:self.authData[@"oldusername"]]) {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#import "BaseAuthenticator.h"
2+
3+
@interface ElyByAuthenticator : BaseAuthenticator
4+
5+
@end
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#import "AFNetworking.h"
2+
#import "ElyByAuthenticator.h"
3+
#import "../ios_uikit_bridge.h"
4+
#import "../utils.h"
5+
6+
@implementation ElyByAuthenticator
7+
8+
- (void)loginWithCallback:(Callback)callback {
9+
callback(localize(@"login.ely.progress.auth", nil), YES);
10+
11+
NSString *username = self.authData[@"input"];
12+
NSString *password = self.authData[@"password"];
13+
14+
if (username == nil || password == nil) {
15+
callback(@"Username or password is missing", NO);
16+
return;
17+
}
18+
19+
NSDictionary *data = @{
20+
@"username": username,
21+
@"password": password,
22+
@"clientToken": [[NSUUID UUID] UUIDString]
23+
};
24+
25+
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
26+
manager.requestSerializer = AFJSONRequestSerializer.serializer;
27+
28+
[manager POST:@"https://authserver.ely.by/auth/authenticate" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
29+
// Обработка успешного ответа
30+
self.authData[@"accessToken"] = response[@"accessToken"];
31+
self.authData[@"clientToken"] = response[@"clientToken"];
32+
self.authData[@"username"] = response[@"selectedProfile"][@"name"];
33+
self.authData[@"uuid"] = response[@"selectedProfile"][@"id"];
34+
self.authData[@"profileId"] = response[@"selectedProfile"][@"id"];
35+
36+
// Форматирование UUID с дефисами
37+
NSString *uuid = response[@"selectedProfile"][@"id"];
38+
if (uuid.length == 32) { // Если UUID без дефисов
39+
self.authData[@"profileId"] = [NSString stringWithFormat:@"%@-%@-%@-%@-%@",
40+
[uuid substringWithRange:NSMakeRange(0, 8)],
41+
[uuid substringWithRange:NSMakeRange(8, 4)],
42+
[uuid substringWithRange:NSMakeRange(12, 4)],
43+
[uuid substringWithRange:NSMakeRange(16, 4)],
44+
[uuid substringWithRange:NSMakeRange(20, 12)]
45+
];
46+
}
47+
48+
// Установка URL аватара
49+
self.authData[@"profilePicURL"] = [NSString stringWithFormat:@"https://mc-heads.net/head/%@/120", self.authData[@"profileId"]];
50+
51+
// Время истечения токена (24 часа)
52+
self.authData[@"expiresAt"] = @((long)[NSDate.date timeIntervalSince1970] + 86400);
53+
54+
// Сохраняем изменения
55+
callback(nil, [self saveChanges]);
56+
57+
} failure:^(NSURLSessionDataTask *task, NSError *error) {
58+
NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
59+
if (errorData) {
60+
NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:errorData options:kNilOptions error:nil];
61+
callback(errorDict[@"errorMessage"] ?: error.localizedDescription, NO);
62+
} else {
63+
callback(error.localizedDescription, NO);
64+
}
65+
}];
66+
}
67+
68+
- (void)refreshTokenWithCallback:(Callback)callback {
69+
callback(localize(@"login.ely.progress.refresh", nil), YES);
70+
71+
NSDictionary *data = @{
72+
@"accessToken": self.authData[@"accessToken"],
73+
@"clientToken": self.authData[@"clientToken"]
74+
};
75+
76+
AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
77+
manager.requestSerializer = AFJSONRequestSerializer.serializer;
78+
79+
[manager POST:@"https://authserver.ely.by/auth/refresh" parameters:data headers:nil progress:nil success:^(NSURLSessionDataTask *task, NSDictionary *response) {
80+
// Обновляем токены
81+
self.authData[@"accessToken"] = response[@"accessToken"];
82+
self.authData[@"clientToken"] = response[@"clientToken"];
83+
84+
// Время истечения токена (24 часа)
85+
self.authData[@"expiresAt"] = @((long)[NSDate.date timeIntervalSince1970] + 86400);
86+
87+
// Сохраняем изменения
88+
callback(nil, [self saveChanges]);
89+
90+
} failure:^(NSURLSessionDataTask *task, NSError *error) {
91+
NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
92+
if (errorData) {
93+
NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:errorData options:kNilOptions error:nil];
94+
callback(errorDict[@"errorMessage"] ?: error.localizedDescription, NO);
95+
} else {
96+
callback(error.localizedDescription, NO);
97+
}
98+
}];
99+
}
100+
101+
@end

Natives/resources/en.lproj/Localizable.strings

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868

6969
"init.migrateDir" = "The PojavLauncher data directory is now %@. All of your existing data has been moved to the new location.";
7070

71-
"java.error.missing_main_class" = "%@ is missing main class attribute. If this is a mod, put it into the mods folder of your selected instance.";
71+
"java.error.missing_main_class" = "%@ is missing main class attribute. If this is a mod, put it into the "mods" folder of your selected instance.";
7272
"java.error.missing_runtime" = "%@ requires Java %d or later to run. Please install it first and specify it in Manage Runtimes.";
7373

7474
"launcher.mcl.downloading_file" = "Downloading %@";
@@ -84,6 +84,8 @@
8484
"login.error.username.outOfRange" = "Username must be at least 3 characters and a maximum of 16 characters";
8585

8686
"login.alert.field.username" = "Username";
87+
"login.alert.field.password" = "Password";
88+
"login.error.fields.empty" = "Username or password is empty";
8789

8890
"login.jit.checking" = "Checking for JIT";
8991
"login.jit.enabled" = "JIT has been enabled";
@@ -98,6 +100,7 @@
98100
"login.option.select" = "Select account";
99101
"login.option.demo" = "Demo account";
100102
"login.option.microsoft" = "Microsoft account";
103+
"login.option.elyby" = "Ely.by account";
101104
"login.option.local" = "Local account";
102105

103106
"login.warn.title.legacy_device" = "The next release of PojavLauncher will not be compatible with this device.";
@@ -233,7 +236,7 @@
233236
"preference.manage_runtime.header.default" = "Preferred Java version";
234237
"preference.manage_runtime.default.1165" = "1.16.5 and older";
235238
"preference.manage_runtime.default.117" = "1.17 and newer";
236-
"preference.manage_runtime.footer.default" = "Minecraft 1.16.5 and older may run with a newer Java version, however compatibility with mods is not guaranteed. External runtimes can be placed in the java_runtimes folder. Changing Java version for Execute .jar only takes effect if the installer does not expect a newer Java version.";
239+
"preference.manage_runtime.footer.default" = "Minecraft 1.16.5 and older may run with a newer Java version, however compatibility with mods is not guaranteed. External runtimes can be placed in the "java_runtimes" folder. Changing Java version for "Execute .jar" only takes effect if the installer does not expect a newer Java version.";
237240
"preference.manage_runtime.footer.java8" = "This is the default version for Minecraft 1.16.5 and older.";
238241
"preference.manage_runtime.footer.java17" = "This is the default version for Minecraft 1.17 and newer.";
239242
"preference.manage_runtime.header.invalid" = "Invalid runtimes";
@@ -253,7 +256,7 @@
253256
"preference.title.custom_env" = "Environment variables";
254257
"preference.detail.custom_env" = "This option is under construction. Create and edit custom_env.txt in the meantime.";
255258

256-
"preference.title.debug_skip_wait_jit" = "Skip \"Waiting for JIT\" dialog";
259+
"preference.title.debug_skip_wait_jit" = "Skip "Waiting for JIT" dialog";
257260
"preference.detail.debug_skip_wait_jit" = "This does not allow you to launch without JIT, instead this skips debugger check, because check fails on certain jailbreaks.";
258261
"preference.title.debug_ipad_ui" = "Unlock iPadOS UI";
259262
"preference.detail.debug_ipad_ui" = "Unlock iPad-exclusive UI (alert, keyboard, etc.) if enabled or iPhone UI if disabled.";
@@ -324,3 +327,6 @@
324327
"controller_configurator.section.footer.menu_mappings" = "Bindings when the mouse is not locked, such as on the title screen or in the pause menu.";
325328
"controller_configurator.section.controller_style" = "Controller type";
326329
"controller_configurator.section.footer.controller_style" = "Show different controller button names in this configurator.";
330+
331+
"login.ely.progress.auth" = "Authenticating with Ely.by...";
332+
"login.ely.progress.refresh" = "Refreshing Ely.by token...";

0 commit comments

Comments
 (0)