M Cargo.lock => Cargo.lock +2 -0
@@ 3325,6 3325,7 @@ dependencies = [
"actix-files",
"actix-request-identifier",
"actix-web",
+ "hmac",
"jwt",
"log",
"once_cell",
@@ 3332,6 3333,7 @@ dependencies = [
"reqwest",
"sea-orm",
"serde",
+ "sha2",
"simple_logger",
"starkingdoms_api_entities",
"starkingdoms_api_migration",
M api/Cargo.toml => api/Cargo.toml +3 -1
@@ 28,4 28,6 @@ tera = "1" # Templates
jwt = { version = "0.16", features = ["openssl"] } # Auth
openssl = "0.10" # Auth
-reqwest = "0.11" # Auth>
\ No newline at end of file
+reqwest = "0.11" # Auth
+hmac = "0.12.1" # Auth
+sha2 = "0.10.6" # Auth<
\ No newline at end of file
M api/src/config.rs => api/src/config.rs +1 -0
@@ 28,6 28,7 @@ pub struct StarkingdomsApiConfig {
pub database: StarkingdomsApiConfigDatabase,
pub server: StarkingdomsApiConfigServer,
pub internal_tokens: Vec<String>,
+ pub jwt_signing_secret: String,
pub base: String,
pub game: String,
pub realms: HashMap<String, StarkingdomsApiConfigRealm>
M api/src/routes/callback.rs => api/src/routes/callback.rs +95 -2
@@ 1,11 1,18 @@
use std::collections::BTreeMap;
+use std::time::{SystemTime, UNIX_EPOCH};
use actix_web::{get, HttpResponse};
use actix_web::web::{Data, Query};
-use jwt::{PKeyWithDigest, VerifyWithKey};
+use hmac::digest::KeyInit;
+use hmac::Hmac;
+use jwt::{PKeyWithDigest, SignWithKey, VerifyWithKey};
use log::{debug, error};
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
+use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};
use serde::Deserialize;
+use sha2::Sha256;
+use ulid::Ulid;
+use starkingdoms_api_entities::entity;
use crate::AppState;
use crate::config::CONFIG;
use crate::error::{APIError, APIErrorsResponse};
@@ 61,7 68,93 @@ pub async fn callback(query: Query<CallbackQueryParams>, state: Data<AppState>)
debug!("got authenticated realm native authorization: authenticated as {}:{}", realm, realm_local_id);
- HttpResponse::Ok().finish()
+ // see if a user on this realm already exists
+ let maybe_user = match entity::user_auth_realm::Entity::find()
+ .filter(
+ entity::user_auth_realm::Column::RealmNativeId.eq(realm_local_id.clone())
+ )
+ .filter(
+ entity::user_auth_realm::Column::Realm.eq(realm.clone())
+ ).one(&state.conn).await {
+ Ok(r) => r,
+ Err(e) => {
+ error!("database error: {}", e);
+ return HttpResponse::InternalServerError().json(APIErrorsResponse {
+ errors: vec![
+ APIError {
+ code: "ERR_DB_ERROR".to_string(),
+ message: "Database error".to_string(),
+ path: None,
+ }
+ ],
+ })
+ }
+ };
+
+ if let Some(user) = maybe_user {
+ let key: Hmac<Sha256> = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap();
+ let mut claims = BTreeMap::new();
+ claims.insert("user", user.id.clone());
+ let token_str = claims.sign_with_key(&key).unwrap();
+ let auth_url = format!("{}/?token={}&user={}", CONFIG.game, token_str, user.id);
+ return HttpResponse::Found().append_header(("Location", auth_url)).finish();
+ }
+
+ // create the user
+ let new_user = entity::user::Model {
+ id: format!("user-{}", Ulid::new().to_string()),
+ username: Ulid::new().to_string(),
+ created_on: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64,
+ };
+ let new_user_realm = entity::user_auth_realm::Model {
+ id: format!("{}-{}:{}", new_user.id.clone(), realm.clone(), realm_local_id.clone()),
+ realm: realm.clone(),
+ realm_native_id: realm_local_id.clone(),
+ user: new_user.id.clone(),
+ };
+
+ let key: Hmac<Sha256> = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap();
+ let mut claims = BTreeMap::new();
+ claims.insert("user", new_user.id.clone());
+ let token_str = claims.sign_with_key(&key).unwrap();
+ let auth_url = format!("{}/?token={}&user={}", CONFIG.game, token_str, new_user.id.clone());
+
+ let new_user_active_model = new_user.into_active_model();
+ let new_user_realm_active_model = new_user_realm.into_active_model();
+
+ match new_user_active_model.insert(&state.conn).await {
+ Ok(_) => (),
+ Err(e) => {
+ error!("database error: {}", e);
+ return HttpResponse::InternalServerError().json(APIErrorsResponse {
+ errors: vec![
+ APIError {
+ code: "ERR_DB_ERROR".to_string(),
+ message: "Database error".to_string(),
+ path: None,
+ }
+ ],
+ })
+ }
+ }
+
+ match new_user_realm_active_model.insert(&state.conn).await {
+ Ok(_) => (),
+ Err(e) => {
+ error!("database error: {}", e);
+ return HttpResponse::InternalServerError().json(APIErrorsResponse {
+ errors: vec![
+ APIError {
+ code: "ERR_DB_ERROR".to_string(),
+ message: "Database error".to_string(),
+ path: None,
+ }
+ ],
+ })
+ }
+ }
+
+ HttpResponse::Found().append_header(("Location", auth_url)).finish()
}
fn generic_unauthorized() -> HttpResponse {
M api/templates/select_realm.tera => api/templates/select_realm.tera +1 -1
@@ 7,5 7,5 @@
{% for realm_id, realm in realms %}
<a href="{{ realm.authorize_url }}?return={{ back_to }}">{{ realm_id }}</a>
{% endfor %}
- <p>Selecting an authentication provider will redirect you to our trusted partner <a href="https://e3t.cc">e3team</a>'s website for login. You will be returned to StarKingdoms when finished.</p>
+ <p>Selecting an authentication provider will redirect you to our parent <a href="https://e3t.cc">e3team</a>'s website for login. You will be returned to StarKingdoms when finished.</p>
{% endblock body %}=
\ No newline at end of file
M client/index.html => client/index.html +7 -0
@@ 53,6 53,13 @@
for (let i = 0; i < servers.length; i++) {
load_server(servers[i]);
}
+
+ let query = new URLSearchParams(window.location.search);
+
+ if (query.has("token") && query.has("user")) {
+ window.localStorage.setItem("token", query.get("token"));
+ window.localStorage.setItem("user", query.get("user"));
+ }
</script>
</body>
</html>
M client/src/index.ts => client/src/index.ts +8 -3
@@ 18,7 18,8 @@ export interface GlobalData {
spritesheet: object | null,
context: CanvasRenderingContext2D,
keys: Keys,
- velocity: number
+ velocity: number,
+ can_beam_out: boolean
}
export interface Keys {
@@ 43,12 44,17 @@ export const global: GlobalData = {
left: false,
right: false
},
- velocity: 0
+ velocity: 0,
+ can_beam_out: false
}
async function client_main(server: string, username: string, texture_quality: string) {
logger.info("StarKingdoms client - starting");
+ if (window.localStorage.getItem("token") !== null && window.localStorage.getItem("user") !== null) {
+ global.can_beam_out = true;
+ }
+
logger.info("Loading textures");
let spritesheet_url = `/assets/dist/spritesheet-${texture_quality}.png`;
let spritesheet_data_url = `/assets/dist/spritesheet-${texture_quality}.json`;
@@ 224,7 230,6 @@ if (!(query.has("server") || query.has("username") || query.has("textures"))) {
window.location.href = "/index.html";
}
-
client_main(query.get("server")!, query.get("username")!, query.get("textures")!).then(() => {});
function planet_type_to_tex_id(ty: PlanetType): string {