From 4eec43f55cb2c0d87ed2b85cbff048f9f986aa1f Mon Sep 17 00:00:00 2001 From: kuroppoi <68156848+kuroppoi@users.noreply.github.com> Date: Wed, 9 Jun 2021 22:12:18 +0200 Subject: [PATCH] Allow multiple auth tokens per player --- .../java/brainwine/api/GatewayService.java | 10 ++-- .../gameserver/entity/player/Player.java | 20 ++++--- .../entity/player/PlayerManager.java | 53 ++++++++++++------- 3 files changed, 51 insertions(+), 32 deletions(-) diff --git a/api/src/main/java/brainwine/api/GatewayService.java b/api/src/main/java/brainwine/api/GatewayService.java index 6a24ced..30297e5 100644 --- a/api/src/main/java/brainwine/api/GatewayService.java +++ b/api/src/main/java/brainwine/api/GatewayService.java @@ -45,8 +45,7 @@ public class GatewayService { return; } - playerManager.registerPlayer(name); - String token = playerManager.refreshAuthToken(name); + String token = playerManager.register(name); ctx.json(new ServerConnectInfo(api.getGameServerHost(), name, token)); }); @@ -58,7 +57,9 @@ public class GatewayService { String token = request.getToken(); if(password != null) { - if(!playerManager.verifyPassword(name, password)) { + token = playerManager.login(name, password); + + if(token == null) { ContextUtils.error(ctx, "Username or password is incorrect. Please check your credentials."); return; } @@ -72,8 +73,7 @@ public class GatewayService { return; } - String newToken = playerManager.refreshAuthToken(name); - ctx.json(new ServerConnectInfo(api.getGameServerHost(), name, newToken)); + ctx.json(new ServerConnectInfo(api.getGameServerHost(), name, token)); }); // Password reset request diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java index cba1f54..93253a2 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java @@ -61,7 +61,7 @@ import brainwine.gameserver.zone.Chunk; import brainwine.gameserver.zone.MetaBlock; import brainwine.gameserver.zone.Zone; -@JsonIncludeProperties({"name", "email", "password_hash", "token_hash", "admin", "karma", "crowns", "inventory", "equipped_clothing", "equipped_colors", "current_zone"}) +@JsonIncludeProperties({"name", "email", "password_hash", "auth_tokens", "admin", "karma", "crowns", "inventory", "equipped_clothing", "equipped_colors", "current_zone"}) public class Player extends Entity implements CommandExecutor { public static final int MAX_SKILL_LEVEL = 15; @@ -80,8 +80,8 @@ public class Player extends Entity implements CommandExecutor { @JsonProperty("password_hash") private String password; - @JsonProperty("token_hash") - private String authToken; + @JsonProperty("auth_tokens") + private final List authTokens = new ArrayList<>(); @JsonProperty("admin") private boolean admin; @@ -512,12 +512,16 @@ public class Player extends Entity implements CommandExecutor { return password; } - protected void setAuthToken(String authToken) { - this.authToken = authToken; + protected void clearAuthTokens() { + authTokens.clear(); } - protected String getAuthToken() { - return authToken; + protected void addAuthToken(String authToken) { + authTokens.add(authToken); + } + + protected List getAuthTokens() { + return authTokens; } public void setAdmin(boolean admin) { @@ -699,7 +703,7 @@ public class Player extends Entity implements CommandExecutor { Map map = new HashMap<>(); map.put("email", email); map.put("password_hash", password); - map.put("token_hash", authToken); + map.put("auth_tokens", authTokens); map.put("name", name); map.put("admin", admin); map.put("karma", karma); diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/PlayerManager.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/PlayerManager.java index a5df4f4..8fda393 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/PlayerManager.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/PlayerManager.java @@ -94,32 +94,47 @@ public class PlayerManager { } } - public String refreshAuthToken(String name) { - Player player = getPlayer(name); - String token = UUID.randomUUID().toString(); - player.setAuthToken(BCrypt.hashpw(token, BCrypt.gensalt())); - return token; - } - - public boolean verifyPassword(String name, String password) { - Player player = getPlayer(name); - return player == null ? false : player.getPassword() == null ? false : BCrypt.checkpw(password, player.getPassword()); - } - - public boolean verifyAuthToken(String name, String token) { - Player player = getPlayer(name); - return player == null ? false : BCrypt.checkpw(token, player.getAuthToken()); - } - - public void registerPlayer(String name) { + public String register(String name) { if(getPlayer(name) != null) { - return; + return null; } String id = UUID.randomUUID().toString(); Player player = new Player(id, name, GameServer.getInstance().getZoneManager().getRandomZone()); // TODO tutorial zone playersById.put(id, player); playersByName.put(name.toLowerCase(), player); + String authToken = UUID.randomUUID().toString(); + player.addAuthToken(BCrypt.hashpw(authToken, BCrypt.gensalt())); + return authToken; + } + + public String login(String name, String password) { + Player player = getPlayer(name); + + if(player == null || player.getPassword() == null) { + return null; + } + + if(!BCrypt.checkpw(password, player.getPassword())) { + return null; + } + + String authToken = UUID.randomUUID().toString(); + player.addAuthToken(BCrypt.hashpw(authToken, BCrypt.gensalt())); + return authToken; + } + + public boolean verifyAuthToken(String name, String authToken) { + Player player = getPlayer(name); + + // Might not be very efficient... + for(String hashedToken : player.getAuthTokens()) { + if(BCrypt.checkpw(authToken, hashedToken)) { + return true; + } + } + + return false; } public void onPlayerAuthenticate(Connection connection, String version, String name, String authToken) {