use serde::{Deserialize, Serialize};
use ureq::{Agent, Error};
use serde_json::{to_string, from_slice};
use base64::Engine;
use crate::robocraft::ITokenProvider;
pub struct AuthenticatedTokenProvider {
pub username: String,
pub password: String,
client: Agent,
}
impl AuthenticatedTokenProvider {
pub fn with_email(email: &str, password: &str) -> Result<Self, Error> {
let client = Agent::new();
let payload = AuthenticationEmailPayload {
email_address: email.to_string(),
password: password.to_string(),
};
let response = client.post("https://account.freejamgames.com/api/authenticate/email/web")
.set("Content-Type", "application/json")
.send_string(&to_string(&payload).unwrap())?;
let json_res = response.into_json::<AuthenticationResponseInfo>()?;
Ok(Self {
username: json_res.decode_jwt_data().display_name,
password: password.to_string(),
client,
})
}
pub fn with_username(username: &str, password: &str) -> Result<Self, Error> {
let new_obj = Self {
username: username.to_string(),
password: password.to_string(),
client: Agent::new(),
};
new_obj.do_auth()?;
Ok(new_obj)
}
fn do_auth(&self) -> Result<AuthenticationResponseInfo, Error> {
let payload = AuthenticationUsernamePayload {
username: self.username.clone(),
password: self.password.clone(),
};
let response = self.client.post("https://account.freejamgames.com/api/authenticate/displayname/web")
.set("Content-Type", "application/json")
.send_string(&to_string(&payload).unwrap())?;
let json_res = response.into_json::<AuthenticationResponseInfo>()?;
Ok(json_res)
}
pub fn get_account_info(&self) -> Result<AccountInfo, Error> {
let json_res = self.do_auth()?;
Ok(json_res.decode_jwt_data())
}
}
impl ITokenProvider for AuthenticatedTokenProvider {
fn token(&self) -> Result<String, ()> {
let json_res = self.do_auth().map_err(|_|())?;
Ok(json_res.token)
}
}
#[derive(Deserialize, Serialize, Clone)]
pub(crate) struct AuthenticationEmailPayload {
#[serde(rename = "EmailAddress")]
pub email_address: String,
#[serde(rename = "Password")]
pub password: String,
}
#[derive(Deserialize, Serialize, Clone)]
pub(crate) struct AuthenticationUsernamePayload {
#[serde(rename = "DisplayName")]
pub username: String,
#[serde(rename = "Password")]
pub password: String,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct AuthenticationResponseInfo {
#[serde(rename = "Token")]
pub token: String,
#[serde(rename = "RefreshToken")]
pub refresh_token: String,
#[serde(rename = "RefreshTokenExpiry")]
pub refresh_token_expiry: String,
}
impl AuthenticationResponseInfo {
pub fn decode_jwt_data(&self) -> AccountInfo {
let data = self.token.split(".").collect::<Vec<&str>>()[1];
let data_vec = base64::engine::general_purpose::STANDARD.decode(data).unwrap();
from_slice::<AccountInfo>(&data_vec).unwrap()
}
}
#[derive(Deserialize, Serialize, Clone)]
pub struct AccountInfo {
#[serde(rename = "PublicId")]
pub public_id: String,
#[serde(rename = "DisplayName")]
pub display_name: String,
#[serde(rename = "RobocraftName")]
pub robocraft_name: String,
#[serde(rename = "Confirmed")]
pub confirmed: bool,
#[serde(rename = "SupportCode")]
pub support_code: String,
#[serde(rename = "EmailAddress")]
pub email_address: String,
#[serde(rename = "EmailVerified")]
pub email_verified: bool,
#[serde(rename = "CreatedDate")]
pub created_date: String,
#[serde(rename = "Products")]
pub products: Vec<String>,
#[serde(rename = "Flags")]
pub flags: Vec<String>,
#[serde(rename = "HasPassword")]
pub has_password: bool,
#[serde(rename = "MailingLists")]
pub mailing_lists: Vec<String>,
#[serde(rename = "HasSteam")]
pub has_steam: bool,
#[serde(rename = "iss")]
pub iss: String,
#[serde(rename = "sub")]
pub sub: String,
#[serde(rename = "iat")]
pub iat: u64,
#[serde(rename = "exp")]
pub exp: u64,
}