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 6a372ed9..2e686dfa 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java @@ -11,6 +11,7 @@ import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.core.User; +import pro.gravit.launchserver.auth.core.UserSession; import pro.gravit.launchserver.auth.provider.AuthProvider; import pro.gravit.launchserver.auth.provider.AuthProviderDAOResult; import pro.gravit.launchserver.auth.provider.AuthProviderResult; @@ -138,6 +139,26 @@ private String authWithProviderAndHandler(AuthResponse.AuthContext context, Auth private AuthReport authWithCore(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException { AuthCoreProvider provider = context.pair.core; provider.verifyAuth(context); + if(password instanceof AuthOAuthPassword) { + AuthOAuthPassword password1 = (AuthOAuthPassword) password; + UserSession session; + try { + session = provider.getUserSessionByOAuthAccessToken(password1.accessToken); + } catch (AuthCoreProvider.OAuthAccessTokenExpired oAuthAccessTokenExpired) { + throw new AuthException(AuthRequestEvent.OAUTH_TOKEN_EXPIRE); + } + if(session == null) { + throw new AuthException(AuthRequestEvent.OAUTH_TOKEN_INVALID); + } + User user = session.getUser(); + context.client.coreObject = user; + context.client.sessionObject = session; + internalAuth(context.client, context.authType, context.pair, user.getUsername(), user.getUUID(), user.getPermissions(), true); + if(context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) { + return AuthReport.ofMinecraftAccessToken(user.getAccessToken()); + } + return AuthReport.ofMinecraftAccessToken(null); + } User user = provider.getUserByUsername(context.login); if(user == null) { throw new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java index 9bcd6be5..d2516ac7 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java @@ -34,6 +34,8 @@ public class Client { public transient pro.gravit.launchserver.auth.core.User coreObject; + public transient pro.gravit.launchserver.auth.core.UserSession sessionObject; + public transient Map properties; public Map serializableProperties; diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/WebSocketService.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/WebSocketService.java index a603ce98..4c616421 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/WebSocketService.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/WebSocketService.java @@ -92,6 +92,8 @@ public static void registerResponses() { providers.register("pingServer", PingServerResponse.class); providers.register("currentUser", CurrentUserResponse.class); providers.register("features", FeaturesResponse.class); + providers.register("refreshToken", RefreshTokenResponse.class); + providers.register("restore", RestoreResponse.class); } public void forEachActiveChannels(BiConsumer callback) { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ExitResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ExitResponse.java index 89ed71b2..56335827 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ExitResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ExitResponse.java @@ -6,6 +6,7 @@ import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.request.ExitRequestEvent; import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportGetSessionsFromUser; import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler; import pro.gravit.launchserver.socket.response.SimpleResponse; @@ -38,31 +39,49 @@ public void execute(ChannelHandlerContext ctx, Client client) { return; } if (username == null) { - if (client.session == null && exitAll) { - sendError("Session invalid"); - return; - } - WebSocketFrameHandler handler = ctx.pipeline().get(WebSocketFrameHandler.class); - if (handler == null) { - sendError("Exit internal error"); - return; - } - Client newClient = new Client(null); - newClient.checkSign = client.checkSign; - handler.setClient(newClient); - if (client.session != null) server.sessionManager.remove(client.session); - if (exitAll) { - service.forEachActiveChannels(((channel, webSocketFrameHandler) -> { - Client client1 = webSocketFrameHandler.getClient(); - if (client.isAuth && client.username != null) { - if (!client1.isAuth || !client.username.equals(client1.username)) return; - } else { - if (client1.session != client.session) return; + if(client.useOAuth) { + WebSocketFrameHandler handler = ctx.pipeline().get(WebSocketFrameHandler.class); + if (handler == null) { + sendError("Exit internal error"); + return; + } + Client newClient = new Client(null); + newClient.checkSign = client.checkSign; + handler.setClient(newClient); + if(exitAll) { + if(client.auth instanceof AuthSupportGetSessionsFromUser) { + AuthSupportGetSessionsFromUser support = (AuthSupportGetSessionsFromUser) client.auth; + support.clearSessionsByUser(client.getUser()); } - exit(server, webSocketFrameHandler, channel, ExitRequestEvent.ExitReason.SERVER); - })); + } + sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT)); + } else { + if (client.session == null && exitAll) { + sendError("Session invalid"); + return; + } + WebSocketFrameHandler handler = ctx.pipeline().get(WebSocketFrameHandler.class); + if (handler == null) { + sendError("Exit internal error"); + return; + } + Client newClient = new Client(null); + newClient.checkSign = client.checkSign; + handler.setClient(newClient); + if (client.session != null) server.sessionManager.remove(client.session); + if (exitAll) { + service.forEachActiveChannels(((channel, webSocketFrameHandler) -> { + Client client1 = webSocketFrameHandler.getClient(); + if (client.isAuth && client.username != null) { + if (!client1.isAuth || !client.username.equals(client1.username)) return; + } else { + if (client1.session != client.session) return; + } + exit(server, webSocketFrameHandler, channel, ExitRequestEvent.ExitReason.SERVER); + })); + } + sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT)); } - sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT)); } else { service.forEachActiveChannels(((channel, webSocketFrameHandler) -> { Client client1 = webSocketFrameHandler.getClient(); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RefreshTokenResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RefreshTokenResponse.java new file mode 100644 index 00000000..79c27ace --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RefreshTokenResponse.java @@ -0,0 +1,47 @@ +package pro.gravit.launchserver.socket.response.auth; + +import io.netty.channel.ChannelHandlerContext; +import pro.gravit.launcher.events.request.AuthRequestEvent; +import pro.gravit.launcher.events.request.RefreshTokenRequestEvent; +import pro.gravit.launchserver.auth.AuthProviderPair; +import pro.gravit.launchserver.manangers.AuthManager; +import pro.gravit.launchserver.socket.Client; +import pro.gravit.launchserver.socket.response.SimpleResponse; + +public class RefreshTokenResponse extends SimpleResponse { + public String authId; + public String refreshToken; + + @Override + public String getType() { + return "refreshToken"; + } + + @Override + public void execute(ChannelHandlerContext ctx, Client client) throws Exception { + if(refreshToken == null) { + sendError("Invalid request"); + return; + } + AuthProviderPair pair; + if(!client.isAuth) { + if(authId == null || !client.useOAuth) { + pair = server.config.getAuthProviderPair(); + } else { + pair = client.auth; + } + } else { + pair = server.config.getAuthProviderPair(authId); + } + if(pair == null || !pair.isUseCore()) { + sendError("Invalid request"); + return; + } + AuthManager.AuthReport report = pair.core.refreshAccessToken(refreshToken, new AuthResponse.AuthContext(client, null, null, ip, AuthResponse.ConnectTypes.API, pair)); + if(report == null || !report.isUsingOAuth()) { + sendError("Invalid RefreshToken"); + return; + } + sendResult(new RefreshTokenRequestEvent(new AuthRequestEvent.OAuthRequestEvent(report.oauthAccessToken, report.oauthRefreshToken, report.oauthExpire))); + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RestoreResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RestoreResponse.java new file mode 100644 index 00000000..caea76b4 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RestoreResponse.java @@ -0,0 +1,82 @@ +package pro.gravit.launchserver.socket.response.auth; + +import io.netty.channel.ChannelHandlerContext; +import pro.gravit.launcher.events.request.AuthRequestEvent; +import pro.gravit.launcher.events.request.RestoreRequestEvent; +import pro.gravit.launchserver.auth.AuthProviderPair; +import pro.gravit.launchserver.auth.core.AuthCoreProvider; +import pro.gravit.launchserver.auth.core.User; +import pro.gravit.launchserver.auth.core.UserSession; +import pro.gravit.launchserver.socket.Client; +import pro.gravit.launchserver.socket.response.SimpleResponse; + +import java.util.HashMap; +import java.util.Map; + +public class RestoreResponse extends SimpleResponse { + @FunctionalInterface + public interface ExtendedTokenProvider { + void accept(Client client, AuthProviderPair pair, String extendedToken); + } + public static Map providers = new HashMap<>(); + public String authId; + public String accessToken; + public Map extended; + public boolean needUserInfo; + + @Override + public String getType() { + return "restore"; + } + + @Override + public void execute(ChannelHandlerContext ctx, Client client) throws Exception { + if(accessToken == null && !client.isAuth && needUserInfo) { + sendError("Invalid request"); + return; + } + AuthProviderPair pair; + if(!client.isAuth) { + if(authId == null || !client.useOAuth) { + pair = server.config.getAuthProviderPair(); + } else { + pair = client.auth; + } + } else { + pair = server.config.getAuthProviderPair(authId); + } + if(pair == null || !pair.isUseCore()) { + sendError("Invalid request"); + return; + } + if(accessToken != null) { + UserSession session; + try { + session = pair.core.getUserSessionByOAuthAccessToken(accessToken); + } catch (AuthCoreProvider.OAuthAccessTokenExpired e) { + sendError(AuthRequestEvent.OAUTH_TOKEN_EXPIRE); + return; + } + if(session == null) { + sendError(AuthRequestEvent.OAUTH_TOKEN_INVALID); + return; + } + User user = session.getUser(); + client.coreObject = user; + client.sessionObject = session; + server.authManager.internalAuth(client, client.type == null ? AuthResponse.ConnectTypes.API : client.type, pair, user.getUsername(), user.getUUID(), user.getPermissions(), true); + } + if(extended != null) { + extended.forEach((k,v) -> { + ExtendedTokenProvider provider = providers.get(k); + if(provider == null) return; + provider.accept(client, pair, v); + }); + } + if(needUserInfo && client.isAuth) { + sendResult(new RestoreRequestEvent(CurrentUserResponse.collectUserInfoFromClient(client))); + } else { + sendResult(new RestoreRequestEvent()); + } + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java index 6c5ff6cb..02ee4ddb 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java @@ -10,6 +10,8 @@ public class AuthRequestEvent extends RequestEvent { public static final String TWO_FACTOR_NEED_ERROR_MESSAGE = "auth.require2fa"; public static final String ONE_FACTOR_NEED_ERROR_MESSAGE_PREFIX = "auth.require.factor."; + public static final String OAUTH_TOKEN_EXPIRE = "auth.expiretoken"; + public static final String OAUTH_TOKEN_INVALID = "auth.invalidtoken"; public static final String USER_NOT_FOUND_ERROR_MESSAGE = "auth.message.usernotfound"; public static final String WRONG_PASSWORD_ERROR_MESSAGE = "auth.message.wrongpassword"; public static final String ACCOUNT_BLOCKED_ERROR_MESSAGE = "auth.message.blocked"; diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/RefreshTokenRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/RefreshTokenRequestEvent.java new file mode 100644 index 00000000..0f1a5806 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/RefreshTokenRequestEvent.java @@ -0,0 +1,16 @@ +package pro.gravit.launcher.events.request; + +import pro.gravit.launcher.events.RequestEvent; + +public class RefreshTokenRequestEvent extends RequestEvent { + public AuthRequestEvent.OAuthRequestEvent oauth; + + public RefreshTokenRequestEvent(AuthRequestEvent.OAuthRequestEvent oauth) { + this.oauth = oauth; + } + + @Override + public String getType() { + return "refreshToken"; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/RestoreRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/RestoreRequestEvent.java new file mode 100644 index 00000000..0d8cac4a --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/RestoreRequestEvent.java @@ -0,0 +1,19 @@ +package pro.gravit.launcher.events.request; + +import pro.gravit.launcher.events.RequestEvent; + +public class RestoreRequestEvent extends RequestEvent { + public CurrentUserRequestEvent.UserInfo userInfo; + + public RestoreRequestEvent() { + } + + public RestoreRequestEvent(CurrentUserRequestEvent.UserInfo userInfo) { + this.userInfo = userInfo; + } + + @Override + public String getType() { + return "restore"; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/auth/RefreshTokenRequest.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/auth/RefreshTokenRequest.java new file mode 100644 index 00000000..0daa585e --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/auth/RefreshTokenRequest.java @@ -0,0 +1,19 @@ +package pro.gravit.launcher.request.auth; + +import pro.gravit.launcher.events.request.RefreshTokenRequestEvent; +import pro.gravit.launcher.request.Request; + +public class RefreshTokenRequest extends Request { + public String authId; + public String refreshToken; + + public RefreshTokenRequest(String authId, String refreshToken) { + this.authId = authId; + this.refreshToken = refreshToken; + } + + @Override + public String getType() { + return "refreshToken"; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/auth/RestoreRequest.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/auth/RestoreRequest.java new file mode 100644 index 00000000..d23942b9 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/auth/RestoreRequest.java @@ -0,0 +1,28 @@ +package pro.gravit.launcher.request.auth; + +import pro.gravit.launcher.events.request.RestoreRequestEvent; +import pro.gravit.launcher.request.Request; + +import java.util.Map; + +public class RestoreRequest extends Request { + public String authId; + public String accessToken; + public Map extended; + public boolean needUserInfo; + + public RestoreRequest() { + } + + public RestoreRequest(String authId, String accessToken, Map extended, boolean needUserInfo) { + this.authId = authId; + this.accessToken = accessToken; + this.extended = extended; + this.needUserInfo = needUserInfo; + } + + @Override + public String getType() { + return "restore"; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java index 277681a2..b4ee8dca 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java @@ -109,6 +109,8 @@ public void registerResults() { results.register("pingServer", PingServerRequestEvent.class); results.register("currentUser", CurrentUserRequestEvent.class); results.register("features", FeaturesRequestEvent.class); + results.register("refreshToken", RefreshTokenRequestEvent.class); + results.register("restore", RestoreRequestEvent.class); } public void waitIfNotConnected() {