mirror of
https://github.com/ViaVersion/VIAaaS.git
synced 2024-11-25 12:35:36 +01:00
online mode, not tested, mojang is resetting my account
This commit is contained in:
parent
64b8c6a6cb
commit
23c75082a7
@ -11,6 +11,7 @@ import io.netty.channel.ChannelOption
|
||||
import io.netty.channel.socket.SocketChannel
|
||||
import io.netty.channel.socket.nio.NioSocketChannel
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.slf4j.LoggerFactory
|
||||
import us.myles.ViaVersion.api.PacketWrapper
|
||||
import us.myles.ViaVersion.api.Via
|
||||
import us.myles.ViaVersion.api.data.StoredObject
|
||||
@ -32,7 +33,6 @@ import java.security.SecureRandom
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.logging.Logger
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
@ -55,7 +55,7 @@ class CloudPipeline(userConnection: UserConnection) : ProtocolPipeline(userConne
|
||||
}
|
||||
|
||||
object CloudHeadProtocol : SimpleProtocol() {
|
||||
val logger = Logger.getLogger("CloudHandlerProtocol")
|
||||
val logger = LoggerFactory.getLogger("CloudHandlerProtocol")
|
||||
override fun registerPackets() {
|
||||
this.registerIncoming(State.HANDSHAKE, 0, 0, object : PacketRemapper() {
|
||||
override fun registerMap() {
|
||||
@ -265,7 +265,7 @@ object CloudTailProtocol : SimpleProtocol() {
|
||||
|
||||
var sent = false
|
||||
viaWebServer.listeners[fromUndashed(profile.get("id")!!.asString)]?.forEach {
|
||||
it.ws.send("""{"action": "session_hash_request", "session_hash": "$backHash",
|
||||
it.ws.send("""{"action": "session_hash_request", "username": "${data.frontLoginName!!}", "session_hash": "$backHash",
|
||||
| "client_address": "${wrapper.user().channel!!.remoteAddress()}", "backend_public_key":
|
||||
| "${Base64.getEncoder().encodeToString(data.backPublicKey!!.encoded)}"}""".trimMargin())
|
||||
it.ws.flush()
|
||||
@ -276,25 +276,25 @@ object CloudTailProtocol : SimpleProtocol() {
|
||||
throw IllegalStateException("No connection to browser, connect in /auth.html")
|
||||
} else {
|
||||
viaWebServer.pendingSessionHashes.get(backHash).get(15, TimeUnit.SECONDS)
|
||||
wrapper.user().channel!!.eventLoop().submit {
|
||||
val backCrypto = ByteBufAllocator.DEFAULT.buffer()
|
||||
val backChan = wrapper.user().channel!!.pipeline()
|
||||
.get(CloudSideForwarder::class.java).other!!
|
||||
backChan.eventLoop().submit {
|
||||
val backCryptoAnswer = ByteBufAllocator.DEFAULT.buffer()
|
||||
try {
|
||||
backCrypto.writeByte(1) // Packet id
|
||||
Type.BYTE_ARRAY_PRIMITIVE.write(backCrypto, Cipher.getInstance("RSA").let {
|
||||
backCryptoAnswer.writeByte(1) // Packet id
|
||||
Type.BYTE_ARRAY_PRIMITIVE.write(backCryptoAnswer, Cipher.getInstance("RSA").let {
|
||||
it.init(Cipher.ENCRYPT_MODE, data.backPublicKey)
|
||||
it.doFinal(backKey)
|
||||
})
|
||||
Type.BYTE_ARRAY_PRIMITIVE.write(backCrypto, Cipher.getInstance("RSA").let {
|
||||
Type.BYTE_ARRAY_PRIMITIVE.write(backCryptoAnswer, Cipher.getInstance("RSA").let {
|
||||
it.init(Cipher.ENCRYPT_MODE, data.backPublicKey)
|
||||
it.doFinal(data.backToken)
|
||||
})
|
||||
val backChan = wrapper.user().channel!!.pipeline()
|
||||
.get(CloudSideForwarder::class.java).other!!
|
||||
backChan.writeAndFlush(backCrypto.retain())
|
||||
backChan.writeAndFlush(backCryptoAnswer.retain())
|
||||
backChan.pipeline().get(CloudEncryptor::class.java).cipher = backAesEn
|
||||
backChan.pipeline().get(CloudDecryptor::class.java).cipher = backAesDe
|
||||
} finally {
|
||||
backCrypto.release()
|
||||
backCryptoAnswer.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,10 +165,10 @@ class WebLogin : WebState {
|
||||
webClient.ws.send("""{"action": "minecraft_id_result", "success": true,
|
||||
| "username": "$mcIdUser", "uuid": "$uuid", "token": "$token"}""".trimMargin())
|
||||
|
||||
webLogger.info("${webClient.ws.call.request.origin} generated a token for account $mcIdUser $uuid")
|
||||
webLogger.info("Generated a token for account $mcIdUser $uuid")
|
||||
} else {
|
||||
webClient.ws.send("""{"action": "minecraft_id_result", "success": false}""")
|
||||
webLogger.info("${webClient.ws.call.request.origin} failed to generated a token for account $username")
|
||||
webLogger.info("Failed to generated a token for account $username")
|
||||
}
|
||||
}
|
||||
"listen_login_requests" -> {
|
||||
@ -180,10 +180,10 @@ class WebLogin : WebState {
|
||||
webClient.server.listeners.computeIfAbsent(user) { Collections.newSetFromMap(ConcurrentHashMap()) }
|
||||
.add(webClient)
|
||||
|
||||
webLogger.info("${webClient.ws.call.request.origin} is listening for logins for $user")
|
||||
webLogger.info("Listening for logins for $user")
|
||||
} else {
|
||||
webClient.ws.send("""{"action": "listen_login_requests_result", "token": "$token", "success": false}""")
|
||||
webLogger.info("${webClient.ws.call.request.origin} failed token for $user")
|
||||
webLogger.info("Failed token for $user")
|
||||
}
|
||||
}
|
||||
"session_hash_response" -> {
|
||||
|
@ -4,14 +4,34 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>VIAaaS Authenticator</title>
|
||||
<style>
|
||||
body {font-family: sans-serif}
|
||||
</style>
|
||||
<style>body {font-family: sans-serif}</style>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.1/uuid.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
WIP TODO - authenticate to backend server via browser, proxy the Mojang Login API, does this work on 1.7 and other versions?
|
||||
TODO - what to do with invalid session error because it uses the same account?
|
||||
<p>DO NOT TYPE YOUR CREDENTIALS IF YOU DON'T TRUST THIS VIAAAS INSTANCE OR THE CORS PROXY!</p>
|
||||
<p>Mojang API calls in browser are called through a CORS Proxy. See https://github.com/Zibri/cloudflare-cors-anywhere
|
||||
for setting up one.</p>
|
||||
<label for="cors-proxy">CORS Proxy URL:</label>
|
||||
<br>
|
||||
<input type="url" id="cors-proxy" name="cors-proxy" value="" onchange="localStorage.setItem('cors-proxy', this.value);">
|
||||
<script></script>
|
||||
<hr>
|
||||
<p>Browser Minecraft accounts:</p>
|
||||
<p><span id="add-account">
|
||||
<label for="email">Email/Username (legacy):</label>
|
||||
<br>
|
||||
<input type="text" id="email" name="email" value="">
|
||||
<br>
|
||||
<label for="password">Password:</label><br>
|
||||
<input type="password" id="password" name="password" value="">
|
||||
<br><br>
|
||||
<input type="button" value="Login into Minecraft" onclick="loginMc()">
|
||||
</span></p>
|
||||
<span id="accounts"></span>
|
||||
<hr>
|
||||
<p>WebSocket connection status: <span id="connection_status">?</span></p>
|
||||
<p>DO NOT TYPE YOUR CREDENTIALS IF YOU DON'T TRUST THIS VIAAAS INSTANCE!</p>
|
||||
<p><span id="content"></span></p>
|
||||
<script>
|
||||
let urlParams = new URLSearchParams();
|
||||
@ -22,17 +42,155 @@ WIP TODO - authenticate to backend server via browser, proxy the Mojang Login AP
|
||||
alert("Couldn't authenticate with Minecraft.ID: " + urlParams.get("mcauth_msg"));
|
||||
}
|
||||
|
||||
var wsUrl = "wss://" + window.location.host + "/ws";
|
||||
|
||||
var socket = null;
|
||||
var connectionStatus = document.getElementById("connection_status");
|
||||
var content = document.getElementById("content");
|
||||
var acounts = document.getElementById("accounts");
|
||||
|
||||
$("#cors-proxy").val(localStorage.getItem("cors-proxy"));
|
||||
|
||||
function loginMc() {
|
||||
var clientToken = uuid.v4();
|
||||
$.ajax({type: "post",
|
||||
url: localStorage.getItem("cors-proxy") + "https://authserver.mojang.com/authenticate",
|
||||
data: JSON.stringify({
|
||||
agent: {name: "Minecraft", version: 1},
|
||||
username: $("#email").val(),
|
||||
password: $("#password").val(),
|
||||
clientToken: clientToken,
|
||||
}),
|
||||
contentType: "application/json",
|
||||
dataType: "json"
|
||||
}).done((data) => {
|
||||
storeMcAccount(data.accessToken, data.clientToken, data.selectedProfile.name, data.selectedProfile.id);
|
||||
}).fail(() => alert("Failed to login"));
|
||||
$("#email").val("");
|
||||
$("#password").val("");
|
||||
}
|
||||
|
||||
function storeMcAccount(accessToken, clientToken, name, id) {
|
||||
let accounts = JSON.parse(localStorage.getItem("mc_accounts")) || [];
|
||||
accounts.push({accessToken: accessToken, clientToken: clientToken, name: name, id: id});
|
||||
localStorage.setItem("mc_accounts", JSON.stringify(accounts));
|
||||
refreshAccountList();
|
||||
}
|
||||
|
||||
function removeMcAccount(id) {
|
||||
let accounts = JSON.parse(localStorage.getItem("mc_accounts")) || [];
|
||||
accounts = accounts.filter(it => it.id != id);
|
||||
localStorage.setItem("mc_accounts", JSON.stringify(accounts));
|
||||
refreshAccountList();
|
||||
}
|
||||
|
||||
function getMcAccounts() {
|
||||
return JSON.parse(localStorage.getItem("mc_accounts")) || [];
|
||||
}
|
||||
|
||||
function logout(id) {
|
||||
getMcAccounts().filter(it => it.id == id).forEach(it => {
|
||||
$.ajax({type: "post",
|
||||
url: localStorage.getItem("cors-proxy") + "https://authserver.mojang.com/invalidate",
|
||||
data: JSON.stringify({
|
||||
accessToken: it.accessToken,
|
||||
clientToken: it.clientToken
|
||||
}),
|
||||
contentType: "application/json",
|
||||
dataType: "json"
|
||||
}).done((data) => {
|
||||
removeMcAccount(id);
|
||||
}).fail(() => {
|
||||
if (confirm("failed to invalidate token! remove account?")) {
|
||||
removeMcAccount(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addMcAccountToList(id, name) {
|
||||
let p = document.createElement("p");
|
||||
let n = document.createElement("span");
|
||||
n.innerText = id + " (" + name + ") ";
|
||||
let remove = document.createElement("a");
|
||||
remove.innerText = "Remove";
|
||||
remove.href = "javascript:";
|
||||
remove.onclick = () => {
|
||||
logout(id);
|
||||
};
|
||||
p.appendChild(n);
|
||||
p.append(remove);
|
||||
accounts.appendChild(p);
|
||||
}
|
||||
|
||||
function refreshAccountList() {
|
||||
accounts.innerHTML = "";
|
||||
getMcAccounts().forEach(it => addMcAccountToList(it.id, it.name));
|
||||
}
|
||||
|
||||
function refreshAccountsIfNeeded() {
|
||||
getMcAccounts().forEach(it => {
|
||||
$.ajax({type: "post",
|
||||
url: localStorage.getItem("cors-proxy") + "https://authserver.mojang.com/validate",
|
||||
data: JSON.stringify({
|
||||
accessToken: it.accessToken,
|
||||
clientToken: it.clientToken
|
||||
}),
|
||||
contentType: "application/json",
|
||||
dataType: "json"
|
||||
}).fail(() => {
|
||||
// Needs refresh
|
||||
$.ajax({type: "post",
|
||||
url: localStorage.getItem("cors-proxy") + "https://authserver.mojang.com/refresh",
|
||||
data: JSON.stringify({
|
||||
accessToken: it.accessToken,
|
||||
clientToken: it.clientToken
|
||||
}),
|
||||
contentType: "application/json",
|
||||
dataType: "json"
|
||||
}).done((data) => {
|
||||
removeMcAccount(data.selectedProfile.id);
|
||||
storeMcAccount(data.accessToken, data.clientToken, data.selectedProfile.name, data.selectedProfile.id);
|
||||
}).fail(() => {
|
||||
if (confirm("failed to refresh token! remove account?")) {
|
||||
removeMcAccount(it.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
refreshAccountList();
|
||||
|
||||
refreshAccountsIfNeeded();
|
||||
|
||||
function listen(token) {
|
||||
socket.send('{"action": "listen_login_requests", "token": "' + token + '"}');
|
||||
socket.send(JSON.stringify({"action": "listen_login_requests", "token": token}));
|
||||
}
|
||||
|
||||
function saveToken(token) {
|
||||
let hTokens = JSON.parse(localStorage.getItem("tokens")) || {};
|
||||
let tokens = hTokens[wsUrl] || [];
|
||||
tokens.push(token);
|
||||
hTokens[wsUrl] = tokens;
|
||||
localStorage.setItem("tokens", JSON.stringify(hTokens));
|
||||
}
|
||||
|
||||
function removeToken(token) {
|
||||
let hTokens = JSON.parse(localStorage.getItem("tokens")) || {};
|
||||
let tokens = hTokens[wsUrl] || [];
|
||||
tokens = tokens.filter(it => it != token);
|
||||
hTokens[wsUrl] = tokens;
|
||||
localStorage.setItem("tokens", JSON.stringify(hTokens));
|
||||
}
|
||||
|
||||
function getTokens() {
|
||||
return (JSON.parse(localStorage.getItem("tokens")) || {})[wsUrl] || [];
|
||||
}
|
||||
|
||||
function connect() {
|
||||
connectionStatus.innerText = "connecting...";
|
||||
socket = new WebSocket("wss://" + window.location.host + "/ws");
|
||||
socket = new WebSocket(wsUrl);
|
||||
|
||||
socket.onerror = e => {
|
||||
console.log(e);
|
||||
@ -44,7 +202,7 @@ WIP TODO - authenticate to backend server via browser, proxy the Mojang Login AP
|
||||
connectionStatus.innerText = "connected";
|
||||
content.innerHTML = "";
|
||||
|
||||
(localStorage.getItem("tokens") || "").split(",").filter(it => it != "").forEach(listen);
|
||||
getTokens().forEach(listen);
|
||||
};
|
||||
|
||||
socket.onclose = evt => {
|
||||
@ -58,31 +216,36 @@ WIP TODO - authenticate to backend server via browser, proxy the Mojang Login AP
|
||||
let parsed = JSON.parse(event.data);
|
||||
if (parsed.action == "ad_minecraft_id_login") {
|
||||
if (username != null && mcauth_code != null) {
|
||||
let p = document.createElement("p");
|
||||
let add = document.createElement("a");
|
||||
add.innerText = "Add account " + username;
|
||||
p.appendChild(add);
|
||||
add.innerText = "Listen to " + username;
|
||||
add.href = "javascript:";
|
||||
add.onclick = () => {
|
||||
socket.send('{"action": "minecraft_id_login", "username": "' + username + '", "code": "' + mcauth_code + '"}');
|
||||
socket.send(JSON.stringify({
|
||||
"action": "minecraft_id_login",
|
||||
"username": username,
|
||||
"code": mcauth_code}));
|
||||
};
|
||||
content.appendChild(add);
|
||||
content.appendChild(p);
|
||||
}
|
||||
let p = document.createElement("p");
|
||||
let link = document.createElement("a");
|
||||
link.innerText = "Add account with Minecraft.ID";
|
||||
p.appendChild(link);
|
||||
link.innerText = "Listen to username in VIAaaS instance";
|
||||
link.href = "javascript:";
|
||||
link.onclick = () => {
|
||||
let user = prompt("Username: ", "");
|
||||
let callbackUrl = new URL(location.origin + location.pathname + "?username=" + encodeURIComponent(user));
|
||||
location = "https://api.minecraft.id/gateway/start/" + encodeURIComponent(user) + "?callback=" + encodeURIComponent(callbackUrl);
|
||||
};
|
||||
content.appendChild(link);
|
||||
content.appendChild(p);
|
||||
} else if (parsed.action == "minecraft_id_result") {
|
||||
if (!parsed.success) {
|
||||
alert("Server couldn't verify account via Minecraft.ID");
|
||||
} else {
|
||||
listen(parsed.token);
|
||||
let tokens = (localStorage.getItem("tokens") || "").split(",");
|
||||
tokens.push(parsed.token);
|
||||
localStorage.setItem("tokens", tokens.join(","));
|
||||
saveToken(parsed.token);
|
||||
}
|
||||
} else if (parsed.action == "listen_login_requests_result") {
|
||||
if (parsed.success) {
|
||||
@ -90,13 +253,30 @@ WIP TODO - authenticate to backend server via browser, proxy the Mojang Login AP
|
||||
msg.innerText = "Listening to logins with username: " + parsed.username;
|
||||
content.appendChild(msg);
|
||||
} else {
|
||||
let tokens = (localStorage.getItem("tokens") || "").split(",");
|
||||
tokens = tokens.filter(it => it != parsed.token);
|
||||
localStorage.setItem("tokens", tokens.join(","));
|
||||
removeToken(parsed.token);
|
||||
}
|
||||
} else if (parsed.action == "session_hash_request") {
|
||||
alert("TODO!"); // todo
|
||||
socket.send('{"action": "session_hash_response", "session_hash": "' + parsed.session_hash + '"}');
|
||||
if (confirm("auth request sent from server, info: " + event.data + ". Should we authenticate?")) {
|
||||
let accounts = getMcAccounts().filter(it => it.user.toLowerCase() == parsed.username.toLowerCase());
|
||||
accounts.forEach(it => {
|
||||
$.ajax({type: "post",
|
||||
url: localStorage.getItem("cors-proxy") + "https://sessionserver.mojang.com/session/minecraft/join",
|
||||
data: JSON.stringify({
|
||||
accessToken: it.accessToken,
|
||||
selectedProfile: it.id,
|
||||
serverId: parsed.session_hash
|
||||
}),
|
||||
contentType: "application/json",
|
||||
dataType: "json"
|
||||
}).done((data) => {
|
||||
socket.send(JSON.stringify({"action": "session_hash_response", "session_hash": parsed.session_hash}));
|
||||
}).fail((e) => {
|
||||
console.log(e);
|
||||
alert("Failed to authenticate to Minecraft backend server!");
|
||||
});
|
||||
});
|
||||
if (accounts.length == 0) alert("Couldn't find " + parsed.username + " account in browser");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user