mirror of
https://github.com/exzork/GCAuth.git
synced 2024-11-27 18:01:14 +00:00
use otp instead of token, implement createAccount and verifyUser, add some config
This commit is contained in:
parent
66df9935d2
commit
547a1b03c7
@ -10,7 +10,7 @@ sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
|
||||
group 'me.exzork.gcauth'
|
||||
version '2.2.1'
|
||||
version '2.3.0'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@ -20,7 +20,7 @@ dependencies {
|
||||
implementation files('lib/grasscutter-1.1.2-dev.jar')
|
||||
implementation 'org.springframework.security:spring-security-crypto:5.6.3'
|
||||
implementation 'commons-logging:commons-logging:1.2'
|
||||
implementation 'com.auth0:java-jwt:3.19.1'
|
||||
implementation 'com.auth0:java-jwt:3.19.2'
|
||||
implementation 'javax.servlet:javax.servlet-api:4.0.1'
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ jar{
|
||||
jar.baseName = 'gcauth'
|
||||
from{
|
||||
configurations.runtimeClasspath.collect{
|
||||
if (it.name in ['spring-security-crypto-5.6.3.jar','commons-logging-1.2.jar','javax.servlet-api-4.0.1.jar','java-jwt-3.19.1.jar','jackson-annotations-2.13.2.jar','jackson-core-2.13.2.jar','jackson-databind-2.13.2.2.jar']) {
|
||||
if (it.name in ['spring-security-crypto-5.6.3.jar','commons-logging-1.2.jar','javax.servlet-api-4.0.1.jar','java-jwt-3.19.2.jar','jackson-annotations-2.13.2.jar','jackson-core-2.13.2.jar','jackson-databind-2.13.2.2.jar']) {
|
||||
zipTree(it)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import me.exzork.gcauth.utils.Authentication;
|
||||
public final class Config {
|
||||
public String Hash = "BCRYPT";
|
||||
public String jwtSecret = Authentication.generateRandomString(32);
|
||||
public long jwtExpiration = 86400;
|
||||
public long otpExpiration = 300;
|
||||
public String[] defaultPermissions = new String[]{""};
|
||||
public String ACCESS_KEY = "";
|
||||
public String accessKey = "";
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import emu.grasscutter.auth.DefaultAuthentication;
|
||||
import emu.grasscutter.plugin.Plugin;
|
||||
import static emu.grasscutter.Configuration.ACCOUNT;
|
||||
|
||||
import emu.grasscutter.plugin.PluginManager;
|
||||
import me.exzork.gcauth.handler.*;
|
||||
import me.exzork.gcauth.utils.Authentication;
|
||||
|
||||
@ -21,6 +22,9 @@ public class GCAuth extends Plugin {
|
||||
private static Config config;
|
||||
private File configFile;
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
public static GCAuth getInstance() {
|
||||
return (GCAuth) Grasscutter.getPluginManager().getPlugin("GCAuth");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
@ -65,10 +69,6 @@ public class GCAuth extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
public static Config getConfigStatic() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package me.exzork.gcauth.handler;
|
||||
|
||||
import emu.grasscutter.auth.*;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.server.http.objects.ComboTokenResJson;
|
||||
import emu.grasscutter.server.http.objects.LoginResultJson;
|
||||
import me.exzork.gcauth.utils.Authentication;
|
||||
|
||||
public class GCAuthAuthenticationHandler implements AuthenticationSystem {
|
||||
private final Authenticator<LoginResultJson> gcAuthAuthenticator = new GCAuthenticators.GCAuthAuthenticator();
|
||||
@ -13,7 +15,8 @@ public class GCAuthAuthenticationHandler implements AuthenticationSystem {
|
||||
|
||||
@Override
|
||||
public void createAccount(String username, String password) {
|
||||
// Unhandled.
|
||||
password = Authentication.generateHash(password);
|
||||
DatabaseHelper.createAccountWithPassword(username, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -23,7 +26,11 @@ public class GCAuthAuthenticationHandler implements AuthenticationSystem {
|
||||
|
||||
@Override
|
||||
public Account verifyUser(String s) {
|
||||
return null;
|
||||
String uid = Authentication.getUsernameFromJwt(s);
|
||||
if (uid == null) {
|
||||
return null;
|
||||
}
|
||||
return DatabaseHelper.getAccountById(uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -45,4 +52,9 @@ public class GCAuthAuthenticationHandler implements AuthenticationSystem {
|
||||
public ExternalAuthenticator getExternalAuthenticator() {
|
||||
return externalAuthenticator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthAuthenticator getOAuthAuthenticator() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class GCAuthExternalAuthenticator implements ExternalAuthenticator {
|
||||
authResponse.jwt = "";
|
||||
} else {
|
||||
LoginGenerateToken loginGenerateToken = new Gson().fromJson(requestBody, LoginGenerateToken.class);
|
||||
if (!GCAuth.getConfigStatic().ACCESS_KEY.isEmpty() && !GCAuth.getConfigStatic().ACCESS_KEY.equals(loginGenerateToken.access_key)){
|
||||
if (!GCAuth.getInstance().getConfig().accessKey.isEmpty() && !GCAuth.getInstance().getConfig().accessKey.equals(loginGenerateToken.access_key)){
|
||||
authResponse.success = false;
|
||||
authResponse.message = "ERROR_ACCESS_KEY"; // ENG = "Error access key was sent with the request"
|
||||
authResponse.jwt = "";
|
||||
@ -77,7 +77,7 @@ public class GCAuthExternalAuthenticator implements ExternalAuthenticator {
|
||||
authResponse.jwt = "";
|
||||
} else {
|
||||
RegisterAccount registerAccount = new Gson().fromJson(requestBody, RegisterAccount.class);
|
||||
if (!GCAuth.getConfigStatic().ACCESS_KEY.isEmpty() && !GCAuth.getConfigStatic().ACCESS_KEY.equals(registerAccount.access_key)){
|
||||
if (!GCAuth.getInstance().getConfig().accessKey.isEmpty() && !GCAuth.getInstance().getConfig().accessKey.equals(registerAccount.access_key)){
|
||||
authResponse.success = false;
|
||||
authResponse.message = "ERROR_ACCESS_KEY"; // ENG = "Error access key was sent with the request"
|
||||
authResponse.jwt = "";
|
||||
@ -130,8 +130,8 @@ public class GCAuthExternalAuthenticator implements ExternalAuthenticator {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (authResponse.success) {
|
||||
if (GCAuth.getConfigStatic().defaultPermissions.length > 0) {
|
||||
for (String permission : GCAuth.getConfigStatic().defaultPermissions) {
|
||||
if (GCAuth.getInstance().getConfig().defaultPermissions.length > 0) {
|
||||
for (String permission : GCAuth.getInstance().getConfig().defaultPermissions) {
|
||||
account.addPermission(permission);
|
||||
}
|
||||
account.save();
|
||||
@ -154,7 +154,7 @@ public class GCAuthExternalAuthenticator implements ExternalAuthenticator {
|
||||
authResponse.jwt = "";
|
||||
} else {
|
||||
ChangePasswordAccount changePasswordAccount = new Gson().fromJson(requestBody, ChangePasswordAccount.class);
|
||||
if (!GCAuth.getConfigStatic().ACCESS_KEY.isEmpty() && !GCAuth.getConfigStatic().ACCESS_KEY.equals(changePasswordAccount.access_key)){
|
||||
if (!GCAuth.getInstance().getConfig().accessKey.isEmpty() && !GCAuth.getInstance().getConfig().accessKey.equals(changePasswordAccount.access_key)){
|
||||
authResponse.success = false;
|
||||
authResponse.message = "ERROR_ACCESS_KEY"; // ENG = "Error access key was sent with the request"
|
||||
authResponse.jwt = "";
|
||||
|
@ -18,11 +18,10 @@ public class GCAuthenticators {
|
||||
var requestData = authenticationRequest.getPasswordRequest();
|
||||
assert requestData != null; // This should never be null.
|
||||
|
||||
Account account = Authentication.getAccountByOneTimeToken(requestData.account);
|
||||
Account account = Authentication.getAccountByOTP(requestData.account);
|
||||
if(account == null) {
|
||||
Grasscutter.getLogger().info("[GCAuth] Client " + requestData.account + " tried to login with invalid one time token.");
|
||||
response.retcode = -201;
|
||||
response.message = "Token is invalid";
|
||||
response.message = "OTP invalid";
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,15 @@ import me.exzork.gcauth.GCAuth;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public final class Authentication {
|
||||
public static final HashMap<String,String> tokens = new HashMap<>();
|
||||
private static Algorithm key = Algorithm.HMAC256(generateRandomString(32));
|
||||
public static final HashMap<String, String> otps = new HashMap<>();
|
||||
private static Algorithm key = Algorithm.HMAC256(generateRandomNumber(32));
|
||||
public static Algorithm getKey() {
|
||||
return key;
|
||||
}
|
||||
@ -20,44 +24,59 @@ public final class Authentication {
|
||||
public static Account getAccountByUsernameAndPassword(String username, String password) {
|
||||
Account account = DatabaseHelper.getAccountByName(username);
|
||||
if (account == null) return null;
|
||||
if(account.getPassword() != null && !account.getPassword().isEmpty()) {
|
||||
if(!verifyPassword(password, account.getPassword())) account = null;
|
||||
if (account.getPassword() != null && !account.getPassword().isEmpty()) {
|
||||
if (!verifyPassword(password, account.getPassword())) account = null;
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
public static Account getAccountByOneTimeToken(String token) {
|
||||
String username = Authentication.tokens.get(token);
|
||||
public static Account getAccountByOTP(String otp) {
|
||||
String username = Authentication.otps.get(otp);
|
||||
if (username == null) return null;
|
||||
Authentication.tokens.remove(token);
|
||||
Authentication.otps.remove(otp);
|
||||
return DatabaseHelper.getAccountByName(username);
|
||||
}
|
||||
|
||||
public static String generateRandomString(int length){
|
||||
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
public static String generateRandomNumber(int length) {
|
||||
String chars = "0123456789";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i = 0; i < length; i++) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(chars.charAt((int) (Math.random() * chars.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String generateOneTimeToken(Account account) {
|
||||
String token = Authentication.generateRandomString(32);
|
||||
Authentication.tokens.put(token, account.getUsername());
|
||||
public static String generateRandomString(int length) {
|
||||
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(chars.charAt((int) (Math.random() * chars.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String generateOTP(Account account) {
|
||||
String token = Authentication.generateRandomNumber(6);
|
||||
Authentication.otps.put(token, account.getUsername());
|
||||
return token;
|
||||
}
|
||||
|
||||
public static String generateJwt(Account account) {
|
||||
String otp = generateOTP(account);
|
||||
while (otps.get(otp) != null) {
|
||||
otp = generateOTP(account);
|
||||
}
|
||||
watchOTP(otp);
|
||||
return JWT.create()
|
||||
.withClaim("token",generateOneTimeToken(account))
|
||||
.withClaim("username",account.getUsername())
|
||||
.withClaim("uid",account.getPlayerUid())
|
||||
.withClaim("token", otp)
|
||||
.withClaim("username", account.getUsername())
|
||||
.withClaim("uid", account.getPlayerUid())
|
||||
.withExpiresAt(Date.from(Instant.ofEpochSecond(System.currentTimeMillis() / 1000 + GCAuth.getInstance().getConfig().jwtExpiration)))
|
||||
.sign(key);
|
||||
}
|
||||
|
||||
public static String generateHash(String password) {
|
||||
return switch (GCAuth.getConfigStatic().Hash.toLowerCase()) {
|
||||
return switch (GCAuth.getInstance().getConfig().Hash.toLowerCase()) {
|
||||
case "bcrypt" -> new BCryptPasswordEncoder().encode(password);
|
||||
case "scrypt" -> new SCryptPasswordEncoder().encode(password);
|
||||
default -> password;
|
||||
@ -65,10 +84,33 @@ public final class Authentication {
|
||||
}
|
||||
|
||||
private static boolean verifyPassword(String password, String hash) {
|
||||
return switch (GCAuth.getConfigStatic().Hash.toLowerCase()) {
|
||||
return switch (GCAuth.getInstance().getConfig().Hash.toLowerCase()) {
|
||||
case "bcrypt" -> new BCryptPasswordEncoder().matches(password, hash);
|
||||
case "scrypt" -> new SCryptPasswordEncoder().matches(password, hash);
|
||||
default -> password.equals(hash);
|
||||
};
|
||||
}
|
||||
|
||||
public static String getUsernameFromJwt(String jwt) {
|
||||
String username = null;
|
||||
try {
|
||||
username = JWT.require(key)
|
||||
.build()
|
||||
.verify(jwt)
|
||||
.getClaim("username")
|
||||
.asString();
|
||||
}catch (Exception ignored) {}
|
||||
return username;
|
||||
}
|
||||
|
||||
public static void watchOTP(String otp){
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
otps.remove(otp);
|
||||
timer.cancel();
|
||||
}
|
||||
},GCAuth.getInstance().getConfig().otpExpiration * 1000);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user