mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-01-22 07:14:16 +03:00
[FEATURE] AuthCoreProvider: OAuth Support Part 1
This commit is contained in:
parent
f317912de7
commit
b919020988
4 changed files with 100 additions and 31 deletions
|
@ -11,6 +11,7 @@
|
||||||
import pro.gravit.launchserver.Reconfigurable;
|
import pro.gravit.launchserver.Reconfigurable;
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportGetAllUsers;
|
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportGetAllUsers;
|
||||||
|
import pro.gravit.launchserver.manangers.AuthManager;
|
||||||
import pro.gravit.launchserver.socket.Client;
|
import pro.gravit.launchserver.socket.Client;
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
import pro.gravit.utils.ProviderMap;
|
import pro.gravit.utils.ProviderMap;
|
||||||
|
@ -41,11 +42,13 @@ public static void registerProviders() {
|
||||||
}
|
}
|
||||||
public abstract User getUserByUsername(String username);
|
public abstract User getUserByUsername(String username);
|
||||||
public abstract User getUserByUUID(UUID uuid);
|
public abstract User getUserByUUID(UUID uuid);
|
||||||
|
public abstract User getUserByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired;
|
||||||
|
public abstract AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context /* may be null */);
|
||||||
public abstract void verifyAuth(AuthResponse.AuthContext context) throws AuthException;
|
public abstract void verifyAuth(AuthResponse.AuthContext context) throws AuthException;
|
||||||
public abstract PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password);
|
public abstract PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password);
|
||||||
|
public abstract AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context /* may be null */, PasswordVerifyReport report /* may be null */, boolean minecraftAccess) throws IOException;
|
||||||
public abstract void init(LaunchServer server);
|
public abstract void init(LaunchServer server);
|
||||||
// Auth Handler methods
|
// Auth Handler methods
|
||||||
protected abstract boolean updateAuth(User user, String accessToken) throws IOException;
|
|
||||||
protected abstract boolean updateServerID(User user, String serverID) throws IOException;
|
protected abstract boolean updateServerID(User user, String serverID) throws IOException;
|
||||||
|
|
||||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
||||||
|
@ -125,7 +128,6 @@ public static class PasswordVerifyReport {
|
||||||
public final boolean success;
|
public final boolean success;
|
||||||
public final boolean needMoreFactor;
|
public final boolean needMoreFactor;
|
||||||
public final List<Integer> factors;
|
public final List<Integer> factors;
|
||||||
public final String accessToken;
|
|
||||||
public static final PasswordVerifyReport REQUIRED_2FA = new PasswordVerifyReport(-1);
|
public static final PasswordVerifyReport REQUIRED_2FA = new PasswordVerifyReport(-1);
|
||||||
public static final PasswordVerifyReport FAILED = new PasswordVerifyReport(false);
|
public static final PasswordVerifyReport FAILED = new PasswordVerifyReport(false);
|
||||||
|
|
||||||
|
@ -133,28 +135,37 @@ public PasswordVerifyReport(boolean success) {
|
||||||
this.success = success;
|
this.success = success;
|
||||||
this.needMoreFactor = false;
|
this.needMoreFactor = false;
|
||||||
this.factors = List.of();
|
this.factors = List.of();
|
||||||
this.accessToken = SecurityHelper.randomStringToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PasswordVerifyReport(String accessToken) {
|
|
||||||
this.success = true;
|
|
||||||
this.needMoreFactor = false;
|
|
||||||
this.factors = List.of();
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PasswordVerifyReport(int nextFactor) {
|
public PasswordVerifyReport(int nextFactor) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
this.needMoreFactor = true;
|
this.needMoreFactor = true;
|
||||||
this.factors = List.of(nextFactor);
|
this.factors = List.of(nextFactor);
|
||||||
this.accessToken = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PasswordVerifyReport(List<Integer> factors) {
|
public PasswordVerifyReport(List<Integer> factors) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
this.needMoreFactor = false;
|
this.needMoreFactor = false;
|
||||||
this.factors = Collections.unmodifiableList(factors);
|
this.factors = Collections.unmodifiableList(factors);
|
||||||
this.accessToken = null;
|
}
|
||||||
|
|
||||||
|
private PasswordVerifyReport(boolean success, boolean needMoreFactor, List<Integer> factors) {
|
||||||
|
this.success = success;
|
||||||
|
this.needMoreFactor = needMoreFactor;
|
||||||
|
this.factors = factors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OAuthAccessTokenExpired extends Exception {
|
||||||
|
public OAuthAccessTokenExpired() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public OAuthAccessTokenExpired(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OAuthAccessTokenExpired(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||||
|
import pro.gravit.launchserver.manangers.AuthManager;
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
@ -56,6 +58,16 @@ public User getUserByUUID(UUID uuid) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByOAuthAccessToken(String accessToken) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void verifyAuth(AuthResponse.AuthContext context) throws AuthException {
|
public void verifyAuth(AuthResponse.AuthContext context) throws AuthException {
|
||||||
|
|
||||||
|
@ -70,6 +82,17 @@ public PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordIn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context, PasswordVerifyReport report, boolean minecraftAccess) throws IOException {
|
||||||
|
if(minecraftAccess) {
|
||||||
|
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
||||||
|
updateAuth(user, minecraftAccessToken);
|
||||||
|
return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken);
|
||||||
|
} else {
|
||||||
|
return AuthManager.AuthReport.ofMinecraftAccessToken(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(LaunchServer server) {
|
public void init(LaunchServer server) {
|
||||||
if (mySQLHolder == null) logger.error("mySQLHolder cannot be null");
|
if (mySQLHolder == null) logger.error("mySQLHolder cannot be null");
|
||||||
|
@ -90,7 +113,6 @@ public void init(LaunchServer server) {
|
||||||
table, serverIDColumn, uuidColumn);
|
table, serverIDColumn, uuidColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
||||||
try (Connection c = mySQLHolder.getConnection()) {
|
try (Connection c = mySQLHolder.getConnection()) {
|
||||||
PreparedStatement s = c.prepareStatement(updateAuthSQL);
|
PreparedStatement s = c.prepareStatement(updateAuthSQL);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
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.manangers.AuthManager;
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -19,6 +20,16 @@ public User getUserByUUID(UUID uuid) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByOAuthAccessToken(String accessToken) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void verifyAuth(AuthResponse.AuthContext context) throws AuthException {
|
public void verifyAuth(AuthResponse.AuthContext context) throws AuthException {
|
||||||
throw new AuthException("Please configure AuthCoreProvider");
|
throw new AuthException("Please configure AuthCoreProvider");
|
||||||
|
@ -30,13 +41,13 @@ public PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordIn
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(LaunchServer server) {
|
public AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context, PasswordVerifyReport report, boolean minecraftAccess) throws IOException {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
public void init(LaunchServer server) {
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
import pro.gravit.launchserver.auth.provider.AuthProviderResult;
|
import pro.gravit.launchserver.auth.provider.AuthProviderResult;
|
||||||
import pro.gravit.launchserver.socket.Client;
|
import pro.gravit.launchserver.socket.Client;
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
|
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
|
@ -63,26 +62,52 @@ public void check(AuthResponse.AuthContext context) throws AuthException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AuthReport {
|
||||||
|
public final String minecraftAccessToken;
|
||||||
|
public final String oauthAccessToken;
|
||||||
|
public final String oauthRefreshToken;
|
||||||
|
public final long oauthExpire;
|
||||||
|
|
||||||
|
private AuthReport(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire) {
|
||||||
|
this.minecraftAccessToken = minecraftAccessToken;
|
||||||
|
this.oauthAccessToken = oauthAccessToken;
|
||||||
|
this.oauthRefreshToken = oauthRefreshToken;
|
||||||
|
this.oauthExpire = oauthExpire;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthReport ofOAuth(String oauthAccessToken, String oauthRefreshToken, long oauthExpire) {
|
||||||
|
return new AuthReport(null, oauthAccessToken, oauthRefreshToken, oauthExpire);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthReport ofOAuthWithMinecraft(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire) {
|
||||||
|
return new AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthReport ofMinecraftAccessToken(String minecraftAccessToken) {
|
||||||
|
return new AuthReport(minecraftAccessToken, null, null, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full client authorization with password verification
|
* Full client authorization with password verification
|
||||||
* @param context AuthContext
|
* @param context AuthContext
|
||||||
* @param password User password
|
* @param password User password
|
||||||
* @return Access token
|
* @return Access token
|
||||||
*/
|
*/
|
||||||
public String auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException {
|
public AuthReport auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||||
AuthProviderPair pair = context.pair;
|
AuthProviderPair pair = context.pair;
|
||||||
String accessToken;
|
AuthReport report;
|
||||||
if(pair.core == null) {
|
if(pair.core == null) {
|
||||||
try {
|
try {
|
||||||
accessToken = authWithProviderAndHandler(context, password);
|
report = AuthReport.ofMinecraftAccessToken(authWithProviderAndHandler(context, password));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if(e instanceof AuthException) throw (AuthException) e;
|
if(e instanceof AuthException) throw (AuthException) e;
|
||||||
throw new AuthException("Internal Auth Error. Please contact administrator");
|
throw new AuthException("Internal Auth Error. Please contact administrator");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
accessToken = authWithCore(context, password);
|
report = authWithCore(context, password);
|
||||||
}
|
}
|
||||||
return accessToken;
|
return report;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@ -106,7 +131,7 @@ private String authWithProviderAndHandler(AuthResponse.AuthContext context, Auth
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String authWithCore(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException {
|
private AuthReport authWithCore(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||||
AuthCoreProvider provider = context.pair.core;
|
AuthCoreProvider provider = context.pair.core;
|
||||||
provider.verifyAuth(context);
|
provider.verifyAuth(context);
|
||||||
User user = provider.getUserByUsername(context.login);
|
User user = provider.getUserByUsername(context.login);
|
||||||
|
@ -115,17 +140,17 @@ private String authWithCore(AuthResponse.AuthContext context, AuthRequest.AuthPa
|
||||||
}
|
}
|
||||||
AuthCoreProvider.PasswordVerifyReport report = provider.verifyPassword(user, password);
|
AuthCoreProvider.PasswordVerifyReport report = provider.verifyPassword(user, password);
|
||||||
if(report.success) {
|
if(report.success) {
|
||||||
String accessToken;
|
|
||||||
UUID uuid = user.getUUID();
|
UUID uuid = user.getUUID();
|
||||||
if(context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
|
AuthReport result;
|
||||||
provider.verifyAuth(context);
|
try {
|
||||||
accessToken = report.accessToken;
|
result = provider.createOAuthSession(user, context, report, context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context));
|
||||||
} else {
|
} catch (IOException e) {
|
||||||
accessToken = null;
|
logger.error(e);
|
||||||
|
throw new AuthException("Internal Auth Error");
|
||||||
}
|
}
|
||||||
context.client.coreObject = user;
|
context.client.coreObject = user;
|
||||||
internalAuth(context.client, context.authType, context.pair, user.getUsername(), uuid, user.getPermissions());
|
internalAuth(context.client, context.authType, context.pair, user.getUsername(), uuid, user.getPermissions());
|
||||||
return accessToken;
|
return result;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(report.needMoreFactor) {
|
if(report.needMoreFactor) {
|
||||||
|
|
Loading…
Reference in a new issue