[FEATURE] AuthCoreProvider: OAuth Support Part 3

This commit is contained in:
Gravita 2021-05-23 17:17:56 +07:00
parent d3751732b0
commit ea3310b738
12 changed files with 282 additions and 23 deletions

View file

@ -11,6 +11,7 @@
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.User; 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.AuthProvider;
import pro.gravit.launchserver.auth.provider.AuthProviderDAOResult; import pro.gravit.launchserver.auth.provider.AuthProviderDAOResult;
import pro.gravit.launchserver.auth.provider.AuthProviderResult; 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 { 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);
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); User user = provider.getUserByUsername(context.login);
if(user == null) { if(user == null) {
throw new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE); throw new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE);

View file

@ -34,6 +34,8 @@ public class Client {
public transient pro.gravit.launchserver.auth.core.User coreObject; public transient pro.gravit.launchserver.auth.core.User coreObject;
public transient pro.gravit.launchserver.auth.core.UserSession sessionObject;
public transient Map<String, Object> properties; public transient Map<String, Object> properties;
public Map<String, String> serializableProperties; public Map<String, String> serializableProperties;

View file

@ -92,6 +92,8 @@ public static void registerResponses() {
providers.register("pingServer", PingServerResponse.class); providers.register("pingServer", PingServerResponse.class);
providers.register("currentUser", CurrentUserResponse.class); providers.register("currentUser", CurrentUserResponse.class);
providers.register("features", FeaturesResponse.class); providers.register("features", FeaturesResponse.class);
providers.register("refreshToken", RefreshTokenResponse.class);
providers.register("restore", RestoreResponse.class);
} }
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) { public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {

View file

@ -6,6 +6,7 @@
import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.events.request.ExitRequestEvent; import pro.gravit.launcher.events.request.ExitRequestEvent;
import pro.gravit.launchserver.LaunchServer; 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.Client;
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler; import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
@ -38,6 +39,23 @@ public void execute(ChannelHandlerContext ctx, Client client) {
return; return;
} }
if (username == null) { if (username == null) {
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());
}
}
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
} else {
if (client.session == null && exitAll) { if (client.session == null && exitAll) {
sendError("Session invalid"); sendError("Session invalid");
return; return;
@ -63,6 +81,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
})); }));
} }
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT)); sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
}
} else { } else {
service.forEachActiveChannels(((channel, webSocketFrameHandler) -> { service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
Client client1 = webSocketFrameHandler.getClient(); Client client1 = webSocketFrameHandler.getClient();

View file

@ -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)));
}
}

View file

@ -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<String, ExtendedTokenProvider> providers = new HashMap<>();
public String authId;
public String accessToken;
public Map<String, String> 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());
}
}
}

View file

@ -10,6 +10,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 ONE_FACTOR_NEED_ERROR_MESSAGE_PREFIX = "auth.require.factor."; 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 USER_NOT_FOUND_ERROR_MESSAGE = "auth.message.usernotfound";
public static final String WRONG_PASSWORD_ERROR_MESSAGE = "auth.message.wrongpassword"; public static final String WRONG_PASSWORD_ERROR_MESSAGE = "auth.message.wrongpassword";
public static final String ACCOUNT_BLOCKED_ERROR_MESSAGE = "auth.message.blocked"; public static final String ACCOUNT_BLOCKED_ERROR_MESSAGE = "auth.message.blocked";

View file

@ -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";
}
}

View file

@ -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";
}
}

View file

@ -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<RefreshTokenRequestEvent> {
public String authId;
public String refreshToken;
public RefreshTokenRequest(String authId, String refreshToken) {
this.authId = authId;
this.refreshToken = refreshToken;
}
@Override
public String getType() {
return "refreshToken";
}
}

View file

@ -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<RestoreRequestEvent> {
public String authId;
public String accessToken;
public Map<String, String> extended;
public boolean needUserInfo;
public RestoreRequest() {
}
public RestoreRequest(String authId, String accessToken, Map<String, String> extended, boolean needUserInfo) {
this.authId = authId;
this.accessToken = accessToken;
this.extended = extended;
this.needUserInfo = needUserInfo;
}
@Override
public String getType() {
return "restore";
}
}

View file

@ -109,6 +109,8 @@ public void registerResults() {
results.register("pingServer", PingServerRequestEvent.class); results.register("pingServer", PingServerRequestEvent.class);
results.register("currentUser", CurrentUserRequestEvent.class); results.register("currentUser", CurrentUserRequestEvent.class);
results.register("features", FeaturesRequestEvent.class); results.register("features", FeaturesRequestEvent.class);
results.register("refreshToken", RefreshTokenRequestEvent.class);
results.register("restore", RestoreRequestEvent.class);
} }
public void waitIfNotConnected() { public void waitIfNotConnected() {