2022-07-15 15:39:38 +00:00
//! Authentication flow interface
2023-09-12 09:27:03 -07:00
use crate ::{
hydra ::{ self , init ::DeviceLoginSuccess } ,
launcher ::auth as inner ,
State ,
} ;
2023-06-02 18:36:10 -04:00
use chrono ::Utc ;
2022-07-15 15:39:38 +00:00
2023-06-02 07:09:46 -07:00
use crate ::state ::AuthTask ;
2022-07-15 15:39:38 +00:00
pub use inner ::Credentials ;
2023-03-31 18:44:26 -07:00
/// Authenticate a user with Hydra - part 1
/// This begins the authentication flow quasi-synchronously, returning a URL
/// This can be used in conjunction with 'authenticate_await_complete_flow'
/// to call authenticate and call the flow from the frontend.
/// Visit the URL in a browser, then call and await 'authenticate_await_complete_flow'.
2023-08-17 17:26:21 -07:00
pub async fn authenticate_begin_flow ( ) -> crate ::Result < DeviceLoginSuccess > {
2023-06-02 07:09:46 -07:00
let url = AuthTask ::begin_auth ( ) . await ? ;
2023-03-31 18:44:26 -07:00
Ok ( url )
}
/// Authenticate a user with Hydra - part 2
/// This completes the authentication flow quasi-synchronously, returning the credentials
/// This can be used in conjunction with 'authenticate_begin_flow'
/// to call authenticate and call the flow from the frontend.
2023-08-17 17:26:21 -07:00
pub async fn authenticate_await_complete_flow ( ) -> crate ::Result < Credentials > {
2023-06-02 07:09:46 -07:00
let credentials = AuthTask ::await_auth_completion ( ) . await ? ;
2023-03-31 18:44:26 -07:00
Ok ( credentials )
}
2023-06-02 07:09:46 -07:00
/// Cancels the active authentication flow
pub async fn cancel_flow ( ) -> crate ::Result < ( ) > {
AuthTask ::cancel ( ) . await
}
2022-07-15 15:39:38 +00:00
/// Refresh some credentials using Hydra, if needed
2023-03-31 18:44:26 -07:00
/// This is the primary desired way to get credentials, as it will also refresh them.
2022-07-15 15:39:38 +00:00
#[ tracing::instrument ]
2023-05-18 10:31:52 -07:00
#[ theseus_macros::debug_pin ]
2023-04-13 12:03:15 -07:00
pub async fn refresh ( user : uuid ::Uuid ) -> crate ::Result < Credentials > {
2022-07-15 15:39:38 +00:00
let state = State ::get ( ) . await ? ;
let mut users = state . users . write ( ) . await ;
2023-06-02 18:36:10 -04:00
let mut credentials = users . get ( user ) . ok_or_else ( | | {
crate ::ErrorKind ::OtherError (
" You are not logged in with a Minecraft account! " . to_string ( ) ,
)
2022-07-15 15:39:38 +00:00
. as_error ( )
2023-06-02 18:36:10 -04:00
} ) ? ;
2023-09-12 09:27:03 -07:00
let offline = * state . offline . read ( ) . await ;
2023-08-04 23:38:34 -07:00
2023-09-12 09:27:03 -07:00
if ! offline {
let fetch_semaphore : & crate ::util ::fetch ::FetchSemaphore =
& state . fetch_semaphore ;
if Utc ::now ( ) > credentials . expires
& & inner ::refresh_credentials ( & mut credentials , fetch_semaphore )
. await
. is_err ( )
{
users . remove ( credentials . id ) . await ? ;
return Err ( crate ::ErrorKind ::OtherError (
" Please re-authenticate with your Minecraft account! "
. to_string ( ) ,
)
. as_error ( ) ) ;
}
// Update player info from bearer token
let player_info = hydra ::stages ::player_info ::fetch_info ( & credentials . access_token ) . await . map_err ( | _err | {
crate ::ErrorKind ::HydraError ( " No Minecraft account for your profile. Make sure you own the game and have set a username through the official Minecraft launcher. "
. to_string ( ) )
} ) ? ;
credentials . username = player_info . name ;
users . insert ( & credentials ) . await ? ;
2023-06-02 18:36:10 -04:00
}
Ok ( credentials )
2022-07-15 15:39:38 +00:00
}
/// Remove a user account from the database
#[ tracing::instrument ]
pub async fn remove_user ( user : uuid ::Uuid ) -> crate ::Result < ( ) > {
let state = State ::get ( ) . await ? ;
let mut users = state . users . write ( ) . await ;
if state . settings . read ( ) . await . default_user = = Some ( user ) {
let mut settings = state . settings . write ( ) . await ;
2023-04-26 10:28:08 -07:00
settings . default_user = users . 0. values ( ) . next ( ) . map ( | it | it . id ) ;
2022-07-15 15:39:38 +00:00
}
2023-04-26 10:28:08 -07:00
users . remove ( user ) . await ? ;
2022-07-15 15:39:38 +00:00
Ok ( ( ) )
}
/// Check if a user exists in Theseus
#[ tracing::instrument ]
pub async fn has_user ( user : uuid ::Uuid ) -> crate ::Result < bool > {
let state = State ::get ( ) . await ? ;
let users = state . users . read ( ) . await ;
2023-04-26 10:28:08 -07:00
Ok ( users . contains ( user ) )
2022-07-15 15:39:38 +00:00
}
/// Get a copy of the list of all user credentials
#[ tracing::instrument ]
2023-04-26 10:28:08 -07:00
pub async fn users ( ) -> crate ::Result < Vec < Credentials > > {
2022-07-15 15:39:38 +00:00
let state = State ::get ( ) . await ? ;
let users = state . users . read ( ) . await ;
2023-04-26 10:28:08 -07:00
Ok ( users . 0. values ( ) . cloned ( ) . collect ( ) )
2022-07-15 15:39:38 +00:00
}
2023-03-31 18:44:26 -07:00
/// Get a specific user by user ID
/// Prefer to use 'refresh' instead of this function
#[ tracing::instrument ]
pub async fn get_user ( user : uuid ::Uuid ) -> crate ::Result < Credentials > {
let state = State ::get ( ) . await ? ;
let users = state . users . read ( ) . await ;
2023-04-26 10:28:08 -07:00
let user = users . get ( user ) . ok_or_else ( | | {
2023-03-31 18:44:26 -07:00
crate ::ErrorKind ::OtherError ( format! (
" Tried to get nonexistent user with ID {user} "
) )
. as_error ( )
} ) ? ;
Ok ( user )
}