From b06d249224d8839f44e73c5c5a2b7d98a473f6f2 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Fri, 13 May 2022 16:49:46 +0800 Subject: [PATCH] Implement access control --- README.md | 10 +++- src/main/java/me/exzork/gcauth/Config.java | 1 + .../gcauth/handler/ChangePasswordHandler.java | 47 ++++++++------- .../handler/GCAuthAuthenticationHandler.java | 5 ++ .../exzork/gcauth/handler/LoginHandler.java | 25 +++++--- .../gcauth/handler/RegisterHandler.java | 57 +++++++++++-------- .../gcauth/json/ChangePasswordAccount.java | 1 + .../gcauth/json/LoginGenerateToken.java | 1 + .../exzork/gcauth/json/RegisterAccount.java | 1 + .../exzork/gcauth/utils/Authentication.java | 3 +- 10 files changed, 92 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 97951cb..b1082a5 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,25 @@ # GCAuth + Grasscutter Authentication System + ### Usage : - Place jar inside plugins folder of Grasscutter. - To change hash algorithm change `Hash` in config.json inside plugins/GCAuth (Only Bcrypt and Scrypt is supported) +- To use access control, you need set the `ACCESS_KEY` in config.json inside plugins/GCAuth. (Optional) - All payload must be send with `application/json` and Compact JSON format ( without unnecessary spaces ) - Auth endpoint is: - Authentication Checking : `/authentication/type` (GET) , it'll return `me.exzork.gcauth.handler.GCAuthAuthenticationHandler` if GCAuth is loaded and enabled. - Register: `/authentication/register` (POST) ``` - {"username":"username","password":"password","password_confirmation":"password_confirmation"} + {"username":"username","password":"password","password_confirmation":"password_confirmation","access_key":"access_key"} ``` - Login: `/authentication/login` (POST) ``` - {"username":"username","password":"password"} + {"username":"username","password":"password","access_key":"access_key"} ``` - Change password: `/authentication/change_password` (POST) ``` - {"username":"username","new_password":"new_password","new_password_confirmation":"new_password_confirmation","old_password":"old_password"} + {"username":"username","new_password":"new_password","new_password_confirmation":"new_password_confirmation","old_password":"old_password","access_key":"access_key"} ``` - Response is `JSON` with following keys: - `status` : `success` or `error` @@ -29,6 +32,7 @@ Grasscutter Authentication System - UNKNOWN : Unknown error - INVALID_ACCOUNT : Username or password is invalid - NO_PASSWORD : Password is not set, please set password first by resetting it (change password) + - ERROR_ACCESS_KEY : Access key is invalid (if access control is enabled) - `jwt` : JWT token if success with body : - `token` : Token used for authentication, paste it in username field of client. - `username` : Username of the user. diff --git a/src/main/java/me/exzork/gcauth/Config.java b/src/main/java/me/exzork/gcauth/Config.java index 20c332a..6940900 100644 --- a/src/main/java/me/exzork/gcauth/Config.java +++ b/src/main/java/me/exzork/gcauth/Config.java @@ -5,4 +5,5 @@ import me.exzork.gcauth.utils.Authentication; public final class Config { public String Hash = "BCRYPT"; public String jwtSecret = Authentication.generateRandomString(32); + public String ACCESS_KEY = ""; } diff --git a/src/main/java/me/exzork/gcauth/handler/ChangePasswordHandler.java b/src/main/java/me/exzork/gcauth/handler/ChangePasswordHandler.java index 20ffd43..3821e4b 100644 --- a/src/main/java/me/exzork/gcauth/handler/ChangePasswordHandler.java +++ b/src/main/java/me/exzork/gcauth/handler/ChangePasswordHandler.java @@ -6,6 +6,7 @@ import emu.grasscutter.game.Account; import express.http.HttpContextHandler; import express.http.Request; import express.http.Response; +import me.exzork.gcauth.GCAuth; import me.exzork.gcauth.json.AuthResponseJson; import me.exzork.gcauth.json.ChangePasswordAccount; import me.exzork.gcauth.utils.Authentication; @@ -25,30 +26,36 @@ public class ChangePasswordHandler implements HttpContextHandler { authResponse.jwt = ""; } else { ChangePasswordAccount changePasswordAccount = new Gson().fromJson(requestBody, ChangePasswordAccount.class); - if (changePasswordAccount.new_password.equals(changePasswordAccount.new_password_confirmation)) { - Account account = Authentication.getAccountByUsernameAndPassword(changePasswordAccount.username, changePasswordAccount.old_password); - if (account == null) { - authResponse.success = false; - authResponse.message = "INVALID_ACCOUNT"; // ENG = "Invalid username or password" - authResponse.jwt = ""; - } else { - if (changePasswordAccount.new_password.length() >= 8) { - String newPassword = Authentication.generateHash(changePasswordAccount.new_password); - account.setPassword(newPassword); - account.save(); - authResponse.success = true; - authResponse.message = ""; + if (!GCAuth.getConfigStatic().ACCESS_KEY.isEmpty() && !GCAuth.getConfigStatic().ACCESS_KEY.equals(changePasswordAccount.access_key)){ + authResponse.success = false; + authResponse.message = "ERROR_ACCESS_KEY"; // ENG = "Error access key was sent with the request" + authResponse.jwt = ""; + } else { + if (changePasswordAccount.new_password.equals(changePasswordAccount.new_password_confirmation)) { + Account account = Authentication.getAccountByUsernameAndPassword(changePasswordAccount.username, changePasswordAccount.old_password); + if (account == null) { + authResponse.success = false; + authResponse.message = "INVALID_ACCOUNT"; // ENG = "Invalid username or password" authResponse.jwt = ""; } else { - authResponse.success = false; - authResponse.message = "PASSWORD_INVALID"; // ENG = "Password must be at least 8 characters long" - authResponse.jwt = ""; + if (changePasswordAccount.new_password.length() >= 8) { + String newPassword = Authentication.generateHash(changePasswordAccount.new_password); + account.setPassword(newPassword); + account.save(); + authResponse.success = true; + authResponse.message = ""; + authResponse.jwt = ""; + } else { + authResponse.success = false; + authResponse.message = "PASSWORD_INVALID"; // ENG = "Password must be at least 8 characters long" + authResponse.jwt = ""; + } } + } else { + authResponse.success = false; + authResponse.message = "PASSWORD_MISMATCH"; // ENG = "Passwords do not match." + authResponse.jwt = ""; } - } else { - authResponse.success = false; - authResponse.message = "PASSWORD_MISMATCH"; // ENG = "Passwords do not match." - authResponse.jwt = ""; } } } catch (Exception e) { diff --git a/src/main/java/me/exzork/gcauth/handler/GCAuthAuthenticationHandler.java b/src/main/java/me/exzork/gcauth/handler/GCAuthAuthenticationHandler.java index af5497b..19ab1c6 100644 --- a/src/main/java/me/exzork/gcauth/handler/GCAuthAuthenticationHandler.java +++ b/src/main/java/me/exzork/gcauth/handler/GCAuthAuthenticationHandler.java @@ -44,6 +44,11 @@ public class GCAuthAuthenticationHandler implements AuthenticationHandler { } } + @Override + public boolean verifyUser(String details) { + return false; + } + @Override public LoginResultJson handleGameLogin(Request request, LoginAccountRequestJson requestData) { LoginResultJson responseData = new LoginResultJson(); diff --git a/src/main/java/me/exzork/gcauth/handler/LoginHandler.java b/src/main/java/me/exzork/gcauth/handler/LoginHandler.java index 5cf89bc..77b2aab 100644 --- a/src/main/java/me/exzork/gcauth/handler/LoginHandler.java +++ b/src/main/java/me/exzork/gcauth/handler/LoginHandler.java @@ -6,6 +6,7 @@ import emu.grasscutter.game.Account; import express.http.HttpContextHandler; import express.http.Request; import express.http.Response; +import me.exzork.gcauth.GCAuth; import me.exzork.gcauth.json.AuthResponseJson; import me.exzork.gcauth.json.LoginGenerateToken; import me.exzork.gcauth.utils.Authentication; @@ -26,20 +27,26 @@ public class LoginHandler implements HttpContextHandler { authResponse.jwt = ""; } else { LoginGenerateToken loginGenerateToken = new Gson().fromJson(requestBody, LoginGenerateToken.class); - Account account = Authentication.getAccountByUsernameAndPassword(loginGenerateToken.username, loginGenerateToken.password); - if (account == null) { + if (!GCAuth.getConfigStatic().ACCESS_KEY.isEmpty() && !GCAuth.getConfigStatic().ACCESS_KEY.equals(loginGenerateToken.access_key)){ authResponse.success = false; - authResponse.message = "INVALID_ACCOUNT"; // ENG = "Invalid username or password" + authResponse.message = "ERROR_ACCESS_KEY"; // ENG = "Error access key was sent with the request" authResponse.jwt = ""; } else { - if (account.getPassword() != null && !account.getPassword().isEmpty()) { - authResponse.success = true; - authResponse.message = ""; - authResponse.jwt = Authentication.generateJwt(account); - } else { + Account account = Authentication.getAccountByUsernameAndPassword(loginGenerateToken.username, loginGenerateToken.password); + if (account == null) { authResponse.success = false; - authResponse.message = "NO_PASSWORD"; // ENG = "There is no account password set. Please create a password by resetting it." + authResponse.message = "INVALID_ACCOUNT"; // ENG = "Invalid username or password" authResponse.jwt = ""; + } else { + if (account.getPassword() != null && !account.getPassword().isEmpty()) { + authResponse.success = true; + authResponse.message = ""; + authResponse.jwt = Authentication.generateJwt(account); + } else { + authResponse.success = false; + authResponse.message = "NO_PASSWORD"; // ENG = "There is no account password set. Please create a password by resetting it." + authResponse.jwt = ""; + } } } } diff --git a/src/main/java/me/exzork/gcauth/handler/RegisterHandler.java b/src/main/java/me/exzork/gcauth/handler/RegisterHandler.java index bbe743e..9893c5f 100644 --- a/src/main/java/me/exzork/gcauth/handler/RegisterHandler.java +++ b/src/main/java/me/exzork/gcauth/handler/RegisterHandler.java @@ -7,6 +7,7 @@ import emu.grasscutter.game.Account; import express.http.HttpContextHandler; import express.http.Request; import express.http.Response; +import me.exzork.gcauth.GCAuth; import me.exzork.gcauth.json.AuthResponseJson; import me.exzork.gcauth.json.RegisterAccount; import me.exzork.gcauth.utils.Authentication; @@ -26,43 +27,49 @@ public class RegisterHandler implements HttpContextHandler { authResponse.jwt = ""; } else { RegisterAccount registerAccount = new Gson().fromJson(requestBody, RegisterAccount.class); - if (registerAccount.password.equals(registerAccount.password_confirmation)) { - if (registerAccount.password.length() >= 8) { - String password = Authentication.generateHash(registerAccount.password); - try{ - Account account = Authentication.getAccountByUsernameAndPassword(registerAccount.username, ""); - if (account != null) { - account.setPassword(password); - account.save(); - authResponse.success = true; - authResponse.message = ""; - authResponse.jwt = ""; - } else { - account = DatabaseHelper.createAccountWithPassword(registerAccount.username, password); - if (account == null) { - authResponse.success = false; - authResponse.message = "USERNAME_TAKEN"; // ENG = "Username has already been taken by another user." - authResponse.jwt = ""; - } else { + if (!GCAuth.getConfigStatic().ACCESS_KEY.isEmpty() && !GCAuth.getConfigStatic().ACCESS_KEY.equals(registerAccount.access_key)){ + authResponse.success = false; + authResponse.message = "ERROR_ACCESS_KEY"; // ENG = "Error access key was sent with the request" + authResponse.jwt = ""; + } else { + if (registerAccount.password.equals(registerAccount.password_confirmation)) { + if (registerAccount.password.length() >= 8) { + String password = Authentication.generateHash(registerAccount.password); + try{ + Account account = Authentication.getAccountByUsernameAndPassword(registerAccount.username, ""); + if (account != null) { + account.setPassword(password); + account.save(); authResponse.success = true; authResponse.message = ""; authResponse.jwt = ""; + } else { + account = DatabaseHelper.createAccountWithPassword(registerAccount.username, password); + if (account == null) { + authResponse.success = false; + authResponse.message = "USERNAME_TAKEN"; // ENG = "Username has already been taken by another user." + authResponse.jwt = ""; + } else { + authResponse.success = true; + authResponse.message = ""; + authResponse.jwt = ""; + } } + }catch (Exception ignored){ + authResponse.success = false; + authResponse.message = "UNKNOWN"; // ENG = "Username has already been taken by another user." + authResponse.jwt = ""; } - }catch (Exception ignored){ + } else { authResponse.success = false; - authResponse.message = "UNKNOWN"; // ENG = "Username has already been taken by another user." + authResponse.message = "PASSWORD_INVALID"; // ENG = "Password must be at least 8 characters long" authResponse.jwt = ""; } } else { authResponse.success = false; - authResponse.message = "PASSWORD_INVALID"; // ENG = "Password must be at least 8 characters long" + authResponse.message = "PASSWORD_MISMATCH"; // ENG = "Passwords do not match." authResponse.jwt = ""; } - } else { - authResponse.success = false; - authResponse.message = "PASSWORD_MISMATCH"; // ENG = "Passwords do not match." - authResponse.jwt = ""; } } } catch (Exception e) { diff --git a/src/main/java/me/exzork/gcauth/json/ChangePasswordAccount.java b/src/main/java/me/exzork/gcauth/json/ChangePasswordAccount.java index e823045..50815de 100644 --- a/src/main/java/me/exzork/gcauth/json/ChangePasswordAccount.java +++ b/src/main/java/me/exzork/gcauth/json/ChangePasswordAccount.java @@ -5,4 +5,5 @@ public class ChangePasswordAccount { public String new_password; public String new_password_confirmation; public String old_password; + public String access_key; } diff --git a/src/main/java/me/exzork/gcauth/json/LoginGenerateToken.java b/src/main/java/me/exzork/gcauth/json/LoginGenerateToken.java index 9a9b854..0f2267e 100644 --- a/src/main/java/me/exzork/gcauth/json/LoginGenerateToken.java +++ b/src/main/java/me/exzork/gcauth/json/LoginGenerateToken.java @@ -3,4 +3,5 @@ package me.exzork.gcauth.json; public class LoginGenerateToken { public String username; public String password; + public String access_key; } diff --git a/src/main/java/me/exzork/gcauth/json/RegisterAccount.java b/src/main/java/me/exzork/gcauth/json/RegisterAccount.java index 880639f..93e41fe 100644 --- a/src/main/java/me/exzork/gcauth/json/RegisterAccount.java +++ b/src/main/java/me/exzork/gcauth/json/RegisterAccount.java @@ -4,4 +4,5 @@ public class RegisterAccount { public String username; public String password; public String password_confirmation; + public String access_key; } diff --git a/src/main/java/me/exzork/gcauth/utils/Authentication.java b/src/main/java/me/exzork/gcauth/utils/Authentication.java index 918d3f3..09da8f5 100644 --- a/src/main/java/me/exzork/gcauth/utils/Authentication.java +++ b/src/main/java/me/exzork/gcauth/utils/Authentication.java @@ -49,12 +49,11 @@ public final class Authentication { } public static String generateJwt(Account account) { - String jws = JWT.create() + return JWT.create() .withClaim("token",generateOneTimeToken(account)) .withClaim("username",account.getUsername()) .withClaim("uid",account.getPlayerUid()) .sign(key); - return jws; } public static String generateHash(String password) {