mirror of
https://github.com/ViaVersion/VIAaaS.git
synced 2024-11-25 12:35:36 +01:00
some websockets and minecraft.id wip code
This commit is contained in:
parent
4055d17858
commit
f207ac2365
@ -34,6 +34,8 @@ dependencies {
|
||||
|
||||
implementation("io.ktor:ktor-network-tls-certificates:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-gson:$ktorVersion")
|
||||
implementation("io.ktor:ktor-websockets:$ktorVersion")
|
||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
testImplementation("io.ktor:ktor-server-test-host:$ktorVersion")
|
||||
|
@ -2,6 +2,10 @@ package com.github.creeper123123321.viaaas
|
||||
|
||||
import de.gerrygames.viarewind.api.ViaRewindConfigImpl
|
||||
import io.ktor.application.*
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.features.*
|
||||
import io.ktor.client.features.json.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.network.tls.certificates.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
@ -18,6 +22,15 @@ import us.myles.ViaVersion.util.Config
|
||||
import java.io.File
|
||||
import java.net.InetAddress
|
||||
|
||||
val httpClient = HttpClient {
|
||||
defaultRequest {
|
||||
header("User-Agent", "VIAaaS")
|
||||
}
|
||||
install(JsonFeature) {
|
||||
serializer = GsonSerializer()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
File("config/https.jks").apply {
|
||||
parentFile.mkdirs()
|
||||
@ -61,6 +74,7 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
ktorServer.stop(1000, 1000)
|
||||
httpClient.close()
|
||||
listOf<Future<*>>(future.channel().close(), boss.shutdownGracefully(), worker.shutdownGracefully())
|
||||
.forEach { it.sync() }
|
||||
|
||||
|
@ -1,14 +1,23 @@
|
||||
package com.github.creeper123123321.viaaas
|
||||
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import io.ktor.application.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.features.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.http.cio.websocket.*
|
||||
import io.ktor.http.content.*
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.websocket.*
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import java.net.URLEncoder
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.set
|
||||
|
||||
// todo https://minecraft.id/documentation
|
||||
|
||||
@ -24,13 +33,16 @@ class ViaWebApp {
|
||||
|
||||
routing {
|
||||
webSocket("/ws") {
|
||||
server.connected(this)
|
||||
try {
|
||||
server.connected(this)
|
||||
incoming.consumeEach { frame ->
|
||||
if (frame is Frame.Text) {
|
||||
server.onMessage(this, frame.readText())
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
server.onException(this, e)
|
||||
this.close(CloseReason(CloseReason.Codes.INTERNAL_ERROR, e.toString()))
|
||||
} finally {
|
||||
server.disconnected(this)
|
||||
}
|
||||
@ -46,9 +58,14 @@ class ViaWebApp {
|
||||
|
||||
class WebDashboardServer {
|
||||
val clients = ConcurrentHashMap<WebSocketSession, WebClient>()
|
||||
val loginTokens = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.DAYS)
|
||||
.build<UUID, String>()
|
||||
val usernames = ConcurrentHashMap<String, WebClient>()
|
||||
|
||||
suspend fun connected(ws: WebSocketSession) {
|
||||
val loginState = WebLogin()
|
||||
val client = WebClient(ws, loginState)
|
||||
val client = WebClient(this, ws, loginState)
|
||||
clients[ws] = client
|
||||
loginState.start(client)
|
||||
}
|
||||
@ -63,29 +80,80 @@ class WebDashboardServer {
|
||||
client.state.disconnected(client)
|
||||
clients.remove(ws)
|
||||
}
|
||||
|
||||
suspend fun onException(ws: WebSocketSession, exception: java.lang.Exception) {
|
||||
val client = clients[ws]!!
|
||||
client.state.onException(client, exception)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class WebClient(val ws: WebSocketSession, val state: WebState) {
|
||||
}
|
||||
data class WebClient(val server: WebDashboardServer,
|
||||
val ws: WebSocketSession,
|
||||
val state: WebState,
|
||||
val listenedUsernames: MutableSet<String> = mutableSetOf())
|
||||
|
||||
interface WebState {
|
||||
suspend fun start(webClient: WebClient)
|
||||
suspend fun onMessage(webClient: WebClient, msg: String)
|
||||
suspend fun disconnected(webClient: WebClient)
|
||||
suspend fun onException(webClient: WebClient, exception: java.lang.Exception)
|
||||
}
|
||||
|
||||
class WebLogin : WebState {
|
||||
override suspend fun start(webClient: WebClient) {
|
||||
webClient.ws.send("test")
|
||||
webClient.ws.send("""{"action": "ad_minecraft_id_login"}""")
|
||||
webClient.ws.flush()
|
||||
}
|
||||
|
||||
override suspend fun onMessage(webClient: WebClient, msg: String) {
|
||||
TODO("Not yet implemented")
|
||||
val obj = Gson().fromJson<JsonObject>(msg, JsonObject::class.java)
|
||||
|
||||
when (obj.getAsJsonPrimitive("action").asString) {
|
||||
"minecraft_id_login" -> {
|
||||
val username = obj.getAsJsonPrimitive("username").asString
|
||||
val code = obj.getAsJsonPrimitive("code").asString
|
||||
|
||||
val check = httpClient.submitForm<JsonObject>(
|
||||
"https://api.minecraft.id/gateway/verify/${URLEncoder.encode(username, Charsets.UTF_8)}",
|
||||
formParameters = parametersOf("code", code),
|
||||
encodeInQuery = false) {
|
||||
}
|
||||
|
||||
if (check.getAsJsonPrimitive("valid").asBoolean) {
|
||||
val token = UUID.randomUUID()
|
||||
webClient.server.loginTokens.put(token, username)
|
||||
webClient.ws.send("""{"action": "minecraft_id_result", "success": true,
|
||||
| "username": "$username", "token": "$token"}""".trimMargin())
|
||||
} else {
|
||||
webClient.ws.send("""{"action": "minecraft_id_result", "success": false}""")
|
||||
}
|
||||
}
|
||||
"listen_login_requests" -> {
|
||||
val token = UUID.fromString(obj.getAsJsonPrimitive("token").asString)
|
||||
val user = webClient.server.loginTokens.get(token) { "" }
|
||||
if (user != "") {
|
||||
webClient.ws.send("""{"action": "listen_login_requests_result", "token": "$token", "success": true, "username": "$user"}""")
|
||||
webClient.listenedUsernames.add(user)
|
||||
webClient.server.usernames[user] = webClient
|
||||
} else {
|
||||
webClient.ws.send("""{"action": "listen_login_requests_result", "token": "$token", "success": false}""")
|
||||
}
|
||||
}
|
||||
"session_hash_response" -> {
|
||||
val token = UUID.fromString(obj.getAsJsonPrimitive("token").asString)
|
||||
val user = webClient.server.loginTokens.get(token) { null }!!
|
||||
}
|
||||
else -> throw IllegalStateException("invalid action!")
|
||||
}
|
||||
|
||||
webClient.ws.flush()
|
||||
}
|
||||
|
||||
override suspend fun disconnected(webClient: WebClient) {
|
||||
TODO("Not yet implemented")
|
||||
webClient.listenedUsernames.forEach { webClient.server.usernames.remove(it, webClient) }
|
||||
}
|
||||
|
||||
override suspend fun onException(webClient: WebClient, exception: java.lang.Exception) {
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
<!doctype html>
|
||||
<!-- todo insert here online mode auth code with wss -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@ -10,33 +9,89 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
WIP todo insert here online mode auth code with wss, store login things in the browser, make viaaas ask the browser to contact session servers
|
||||
<p>WebSocket connection status: <span id="connection_status">?</span></p>
|
||||
<p>DO NOT TYPE YOUR CREDENTIALS IF YOU DON'T TRUST THE CONNECTION TO THIS VIAAAS INSTANCE!</p>
|
||||
<p>Minecraft Login:</p>
|
||||
<p>Minecraft Password:</p>
|
||||
<p><span id="content"></span></p>
|
||||
<script>
|
||||
let urlParams = new URLSearchParams();
|
||||
window.location.search.split("?").map(it => new URLSearchParams(it).forEach((a, b) => urlParams.append(b, a)));
|
||||
history.replaceState(null, null, "auth.html");
|
||||
var username = urlParams.get("username");
|
||||
var mcauth_code = urlParams.get("mcauth_code");
|
||||
if (urlParams.get("mcauth_success") == "false") {
|
||||
alert("Couldn't authenticate with Minecraft.ID: " + urlParams.get("mcauth_msg"));
|
||||
}
|
||||
|
||||
var socket = null;
|
||||
var connectionStatus = document.getElementById("connection_status");
|
||||
var content = document.getElementById("content");
|
||||
|
||||
function listen(token) {
|
||||
socket.send('{"action": "listen_login_requests", "token": "' + token + '"}');
|
||||
}
|
||||
|
||||
function connect() {
|
||||
connectionStatus.innerText = "connecting...";
|
||||
socket = new WebSocket("wss://" + window.location.host + "/ws");
|
||||
|
||||
socket.onerror = () => {
|
||||
socket.onerror = e => {
|
||||
console.log(e);
|
||||
connectionStatus.innerText = "socket error";
|
||||
content.innerHTML = "";
|
||||
};
|
||||
|
||||
socket.onopen = () => {
|
||||
connectionStatus.innerText = "connected";
|
||||
content.innerHTML = "";
|
||||
};
|
||||
|
||||
socket.onclose = evt => {
|
||||
connectionStatus.innerText = "disconnected with close code " + evt.code + " and reason: " + evt.reason;
|
||||
content.innerHTML = "";
|
||||
setTimeout(connect, 5000);
|
||||
};
|
||||
|
||||
socket.onmessage = event => {
|
||||
console.log(event.data.toString());
|
||||
let parsed = JSON.parse(event.data);
|
||||
if (parsed.action == "ad_minecraft_id_login") {
|
||||
let link = document.createElement("a");
|
||||
link.innerText = "Prove your ownership of your accounts to this VIAaaS instance with Minecraft.ID";
|
||||
link.href = "javascript:";
|
||||
link.onclick = () => {
|
||||
if (username != null && mcauth_code != null) {
|
||||
socket.send('{"action": "minecraft_id_login", "username": "' + username + '", "code": "' + mcauth_code + '"}');
|
||||
username = null; mcauth_code = null;
|
||||
} else {
|
||||
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);
|
||||
|
||||
(localStorage.getItem("tokens") || "").split(",").filter(it => it != "").forEach(listen);
|
||||
} 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(","));
|
||||
}
|
||||
} else if (parsed.action == "listen_login_requests_result") {
|
||||
if (parsed.success) {
|
||||
let msg = document.createElement("p");
|
||||
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(","));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user