diff options
author | syeyoung <cyoung06@naver.com> | 2022-11-17 00:02:21 +0900 |
---|---|---|
committer | syeyoung <cyoung06@naver.com> | 2022-11-17 00:12:48 +0900 |
commit | d07321fe03f6316f28d8421b7cd4e93dfdfd6b2e (patch) | |
tree | ae5b92754216c0a660a826ef8d208ce1b0fd2ced /loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth | |
parent | ff17b30c4b585622ca3b8e06eb77794aa21f4285 (diff) | |
download | Skyblock-Dungeons-Guide-d07321fe03f6316f28d8421b7cd4e93dfdfd6b2e.tar.gz Skyblock-Dungeons-Guide-d07321fe03f6316f28d8421b7cd4e93dfdfd6b2e.tar.bz2 Skyblock-Dungeons-Guide-d07321fe03f6316f28d8421b7cd4e93dfdfd6b2e.zip |
- Better Exception handling
Signed-off-by: syeyoung <cyoung06@naver.com>
Diffstat (limited to 'loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth')
4 files changed, 90 insertions, 77 deletions
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java index c3dd747a..c6834a47 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java @@ -2,27 +2,18 @@ package kr.syeyoung.dungeonsguide.launcher.auth; import com.google.common.base.Throwables; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.mojang.authlib.exceptions.AuthenticationException; -import kr.syeyoung.dungeonsguide.launcher.Main; import kr.syeyoung.dungeonsguide.launcher.auth.token.*; import kr.syeyoung.dungeonsguide.launcher.events.AuthChangedEvent; -import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthFailedExeption; -import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredException; +import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.AuthFailedExeption; +import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.AuthenticationUnavailableException; +import kr.syeyoung.dungeonsguide.launcher.exceptions.auth.PrivacyPolicyRequiredException; import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiDisplayer; import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiPrivacyPolicy; -import lombok.Setter; import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.util.concurrent.*; @@ -45,10 +36,14 @@ public class AuthManager { else return null; } + /** + * @throws AuthenticationUnavailableException variations of it. + * @return actual dg token + */ public String getWorkingTokenOrThrow() { if (currentToken instanceof DGAuthToken) return currentToken.getToken(); else if (currentToken instanceof FailedAuthToken) throw new AuthFailedExeption(((FailedAuthToken) currentToken).getException()); - else if (currentToken instanceof NullToken) throw new IllegalStateException("No Token"); + else if (currentToken instanceof NullToken) throw new AuthenticationUnavailableException("Null Token"); else if (currentToken instanceof PrivacyPolicyRequiredToken) throw new PrivacyPolicyRequiredException(); throw new IllegalStateException("weird token: "+currentToken); } @@ -104,8 +99,7 @@ public class AuthManager { GuiDisplayer.INSTANCE.displayGui(new GuiPrivacyPolicy()); throw new PrivacyPolicyRequiredException(); } - } catch (NoSuchAlgorithmException | AuthenticationException | IOException | NoSuchPaddingException | - InvalidKeyException | InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException e) { + } catch (Exception e) { currentToken = new FailedAuthToken(e); // TODO: loader notifications on bottom right? // ChatTransmitter.addToQueue("§eDungeons Guide §7:: §r§cDG auth failed, trying again in ten seconds", true); @@ -118,7 +112,7 @@ public class AuthManager { } - AuthToken acceptPrivacyPolicy() throws IOException { + AuthToken acceptPrivacyPolicy() { if (reauthLock) { while(reauthLock); return currentToken; @@ -129,11 +123,11 @@ public class AuthManager { try { currentToken = DgAuthUtil.acceptNewPrivacyPolicy(currentToken.getToken()); if (currentToken instanceof PrivacyPolicyRequiredToken) throw new PrivacyPolicyRequiredException(); - } catch (IOException e) { + } catch (Exception e) { currentToken = new FailedAuthToken(e); // TODO: loader notifications on bottom right? logger.error("Accepting Privacy Policy failed with message {}, trying again in a 2 seconds", String.valueOf(Throwables.getRootCause(e))); - throw e; + throw new AuthFailedExeption(e); } finally { reauthLock = false; } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java index e5408f0b..3b1adaf2 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java @@ -6,7 +6,6 @@ import com.google.gson.JsonParser; import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -19,9 +18,6 @@ public class AuthUtil { private AuthUtil() {} public static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { - PublicKey publicKey = - KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(bytes)); - publicKey.getEncoded(); - return publicKey; + return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(bytes)); } } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DGResponse.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DGResponse.java index 5873fbda..d69fba44 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DGResponse.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DGResponse.java @@ -7,6 +7,7 @@ import org.json.JSONObject; @Data @AllArgsConstructor public class DGResponse<T> { + private final int responseCode; private final String status; private final T data; private final String errorMessage; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java index 78fc91c9..68a31ccd 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java @@ -1,7 +1,5 @@ package kr.syeyoung.dungeonsguide.launcher.auth; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.minecraft.MinecraftSessionService; @@ -9,9 +7,8 @@ import kr.syeyoung.dungeonsguide.launcher.Main; import kr.syeyoung.dungeonsguide.launcher.auth.token.AuthToken; import kr.syeyoung.dungeonsguide.launcher.auth.token.DGAuthToken; import kr.syeyoung.dungeonsguide.launcher.auth.token.PrivacyPolicyRequiredToken; -import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthServerException; -import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredException; -import kr.syeyoung.dungeonsguide.launcher.exceptions.ResponseParsingException; +import kr.syeyoung.dungeonsguide.launcher.exceptions.http.AuthServerException; +import kr.syeyoung.dungeonsguide.launcher.exceptions.http.ResponseParsingException; import net.minecraft.client.Minecraft; import net.minecraft.util.Session; import org.apache.commons.codec.binary.Base64; @@ -22,10 +19,10 @@ import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; -import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; +import java.net.HttpURLConnection; import java.net.URL; import java.security.*; import java.security.spec.InvalidKeySpecException; @@ -35,8 +32,17 @@ public class DgAuthUtil { private static final SecureRandom random = new SecureRandom(); private DgAuthUtil(){} - - private static <T> DGResponse<T> getResponse(HttpsURLConnection connection, Class<T> data) throws IOException { + /** + * + * @param connection + * @param data + * @return + * @param <T> + * @throws IOException when stuff wrong + * @throws ResponseParsingException failed to parse generic response + * @throws AuthServerException auth server returned FAILURE + */ + private static <T> T getResponse(HttpURLConnection connection, Class<T> data) throws IOException { connection.getResponseCode(); InputStream toRead = connection.getErrorStream(); if (toRead == null) @@ -45,24 +51,30 @@ public class DgAuthUtil { try { JSONObject json = new JSONObject(payload); - return new DGResponse( + DGResponse<T> response = new DGResponse<>( + connection.getResponseCode(), json.getString("status"), - (T) json.get("data"), - json.getString("errorMessage"), - json.getString("qrCode") + json.isNull("data") ? null:(T) json.get("data"), + json.isNull("errorMessage") ?null: json.getString("errorMessage"), + json.isNull("qrCode") ? null: json.getString("qrCode") ); - } catch (Exception e) { - throw new ResponseParsingException(payload, e.getMessage()); + + if (!"SUCCESS".equals(response.getStatus())) { + throw new AuthServerException(response); + } + + return (T) response.getData(); + } catch (Exception e) { + throw new ResponseParsingException(payload, e); } finally { toRead.close(); } - } public static String requestAuth() throws IOException { GameProfile profile = Minecraft.getMinecraft().getSession().getProfile(); - HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/requestAuth").openConnection(); + HttpURLConnection connection = (HttpURLConnection) new URL(Main.DOMAIN + "/auth/v2/requestAuth").openConnection(); connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestMethod("POST"); @@ -71,24 +83,25 @@ public class DgAuthUtil { connection.getOutputStream().write(("{\"uuid\":\""+profile.getId().toString()+"\",\"nickname\":\""+profile.getName()+"\"}").getBytes()); - DGResponse<String> preToken = getResponse(connection, String.class); - if (!"SUCCESS".equals(preToken.getStatus())) { - throw new AuthServerException(preToken); - } - - return preToken.getData(); + return getResponse(connection, String.class); } public static byte[] checkSessionAuthenticityAndReturnEncryptedSecret(String tempToken) throws NoSuchAlgorithmException, AuthenticationException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - JSONObject d = getJwtPayload(tempToken); - byte[] sharedSecret = new byte[16]; - random.nextBytes(sharedSecret); - byte[] publicKey =Base64.decodeBase64(d.getString("publicKey")); - - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.ENCRYPT_MODE, AuthUtil.getPublicKey(publicKey)); - byte[] result = cipher.doFinal(sharedSecret); + byte[] sharedSecret = new byte[16]; + byte[] result; + byte[] publicKey; + try { + JSONObject d = getJwtPayload(tempToken); + random.nextBytes(sharedSecret); + publicKey = Base64.decodeBase64(d.getString("publicKey")); + + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, AuthUtil.getPublicKey(publicKey)); + result = cipher.doFinal(sharedSecret); + } catch (Exception e) { + throw new RuntimeException("Failed to parse publicKey, generate shared secret, then encrypt it.", e); + } String hash = calculateServerHash(sharedSecret, publicKey); @@ -99,8 +112,17 @@ public class DgAuthUtil { return result; } + /** + * + * @param tempToken + * @param encSecret + * @return + * @throws IOException when io error happens + * @throws ResponseParsingException when fails to parse exception + * @throws AuthServerException when auth server throws error + */ public static AuthToken verifyAuth(String tempToken, byte[] encSecret) throws IOException { - HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/authenticate").openConnection(); + HttpURLConnection urlConnection = (HttpURLConnection) new URL(Main.DOMAIN + "/auth/v2/authenticate").openConnection(); urlConnection.setRequestMethod("POST"); urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); urlConnection.setRequestProperty("Content-Type", "application/json"); @@ -109,22 +131,22 @@ public class DgAuthUtil { urlConnection.getOutputStream().write(("{\"jwt\":\""+tempToken+"\",\"sharedSecret\":\""+Base64.encodeBase64URLSafeString(encSecret)+"\"}").getBytes()); - DGResponse<JSONObject> postToken = getResponse(urlConnection, JSONObject.class); - if (!"SUCCESS".equals(postToken.getStatus())) { - throw new AuthServerException(postToken); - } - JSONObject data = postToken.getData(); - if (data.getString("result").equals("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED")) { - return new PrivacyPolicyRequiredToken(data.getString("jwt")); - } else if (data.getString("result").equals("SUCCESSFUL")) { - return new DGAuthToken(data.getString("jwt")); - } else { - throw new AuthServerException(postToken); + JSONObject data = getResponse(urlConnection, JSONObject.class); + try { + if (data.getString("result").equals("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED")) { + return new PrivacyPolicyRequiredToken(data.getString("jwt")); + } else if (data.getString("result").equals("SUCCESSFUL")) { + return new DGAuthToken(data.getString("jwt")); + } else { + throw new UnsupportedOperationException("Unknown auth result"); + } + } catch (Exception e) { + throw new ResponseParsingException(data.toString(), e); } } public static AuthToken acceptNewPrivacyPolicy(String tempToken) throws IOException { - HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/acceptPrivacyPolicy").openConnection(); + HttpURLConnection urlConnection = (HttpURLConnection) new URL(Main.DOMAIN + "/auth/v2/acceptPrivacyPolicy").openConnection(); urlConnection.setRequestMethod("POST"); urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); urlConnection.setRequestProperty("Content-Type", "application/json"); @@ -133,17 +155,17 @@ public class DgAuthUtil { urlConnection.getOutputStream().write(tempToken.getBytes()); - DGResponse<JSONObject> postToken = getResponse(urlConnection, JSONObject.class); - if (!"SUCCESS".equals(postToken.getStatus())) { - throw new AuthServerException(postToken); - } - JSONObject data = postToken.getData(); - if (data.getString("result").equals("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED")) { - return new PrivacyPolicyRequiredToken(data.getString("jwt")); - } else if (data.getString("result").equals("SUCCESSFUL")) { - return new DGAuthToken(data.getString("jwt")); - } else { - throw new AuthServerException(postToken); + JSONObject data = getResponse(urlConnection, JSONObject.class); + try { + if (data.getString("result").equals("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED")) { + return new PrivacyPolicyRequiredToken(data.getString("jwt")); + } else if (data.getString("result").equals("SUCCESSFUL")) { + return new DGAuthToken(data.getString("jwt")); + } else { + throw new UnsupportedOperationException("Unknown auth result"); + } + } catch (Exception e) { + throw new ResponseParsingException(data.toString(), e); } } public static JSONObject getJwtPayload(String jwt) { |