diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java index 41891c5e..3dac78f4 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java @@ -37,6 +37,7 @@ public static void registerProviders() { if (!registredProviders) { providers.register("reject", RejectAuthCoreProvider.class); providers.register("mysql", MySQLCoreProvider.class); + providers.register("json", JsonCoreProvider.class); registredProviders = true; } } @@ -136,6 +137,7 @@ public boolean joinServer(Client client, String username, String accessToken, St public static class PasswordVerifyReport { public static final PasswordVerifyReport REQUIRED_2FA = new PasswordVerifyReport(-1); public static final PasswordVerifyReport FAILED = new PasswordVerifyReport(false); + public static final PasswordVerifyReport OK = new PasswordVerifyReport(true); public final boolean success; public final boolean needMoreFactor; public final List factors; diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/JsonCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/JsonCoreProvider.java new file mode 100644 index 00000000..234df17d --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/JsonCoreProvider.java @@ -0,0 +1,289 @@ +package pro.gravit.launchserver.auth.core; + +import pro.gravit.launcher.ClientPermissions; +import pro.gravit.launcher.Launcher; +import pro.gravit.launcher.request.auth.AuthRequest; +import pro.gravit.launcher.request.auth.password.AuthPlainPassword; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.auth.AuthException; +import pro.gravit.launchserver.auth.password.PasswordVerifier; +import pro.gravit.launchserver.manangers.AuthManager; +import pro.gravit.launchserver.socket.response.auth.AuthResponse; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.UUID; + +public class JsonCoreProvider extends AuthCoreProvider { + public String getUserByUsernameUrl; + public String getUserByUUIDUrl; + public String getUserSessionByOAuthAccessTokenUrl; + public String refreshAccessTokenUrl; + public String verifyPasswordUrl; + public String createOAuthSessionUrl; + public String updateServerIdUrl; + public String bearerToken; + public PasswordVerifier passwordVerifier; + private transient HttpClient client; + + public static class JsonGetUserByUsername { + public String username; + + public JsonGetUserByUsername(String username) { + this.username = username; + } + } + + public static class JsonGetUserByUUID { + public UUID uuid; + + public JsonGetUserByUUID(UUID uuid) { + this.uuid = uuid; + } + } + + public static class JsonGetUserSessionByAccessToken { + public String accessToken; + + public JsonGetUserSessionByAccessToken(String accessToken) { + this.accessToken = accessToken; + } + } + + public static class JsonRefreshToken { + public String refreshToken; + public String ip; + + public JsonRefreshToken(String refreshToken, String ip) { + this.refreshToken = refreshToken; + this.ip = ip; + } + } + + public static class JsonAuthReportResponse { + public String minecraftAccessToken; + public String oauthAccessToken; + public String oauthRefreshToken; + public long oauthExpire; + public JsonUserSession session; + + public AuthManager.AuthReport toAuthReport() { + return new AuthManager.AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, session); + } + } + + public static class JsonPasswordVerify { + public String username; + public UUID uuid; + + public JsonPasswordVerify(String username, UUID uuid) { + this.username = username; + this.uuid = uuid; + } + } + + public static class JsonCreateOAuthSession { + public String username; + public UUID uuid; + public boolean minecraftAccess; + + public JsonCreateOAuthSession(String username, UUID uuid, boolean minecraftAccess) { + this.username = username; + this.uuid = uuid; + this.minecraftAccess = minecraftAccess; + } + } + + public static class JsonUpdateServerId { + public String username; + public UUID uuid; + public String serverId; + + public JsonUpdateServerId(String username, UUID uuid, String serverId) { + this.username = username; + this.uuid = uuid; + this.serverId = serverId; + } + } + + public static class JsonSuccessResponse { + public boolean success; + } + + public static class JsonGetUserSessionByOAuthTokenResponse { + public boolean expired; + public UserSession session; + + public JsonGetUserSessionByOAuthTokenResponse() { + } + } + + @Override + public User getUserByUsername(String username) { + return jsonRequest(new JsonGetUserByUsername(username), getUserByUsernameUrl, JsonUser.class); + } + + @Override + public User getUserByUUID(UUID uuid) { + return jsonRequest(new JsonGetUserByUUID(uuid), getUserByUUIDUrl, JsonUser.class); + } + + @Override + public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired { + JsonGetUserSessionByOAuthTokenResponse response = jsonRequest(new JsonGetUserSessionByAccessToken(accessToken), getUserSessionByOAuthAccessTokenUrl, JsonGetUserSessionByOAuthTokenResponse.class); + if (response == null) return null; + if (!response.expired) throw new OAuthAccessTokenExpired(); + return response.session; + } + + @Override + public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { + JsonAuthReportResponse response = jsonRequest(new JsonRefreshToken(refreshToken, context.ip), this.refreshAccessTokenUrl, JsonAuthReportResponse.class); + return response == null ? null : response.toAuthReport(); + } + + @Override + public void verifyAuth(AuthResponse.AuthContext context) throws AuthException { + + } + + @Override + public PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password) { + JsonUser jsonUser = (JsonUser) user; + if (password instanceof AuthPlainPassword && jsonUser.password != null && passwordVerifier != null) { + if (passwordVerifier.check(((AuthPlainPassword) password).password, jsonUser.password)) { + return PasswordVerifyReport.OK; + } else { + return PasswordVerifyReport.FAILED; + } + } + return jsonRequest(new JsonPasswordVerify(user.getUsername(), user.getUUID()), verifyPasswordUrl, PasswordVerifyReport.class); + } + + @Override + public AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context, PasswordVerifyReport report, boolean minecraftAccess) throws IOException { + JsonAuthReportResponse response = jsonRequest(new JsonCreateOAuthSession(user.getUsername(), user.getUUID(), minecraftAccess), createOAuthSessionUrl, JsonAuthReportResponse.class); + return response == null ? null : response.toAuthReport(); + } + + @Override + public void init(LaunchServer server) { + client = HttpClient.newBuilder().build(); + } + + @Override + protected boolean updateServerID(User user, String serverID) throws IOException { + JsonSuccessResponse successResponse = jsonRequest(new JsonUpdateServerId(user.getUsername(), user.getUUID(), serverID), updateServerIdUrl, JsonSuccessResponse.class); + if (successResponse == null) return false; + return successResponse.success; + } + + @Override + public void close() throws IOException { + + } + + public static class JsonUser implements User { + private String username; + private UUID uuid; + private String serverId; + private String accessToken; + private ClientPermissions permissions; + private String password; + + public JsonUser() { + } + + public JsonUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, String password) { + this.username = username; + this.uuid = uuid; + this.serverId = serverId; + this.accessToken = accessToken; + this.permissions = permissions; + this.password = password; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public UUID getUUID() { + return uuid; + } + + @Override + public String getServerId() { + return serverId; + } + + @Override + public String getAccessToken() { + return accessToken; + } + + @Override + public ClientPermissions getPermissions() { + return permissions; + } + } + + public static class JsonUserSession implements UserSession { + public String id; + public JsonUser user; + public long expireIn; + + @Override + public String getID() { + return id; + } + + @Override + public User getUser() { + return user; + } + + @Override + public long getExpireIn() { + return expireIn; + } + } + + + public R jsonRequest(T request, String url, Class clazz) { + HttpRequest.BodyPublisher publisher; + if (request != null) { + publisher = HttpRequest.BodyPublishers.ofString(request.toString()); + } else { + publisher = HttpRequest.BodyPublishers.noBody(); + } + try { + HttpRequest request1 = HttpRequest.newBuilder() + .method("POST", publisher) + .uri(new URI(url)) + .header("Authentication", "Bearer ".concat(bearerToken)) + .header("Content-Type", "application/json; charset=UTF-8") + .header("Accept", "application/json") + .timeout(Duration.ofMillis(10000)) + .build(); + HttpResponse response = client.send(request1, HttpResponse.BodyHandlers.ofInputStream()); + int statusCode = response.statusCode(); + if (200 > statusCode || statusCode > 300) { + return null; + } + try (Reader reader = new InputStreamReader(response.body())) { + return Launcher.gsonManager.gson.fromJson(reader, clazz); + } + } catch (Exception e) { + return null; + } + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java index 83d160b8..ca822311 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java @@ -251,24 +251,34 @@ public static class AuthReport { public final String oauthAccessToken; public final String oauthRefreshToken; public final long oauthExpire; + public final UserSession session; - private AuthReport(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire) { + public AuthReport(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire, UserSession session) { this.minecraftAccessToken = minecraftAccessToken; this.oauthAccessToken = oauthAccessToken; this.oauthRefreshToken = oauthRefreshToken; this.oauthExpire = oauthExpire; + this.session = session; } public static AuthReport ofOAuth(String oauthAccessToken, String oauthRefreshToken, long oauthExpire) { - return new AuthReport(null, oauthAccessToken, oauthRefreshToken, oauthExpire); + return new AuthReport(null, oauthAccessToken, oauthRefreshToken, oauthExpire, null); + } + + public static AuthReport ofOAuth(String oauthAccessToken, String oauthRefreshToken, long oauthExpire, UserSession session) { + return new AuthReport(null, oauthAccessToken, oauthRefreshToken, oauthExpire, session); } public static AuthReport ofOAuthWithMinecraft(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire) { - return new AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire); + return new AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, null); + } + + public static AuthReport ofOAuthWithMinecraft(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire, UserSession session) { + return new AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, session); } public static AuthReport ofMinecraftAccessToken(String minecraftAccessToken) { - return new AuthReport(minecraftAccessToken, null, null, 0); + return new AuthReport(minecraftAccessToken, null, null, 0, null); } public boolean isUsingOAuth() {