OneLauncher-Cracked/packages/oneclient_app/src/hooks/queries/auth.rs
Julian Chang 29d1f1c9d2
Bugs
Fixed #492
Fixed #510
Fixed #506
Fixed #568
Fixed #582
Fixed #578
Fixed #567
Fixed #588
Fixed #585
Fixed #571
Fixed #587
Fixed #584
2026-07-15 13:41:43 -04:00

395 lines
12 KiB
Rust

use std::sync::Mutex;
use freya::query::{
Mutation, MutationCapability, MutationStateData, QueriesStorage, Query, QueryCapability,
QueryStateData, UseMutation, UseQuery, use_mutation, use_query,
};
use oneclient_core::LauncherError;
use oneclient_core::auth::{self, AccountKind, MicrosoftLoginSession, MinecraftAccount};
use uuid::Uuid;
static HANDLED_LOGIN_CODE: Mutex<Option<String>> = Mutex::new(None);
pub fn login_code_already_handled(user_code: &str) -> bool {
let mut handled = HANDLED_LOGIN_CODE.lock().unwrap();
if handled.as_deref() == Some(user_code) {
return true;
}
*handled = Some(user_code.to_string());
false
}
pub fn reset_login_code_dedup() {
*HANDLED_LOGIN_CODE.lock().unwrap() = None;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ListAccountsKeys;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DefaultAccountKeys {
pub fallback: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AccountKeys {
pub id: Uuid,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ListAccountsQuery;
impl QueryCapability for ListAccountsQuery {
type Ok = Vec<MinecraftAccount>;
type Err = LauncherError;
type Keys = ListAccountsKeys;
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::list_accounts().await
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DefaultAccountQuery;
impl QueryCapability for DefaultAccountQuery {
type Ok = Option<MinecraftAccount>;
type Err = LauncherError;
type Keys = DefaultAccountKeys;
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
let account = auth::get_default_account().await?;
if account.is_some() || !keys.fallback {
return Ok(account);
}
let accounts = auth::list_accounts().await?;
Ok(accounts.into_iter().next())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AccountQuery;
impl QueryCapability for AccountQuery {
type Ok = Option<MinecraftAccount>;
type Err = LauncherError;
type Keys = AccountKeys;
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::get_account(keys.id).await
}
}
pub fn use_accounts() -> UseQuery<ListAccountsQuery> {
use_query(Query::new(ListAccountsKeys, ListAccountsQuery))
}
pub fn use_default_account(fallback: bool) -> UseQuery<DefaultAccountQuery> {
use_query(Query::new(
DefaultAccountKeys { fallback },
DefaultAccountQuery,
))
}
pub fn use_current_account() -> UseQuery<DefaultAccountQuery> {
use_default_account(true)
}
pub fn use_account(id: Uuid) -> UseQuery<AccountQuery> {
use_query(Query::new(AccountKeys { id }, AccountQuery))
}
pub fn try_accounts(query: &UseQuery<ListAccountsQuery>) -> Option<Vec<MinecraftAccount>> {
settled_ok(query)
}
pub fn try_default_account(query: &UseQuery<DefaultAccountQuery>) -> Option<MinecraftAccount> {
settled_ok(query).flatten()
}
pub fn try_account(query: &UseQuery<AccountQuery>) -> Option<MinecraftAccount> {
settled_ok(query).flatten()
}
fn settled_ok<Q>(query: &UseQuery<Q>) -> Option<Q::Ok>
where
Q: QueryCapability,
Q::Ok: Clone,
{
let reader = query.read();
match &*reader.state() {
QueryStateData::Settled { res: Ok(value), .. } => Some(value.clone()),
QueryStateData::Loading {
res: Some(Ok(value)),
} => Some(value.clone()),
_ => None,
}
}
async fn invalidate_auth_queries(account_id: Option<Uuid>) {
QueriesStorage::<ListAccountsQuery>::try_invalidate_matching(ListAccountsKeys).await;
for fallback in [false, true] {
QueriesStorage::<DefaultAccountQuery>::try_invalidate_matching(DefaultAccountKeys {
fallback,
})
.await;
}
if let Some(id) = account_id {
QueriesStorage::<AccountQuery>::try_invalidate_matching(AccountKeys { id }).await;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BeginMicrosoftLoginMutation;
impl MutationCapability for BeginMicrosoftLoginMutation {
type Ok = MicrosoftLoginSession;
type Err = LauncherError;
type Keys = ();
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::begin_microsoft_login().await
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FinishMicrosoftLoginMutation;
impl MutationCapability for FinishMicrosoftLoginMutation {
type Ok = MinecraftAccount;
type Err = LauncherError;
type Keys = MicrosoftLoginSession;
async fn run(&self, flow: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::finish_microsoft_login(flow.clone()).await
}
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
if let Ok(account) = result {
invalidate_auth_queries(Some(account.id)).await;
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CancelMicrosoftLoginMutation;
/// Drops a pending browser login server-side when the user cancels before the
/// flow reaches the point of no return. Keyed by the session's CSRF state token.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CancelMicrosoftLoginKeys {
pub state_token: String,
}
impl MutationCapability for CancelMicrosoftLoginMutation {
type Ok = ();
type Err = LauncherError;
type Keys = CancelMicrosoftLoginKeys;
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::cancel_microsoft_login(&keys.state_token).await;
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AddMicrosoftAccountMutation;
impl MutationCapability for AddMicrosoftAccountMutation {
type Ok = MinecraftAccount;
type Err = LauncherError;
type Keys = ();
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
let flow = auth::begin_microsoft_login().await?;
auth::finish_microsoft_login(flow).await
}
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
if let Ok(account) = result {
invalidate_auth_queries(Some(account.id)).await;
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AddOfflineAccountMutation;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AddOfflineAccountKeys {
pub username: String,
}
impl MutationCapability for AddOfflineAccountMutation {
type Ok = MinecraftAccount;
type Err = LauncherError;
type Keys = AddOfflineAccountKeys;
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::add_offline_account(keys.username.clone()).await
}
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
if let Ok(account) = result {
invalidate_auth_queries(Some(account.id)).await;
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RemoveAccountMutation;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RemoveAccountKeys {
pub id: Uuid,
}
impl MutationCapability for RemoveAccountMutation {
type Ok = ();
type Err = LauncherError;
type Keys = RemoveAccountKeys;
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::remove_account(keys.id).await
}
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
if result.is_ok() {
invalidate_auth_queries(Some(keys.id)).await;
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SetDefaultAccountMutation;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SetDefaultAccountKeys {
pub id: Option<Uuid>,
}
impl MutationCapability for SetDefaultAccountMutation {
type Ok = ();
type Err = LauncherError;
type Keys = SetDefaultAccountKeys;
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::set_default_account(keys.id).await
}
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
if result.is_ok() {
invalidate_auth_queries(keys.id).await;
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RefreshAccountMutation;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RefreshAccountKeys {
pub id: Uuid,
}
impl MutationCapability for RefreshAccountMutation {
type Ok = MinecraftAccount;
type Err = LauncherError;
type Keys = RefreshAccountKeys;
async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::refresh_account(keys.id).await
}
async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
if result.is_ok() {
invalidate_auth_queries(Some(keys.id)).await;
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RefreshAllAccountsMutation;
impl MutationCapability for RefreshAllAccountsMutation {
type Ok = Vec<MinecraftAccount>;
type Err = LauncherError;
type Keys = ();
async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
auth::refresh_all_accounts().await
}
async fn on_settled(&self, _keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
if result.is_ok() {
invalidate_auth_queries(None).await;
}
}
}
pub type UseSetDefaultAccount = UseMutation<SetDefaultAccountMutation>;
pub type UseRemoveAccount = UseMutation<RemoveAccountMutation>;
pub type UseRefreshAccount = UseMutation<RefreshAccountMutation>;
pub fn use_begin_microsoft_login() -> UseMutation<BeginMicrosoftLoginMutation> {
use_mutation(Mutation::new(BeginMicrosoftLoginMutation))
}
pub fn use_finish_microsoft_login() -> UseMutation<FinishMicrosoftLoginMutation> {
use_mutation(Mutation::new(FinishMicrosoftLoginMutation))
}
pub fn use_cancel_microsoft_login() -> UseMutation<CancelMicrosoftLoginMutation> {
use_mutation(Mutation::new(CancelMicrosoftLoginMutation))
}
pub fn use_add_microsoft_account() -> UseMutation<AddMicrosoftAccountMutation> {
use_mutation(Mutation::new(AddMicrosoftAccountMutation))
}
pub fn use_add_offline_account() -> UseMutation<AddOfflineAccountMutation> {
use_mutation(Mutation::new(AddOfflineAccountMutation))
}
pub fn use_remove_account() -> UseMutation<RemoveAccountMutation> {
use_mutation(Mutation::new(RemoveAccountMutation))
}
pub fn use_set_default_account() -> UseMutation<SetDefaultAccountMutation> {
use_mutation(Mutation::new(SetDefaultAccountMutation))
}
pub fn use_refresh_account() -> UseMutation<RefreshAccountMutation> {
use_mutation(Mutation::new(RefreshAccountMutation))
}
pub fn use_refresh_all_accounts() -> UseMutation<RefreshAllAccountsMutation> {
use_mutation(Mutation::new(RefreshAllAccountsMutation))
}
pub fn mutation_is_pending<M: MutationCapability>(mutation: &UseMutation<M>) -> bool {
matches!(
&*mutation.read().state(),
MutationStateData::Pending | MutationStateData::Loading { .. }
)
}
pub fn mutation_error<M>(mutation: &UseMutation<M>) -> Option<M::Err>
where
M: MutationCapability,
M::Err: Clone,
{
let reader = mutation.read();
match &*reader.state() {
MutationStateData::Settled { res: Err(err), .. } => Some(err.clone()),
MutationStateData::Loading {
res: Some(Err(err)),
} => Some(err.clone()),
_ => None,
}
}
pub fn accounts_have_microsoft(accounts: &[MinecraftAccount]) -> bool {
accounts.iter().any(|a| a.kind == AccountKind::Microsoft)
}