1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Use steam ID for reliable, persistent CL user auth

This commit is contained in:
NGnius (Graham)
2025-01-26 00:23:07 -05:00
parent 88ef5e7e1c
commit e73002ea72
5 changed files with 139 additions and 50 deletions

View File

@@ -3,6 +3,10 @@ name = "auth"
version = "0.1.0"
edition = "2021"
[features]
steam = ["steamworks"]
default = []
[dependencies]
rocket.workspace = true
libfj.workspace = true
@@ -10,3 +14,5 @@ log.workspace = true
env_logger.workspace = true
uuid = { version = "1.12", features = [ "v4", "fast-rng" ] }
rocket-client-addr = "0.5"
steamworks = { version = "0.11", optional = true }
hex = "0.4"

View File

@@ -1,57 +1,86 @@
use std::sync::RwLock;
use std::collections::HashMap;
use rocket::{post, routes, serde::json::Json, http::Status};
use rocket::{post, routes, serde::json::Json};
#[cfg(feature = "steam")]
static STEAM_SERVER: std::sync::OnceLock<steamworks::Server> = OnceLock::new();
// FIXME don't have global state
static AUTH_MAP: RwLock<Option<HashMap<String, libfj::cardlife::AuthenticationInfo>>> = RwLock::new(None);
#[cfg(feature = "steam")]
fn init_steam() -> steamworks::Server {
let (server, single_client) = steamworks::Server::init(core::net::Ipv4Addr::new(127, 0, 0, 1), 9000, 9001, steamworks::ServerMode::NoAuthentication, "")
.expect("Steam is unavailable");
std::thread::spawn(move || {
loop {
single_client.run_callbacks();
std::thread::sleep(std::time::Duration::from_millis(10));
}
});
server.set_product("920690");
server.log_on_anonymous();
server
}
fn generate_auth(ticket: &str, addr: &str) -> libfj::cardlife::AuthenticationInfo {
let public_guid = uuid::Uuid::new_v4().to_string();
log::warn!("assigning GUID {} to IP address {} (steam ticket {})", public_guid, addr, ticket);
libfj::cardlife::AuthenticationInfo {
public_id: public_guid,
email_address: "nobody@openjamgames.com".to_string(),
display_name: "gaben".to_string(),
purchases: vec![1, 2, 3],
flags: vec![],
/*flags: //vec![
"Dev".to_string(),
"GiveInv".to_string(),
"NoDrop".to_string(),
"DekStruct".to_string(),
"BucketA".to_string(),
"BucketB".to_string(),
"userlogged".to_string(),
],*/
confirmed: true,
token: "1234567890-qwertyuiop-asdfghjkl-zxcvbnm".to_string(),
steam_id: Some("gaben".to_string()),
id: 123456, // FIXME
}
fn get_u64_with_offset(arr: &[u8], start: usize) -> u64 {
let arr_actual: [u8; 8] = [
arr[start],
arr[start+1],
arr[start+2],
arr[start+3],
arr[start+4],
arr[start+5],
arr[start+6],
arr[start+7],
];
u64::from_le_bytes(arr_actual)
}
#[post("/api/auth/steamauthenticate", data = "<body>")]
pub fn steam_auth(addr: &rocket_client_addr::ClientAddr, body: Json<libfj::cardlife::SteamAuthenticationPayload>) -> Json<libfj::cardlife::AuthenticationInfo> {
let addr_str = addr.get_ipv4_string().unwrap_or_else(|| addr.get_ipv6_string());
if AUTH_MAP.read().unwrap().is_none() {
let new_info = generate_auth(&body.steam_ticket, &addr_str);
let mut new_map = HashMap::new();
new_map.insert(addr_str, new_info.clone());
*AUTH_MAP.write().unwrap() = Some(new_map);
Json(new_info)
} else {
if let Some(auth_info) = AUTH_MAP.read().unwrap().as_ref().unwrap().get(&addr_str).map(|x| x.to_owned()) {
Json(auth_info)
} else {
let new_info = generate_auth(&body.steam_ticket, &addr_str);
AUTH_MAP.write().unwrap().as_mut().unwrap().insert(addr_str, new_info.clone());
Json(new_info)
pub fn steam_auth(body: Json<libfj::cardlife::SteamAuthenticationPayload>) -> Result<Json<libfj::cardlife::AuthenticationInfo>, Status> {
log::debug!("steam ticket: {}", body.steam_ticket);
match hex::decode(&body.steam_ticket) {
Ok(ticket) => {
if ticket.len() < 72 {
return Err(Status { code: 400 })
}
let steam_id = get_u64_with_offset(&ticket, 12 /* also at 64 ??? */); // should be 76600000000000000 > number > 76500000000000000
log::debug!("Found steamId {}", steam_id);
#[cfg(feature = "steam")]
{
let steam = STEAM_SERVER.get_or_init(init_steam);
if let Err(e) = steam.begin_authentication_session(steamworks::SteamId::from_raw(steam_id), &ticket) {
log::error!("steam server auth session error: {}", e);
return Err(Status { code: 400 })
}
}
Ok(Json(libfj::cardlife::AuthenticationInfo {
public_id: uuid::Uuid::from_u64_pair(steam_id, steam_id).to_string(),
email_address: "nobody@openjamgames.com".to_string(),
display_name: steam_id.to_string(),
purchases: vec![1, 2, 3],
flags: vec![],
/*flags: //vec![
"Dev".to_string(),
"GiveInv".to_string(),
"NoDrop".to_string(),
"DekStruct".to_string(),
"BucketA".to_string(),
"BucketB".to_string(),
"userlogged".to_string(),
],*/
confirmed: true,
token: uuid::Uuid::from_u64_pair(steam_id, steam_id).to_string(),
steam_id: Some(steam_id.to_string()),
id: (steam_id & (i32::MAX as u64)) as i32,
}))
},
Err(e) => {
log::error!("request with bad steam ticket: {}", e);
Err(Status { code: 400 })
}
}
}
pub fn stage() -> rocket::fairing::AdHoc {
#[cfg(feature = "steam")]
STEAM_SERVER.get_or_init(init_steam);
rocket::fairing::AdHoc::on_ignite("CardLife Steam", |rocket| async {
rocket.mount("/", routes![steam_auth])
})

View File

@@ -3,10 +3,11 @@ use rocket::{post, routes, serde::json::Json};
#[post("/api/auth/temporarygetuserid", data = "<body>")]
pub fn temp_migration(body: Json<libfj::cardlife::TempGetUserIdPayload>) -> Json<libfj::cardlife::TempGetUserIdResponse> {
let steam_id = uuid::Uuid::parse_str(&body.public_id).map(|guid| guid.as_u64_pair().0).unwrap_or(123456);
Json(libfj::cardlife::TempGetUserIdResponse {
public_id: body.public_id.to_owned(),
token: body.token.to_owned(),
user_id: 123456, // FIXME
user_id: (steam_id & (i32::MAX as u64)) as i32,
})
}

1
auth/steam_appid.txt Normal file
View File

@@ -0,0 +1 @@
920690