Complete private authentication, server integrations and activity reporting
Add Fabric/Forge version builds and Paper integration, preserve permanent player identities across renames, harden session authorization, and surface privacy-conscious launcher/server activity in the panel.
This commit is contained in:
commit
a1f19e86c6
74 files changed
+2108
-95
No files matched your search
@@ -42,8 +42,8 @@ async fn with_player(t: &TestApp) -> String {
|
||||
admin
|
||||
}
|
||||
|
||||
fn steve_id() -> String {
|
||||
scopenet_shared::offline_uuid("Steve").replace('-', "")
|
||||
async fn steve_id(t: &TestApp) -> String {
|
||||
t.uuid("Steve").await.replace('-', "")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -82,7 +82,7 @@ async fn full_authlib_flow() {
|
||||
.await;
|
||||
assert_eq!(s, StatusCode::OK, "{auth}");
|
||||
assert_eq!(auth["clientToken"], "ct1");
|
||||
assert_eq!(auth["selectedProfile"]["id"], steve_id());
|
||||
assert_eq!(auth["selectedProfile"]["id"], steve_id(&t).await);
|
||||
assert_eq!(auth["selectedProfile"]["name"], "Steve");
|
||||
assert_eq!(auth["availableProfiles"].as_array().unwrap().len(), 1);
|
||||
assert!(auth["user"]["id"].is_string());
|
||||
@@ -140,7 +140,7 @@ async fn full_authlib_flow() {
|
||||
"POST",
|
||||
&format!("{Y}/sessionserver/session/minecraft/join"),
|
||||
None,
|
||||
Some(json!({"accessToken": token, "selectedProfile": steve_id(), "serverId": "-4b1d2f"})),
|
||||
Some(json!({"accessToken": token, "selectedProfile": steve_id(&t).await, "serverId": "-4b1d2f"})),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(s, StatusCode::NO_CONTENT);
|
||||
@@ -157,7 +157,7 @@ async fn full_authlib_flow() {
|
||||
let (s, joined) =
|
||||
t.call("GET", &format!("{Y}/sessionserver/session/minecraft/hasJoined?username=Steve&serverId=-4b1d2f"), None, None).await;
|
||||
assert_eq!(s, StatusCode::OK, "{joined}");
|
||||
assert_eq!(joined["id"], steve_id());
|
||||
assert_eq!(joined["id"], steve_id(&t).await);
|
||||
let textures = joined["properties"].as_array().unwrap().iter().find(|p| p["name"] == "textures").unwrap();
|
||||
let value = textures["value"].as_str().unwrap();
|
||||
assert!(
|
||||
@@ -176,13 +176,13 @@ async fn full_authlib_flow() {
|
||||
assert_eq!(s, StatusCode::NO_CONTENT, "wrong server id");
|
||||
|
||||
// Profile lookups.
|
||||
let (_, unsigned) = t.call("GET", &format!("{Y}/sessionserver/session/minecraft/profile/{}", steve_id()), None, None).await;
|
||||
let (_, unsigned) = t.call("GET", &format!("{Y}/sessionserver/session/minecraft/profile/{}", steve_id(&t).await), None, None).await;
|
||||
assert!(unsigned["properties"][0].get("signature").is_none());
|
||||
let (_, signed) =
|
||||
t.call("GET", &format!("{Y}/sessionserver/session/minecraft/profile/{}?unsigned=false", steve_id()), None, None).await;
|
||||
t.call("GET", &format!("{Y}/sessionserver/session/minecraft/profile/{}?unsigned=false", steve_id(&t).await), None, None).await;
|
||||
assert!(signed["properties"][0]["signature"].is_string());
|
||||
let (_, found) = t.call("POST", &format!("{Y}/api/profiles/minecraft"), None, Some(json!(["steve", "nobody"]))).await;
|
||||
assert_eq!(found, json!([{"id": steve_id(), "name": "Steve"}]));
|
||||
assert_eq!(found, json!([{"id": steve_id(&t).await, "name": "Steve"}]));
|
||||
|
||||
// Refresh rotates the token.
|
||||
let (s, refreshed) =
|
||||
@@ -209,7 +209,7 @@ async fn launcher_login_returns_game_session() {
|
||||
let (s, v) = t.call("POST", "/api/v1/auth/login", None, Some(json!({"username": "Steve", "password": "password123"}))).await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
let token = v["yggdrasil"]["access_token"].as_str().unwrap();
|
||||
assert_eq!(v["user"]["uuid"], scopenet_shared::offline_uuid("Steve"));
|
||||
assert_eq!(v["user"]["uuid"], t.uuid("Steve").await);
|
||||
let (s, _) = t.call("POST", &format!("{Y}/authserver/validate"), None, Some(json!({"accessToken": token}))).await;
|
||||
assert_eq!(s, StatusCode::NO_CONTENT);
|
||||
}
|
||||
@@ -255,7 +255,7 @@ async fn chat_certificates_are_signed_like_mojang() {
|
||||
// Rebuild the V2 payload the way Minecraft does and check the signature.
|
||||
let body: String = pem.lines().filter(|l| !l.starts_with("-----")).map(|l| l.trim()).collect();
|
||||
let der = b64(&body);
|
||||
let uuid = uuid::Uuid::parse_str(&scopenet_shared::offline_uuid("Steve")).unwrap();
|
||||
let uuid = uuid::Uuid::parse_str(&t.uuid("Steve").await).unwrap();
|
||||
let mut payload = uuid.as_bytes().to_vec();
|
||||
payload.extend_from_slice(&expires.timestamp_millis().to_be_bytes());
|
||||
payload.extend_from_slice(&der);
|
||||
@@ -278,7 +278,7 @@ async fn avatars_and_skin_validation() {
|
||||
let t = setup().await;
|
||||
with_player(&t).await;
|
||||
let panel = t.login("Steve", "password123").await;
|
||||
let (s, _) = t.call("GET", &format!("/api/v1/avatar/{}", steve_id()), None, None).await;
|
||||
let (s, _) = t.call("GET", &format!("/api/v1/avatar/{}", steve_id(&t).await), None, None).await;
|
||||
assert_eq!(s, StatusCode::NOT_FOUND, "no skin yet");
|
||||
|
||||
let (ct, body) = multipart(&[], ("bad.png", b"GIF89a not a png"));
|
||||
@@ -297,7 +297,7 @@ async fn avatars_and_skin_validation() {
|
||||
.body(Body::from(body))
|
||||
.unwrap();
|
||||
assert_eq!(t.send(req).await.0, StatusCode::OK);
|
||||
for id in [steve_id(), "Steve".to_string()] {
|
||||
for id in [steve_id(&t).await, "Steve".to_string()] {
|
||||
let resp =
|
||||
t.router.clone().oneshot(Request::get(format!("/api/v1/avatar/{id}?size=32")).body(Body::empty()).unwrap()).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
||||
Reference in new issue
Block a user