mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-04-11 18:57:30 +03:00
MFASupport
By PC
This commit is contained in:
parent
90f360c565
commit
9664079799
6 changed files with 62 additions and 5 deletions
|
@ -71,6 +71,7 @@ task cleanjar(type: Jar, dependsOn: jar) {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
pack project(':LauncherAPI')
|
pack project(':LauncherAPI')
|
||||||
|
compile 'dev.samstevens.totp:totp:1.7'
|
||||||
bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
|
bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
|
||||||
bundle group: 'org.jline', name: 'jline', version: rootProject['verJline']
|
bundle group: 'org.jline', name: 'jline', version: rootProject['verJline']
|
||||||
bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline']
|
bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline']
|
||||||
|
|
|
@ -36,7 +36,7 @@ public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthTyp
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
|
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
|
||||||
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE;
|
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.TOTP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
package pro.gravit.launchserver.auth.provider;
|
package pro.gravit.launchserver.auth.provider;
|
||||||
|
|
||||||
|
import dev.samstevens.totp.code.CodeGenerator;
|
||||||
|
import dev.samstevens.totp.code.CodeVerifier;
|
||||||
|
import dev.samstevens.totp.code.DefaultCodeGenerator;
|
||||||
|
import dev.samstevens.totp.code.DefaultCodeVerifier;
|
||||||
|
import dev.samstevens.totp.time.SystemTimeProvider;
|
||||||
|
import dev.samstevens.totp.time.TimeProvider;
|
||||||
import pro.gravit.launcher.ClientPermissions;
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
|
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
|
import pro.gravit.launcher.request.auth.password.Auth2FAPassword;
|
||||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||||
|
import pro.gravit.launcher.request.auth.password.AuthTOTPPassword;
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||||
|
@ -32,18 +41,44 @@ public void init(LaunchServer srv) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws SQLException, AuthException {
|
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws SQLException, AuthException {
|
||||||
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
|
|
||||||
|
if (!(password instanceof Auth2FAPassword || password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
|
||||||
|
TimeProvider timeProvider = new SystemTimeProvider();
|
||||||
|
CodeGenerator codeGenerator = new DefaultCodeGenerator();
|
||||||
|
CodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
|
||||||
|
AuthPlainPassword first;
|
||||||
|
AuthTOTPPassword second;
|
||||||
|
if(password instanceof Auth2FAPassword) {
|
||||||
|
first = (AuthPlainPassword) ((Auth2FAPassword) password).firstPassword;
|
||||||
|
second = (AuthTOTPPassword) ((Auth2FAPassword) password).secondPassword;
|
||||||
|
} else {
|
||||||
|
first = (AuthPlainPassword) password;
|
||||||
|
second = null;
|
||||||
|
}
|
||||||
try (Connection c = mySQLHolder.getConnection()) {
|
try (Connection c = mySQLHolder.getConnection()) {
|
||||||
PreparedStatement s = c.prepareStatement(query);
|
PreparedStatement s = c.prepareStatement(query);
|
||||||
String[] replaceParams = {"login", login, "password", ((AuthPlainPassword) password).password, "ip", ip};
|
String[] replaceParams = {"login", login, "password", first.password, "ip", ip};
|
||||||
for (int i = 0; i < queryParams.length; i++)
|
for (int i = 0; i < queryParams.length; i++)
|
||||||
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
|
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
|
||||||
|
|
||||||
// Execute SQL query
|
// Execute SQL query
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||||
try (ResultSet set = s.executeQuery()) {
|
try (ResultSet set = s.executeQuery()) {
|
||||||
return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions(
|
if (set.next()){
|
||||||
set.getLong(2), flagsEnabled ? set.getLong(3) : 0)) : authError(message);
|
if (set.getBoolean("has_mfa")){
|
||||||
|
if (second == null){
|
||||||
|
return authError(AuthRequestEvent.TWO_FACTOR_NEED_ERROR_MESSAGE);
|
||||||
|
}else{
|
||||||
|
boolean successful = verifier.isValidCode(set.getString("secret"), second.totp);
|
||||||
|
return successful ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions(
|
||||||
|
set.getLong(2), flagsEnabled ? set.getLong(3) : 0)) : authError(AuthRequestEvent.TWO_FACTOR_BAD_MESSAGE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions(
|
||||||
|
set.getLong(2), flagsEnabled ? set.getLong(3) : 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return authError(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package pro.gravit.launchserver.socket.response.auth;
|
package pro.gravit.launchserver.socket.response.auth;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import net.sf.launch4j.Log;
|
||||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
|
import pro.gravit.launcher.request.auth.password.Auth2FAPassword;
|
||||||
import pro.gravit.launcher.request.auth.password.AuthECPassword;
|
import pro.gravit.launcher.request.auth.password.AuthECPassword;
|
||||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
|
@ -57,6 +59,15 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
throw new AuthException("Password decryption error");
|
throw new AuthException("Password decryption error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (password instanceof Auth2FAPassword){
|
||||||
|
try {
|
||||||
|
((Auth2FAPassword) password).firstPassword = new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey
|
||||||
|
, ((AuthECPassword)(((Auth2FAPassword) password).firstPassword)).password)));
|
||||||
|
} catch (IllegalBlockSizeException | BadPaddingException ignored) {
|
||||||
|
throw new AuthException("Password decryption error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (clientData.isAuth) {
|
if (clientData.isAuth) {
|
||||||
if (LogHelper.isDevEnabled()) {
|
if (LogHelper.isDevEnabled()) {
|
||||||
LogHelper.warning("Client %s double auth", clientData.username == null ? ip : clientData.username);
|
LogHelper.warning("Client %s double auth", clientData.username == null ? ip : clientData.username);
|
||||||
|
@ -64,6 +75,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
sendError("You are already logged in");
|
sendError("You are already logged in");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthProviderPair pair;
|
AuthProviderPair pair;
|
||||||
if (auth_id == null || auth_id.isEmpty()) pair = server.config.getAuthProviderPair();
|
if (auth_id == null || auth_id.isEmpty()) pair = server.config.getAuthProviderPair();
|
||||||
else pair = server.config.getAuthProviderPair(auth_id);
|
else pair = server.config.getAuthProviderPair(auth_id);
|
||||||
|
@ -71,6 +83,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
sendError("auth_id incorrect");
|
sendError("auth_id incorrect");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthContext context = new AuthContext(clientData, login, client, ip, authType);
|
AuthContext context = new AuthContext(clientData, login, client, ip, authType);
|
||||||
AuthProvider provider = pair.provider;
|
AuthProvider provider = pair.provider;
|
||||||
server.authHookManager.preHook.hook(context, clientData);
|
server.authHookManager.preHook.hook(context, clientData);
|
||||||
|
@ -80,6 +93,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
AuthProvider.authError(String.format("Illegal result: '%s'", aresult.username));
|
AuthProvider.authError(String.format("Illegal result: '%s'", aresult.username));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (clientData.profile == null) {
|
//if (clientData.profile == null) {
|
||||||
// throw new AuthException("You profile not found");
|
// throw new AuthException("You profile not found");
|
||||||
//}
|
//}
|
||||||
|
@ -88,6 +102,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
clientData.permissions = aresult.permissions;
|
clientData.permissions = aresult.permissions;
|
||||||
clientData.auth_id = auth_id;
|
clientData.auth_id = auth_id;
|
||||||
clientData.updateAuth(server);
|
clientData.updateAuth(server);
|
||||||
|
|
||||||
if (aresult.username != null)
|
if (aresult.username != null)
|
||||||
clientData.username = aresult.username;
|
clientData.username = aresult.username;
|
||||||
else
|
else
|
||||||
|
@ -104,6 +119,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
}
|
}
|
||||||
result.session = clientData.session;
|
result.session = clientData.session;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID uuid;
|
UUID uuid;
|
||||||
if (authType == ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
|
if (authType == ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
|
||||||
uuid = pair.handler.auth(aresult);
|
uuid = pair.handler.auth(aresult);
|
||||||
|
@ -114,12 +130,14 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
uuid = pair.handler.usernameToUUID(aresult.username);
|
uuid = pair.handler.usernameToUUID(aresult.username);
|
||||||
result.accessToken = null;
|
result.accessToken = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, aresult.username, client, clientData.auth.textureProvider);
|
result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, aresult.username, client, clientData.auth.textureProvider);
|
||||||
|
|
||||||
clientData.type = authType;
|
clientData.type = authType;
|
||||||
sendResult(result);
|
sendResult(result);
|
||||||
} catch (AuthException | HookException e) {
|
} catch (AuthException | HookException e) {
|
||||||
sendError(e.getMessage());
|
sendError(e.getMessage());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
public class AuthRequestEvent extends RequestEvent {
|
public class AuthRequestEvent extends RequestEvent {
|
||||||
public static final String TWO_FACTOR_NEED_ERROR_MESSAGE = "auth.require2fa";
|
public static final String TWO_FACTOR_NEED_ERROR_MESSAGE = "auth.require2fa";
|
||||||
|
public static final String TWO_FACTOR_BAD_MESSAGE = "auth.bad2fa";
|
||||||
|
public static final String TWO_FACTOR_NULL = "auth.null2fa";
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public ClientPermissions permissions;
|
public ClientPermissions permissions;
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
|
|
|
@ -2,3 +2,4 @@ org.gradle.parallel=true
|
||||||
org.gradle.daemon=false
|
org.gradle.daemon=false
|
||||||
org.gradle.configureondemand=true
|
org.gradle.configureondemand=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
|
org.gradle.java.home=C:/Users/Clercqer/.jdks/liberica-11.0.8
|
Loading…
Reference in a new issue