ddnet_account_client/
account_token.rs

1use ddnet_accounts_shared::{
2    account_server::{account_token::AccountTokenError, errors::AccountServerRequestError},
3    client::account_token::{
4        AccountTokenEmailRequest, AccountTokenOperation, AccountTokenSteamRequest, SecretKey,
5    },
6};
7
8use anyhow::anyhow;
9use thiserror::Error;
10
11use crate::{
12    errors::{FsLikeError, HttpLikeError},
13    interface::Io,
14    safe_interface::{IoSafe, SafeIo},
15};
16
17/// The result of a [`account`] request.
18#[derive(Error, Debug)]
19pub enum AccountTokenResult {
20    /// A http like error occurred.
21    #[error("{0}")]
22    HttpLikeError(HttpLikeError),
23    /// A fs like error occurred.
24    #[error("{0}")]
25    FsLikeError(FsLikeError),
26    #[error("{0}")]
27    /// The account server responded with an error.
28    AccountServerRequstError(AccountServerRequestError<AccountTokenError>),
29    /// Errors that are not handled explicitly.
30    #[error("Account failed: {0}")]
31    Other(anyhow::Error),
32}
33
34impl From<HttpLikeError> for AccountTokenResult {
35    fn from(value: HttpLikeError) -> Self {
36        Self::HttpLikeError(value)
37    }
38}
39
40impl From<FsLikeError> for AccountTokenResult {
41    fn from(value: FsLikeError) -> Self {
42        Self::FsLikeError(value)
43    }
44}
45
46fn get_secret_key(
47    secret_key_hex: Option<String>,
48) -> anyhow::Result<Option<SecretKey>, AccountTokenResult> {
49    secret_key_hex
50        .map(hex::decode)
51        .transpose()
52        .map_err(|err| AccountTokenResult::Other(err.into()))?
53        .map(|secret_key| secret_key.try_into())
54        .transpose()
55        .map_err(|_| {
56            AccountTokenResult::Other(anyhow!(
57                "secret key had an invalid length. make sure you copied it correctly."
58            ))
59        })
60}
61
62/// Generate a token sent by email.
63pub async fn account_token_email(
64    email: email_address::EmailAddress,
65    op: AccountTokenOperation,
66    secret_key_hex: Option<String>,
67    io: &dyn Io,
68) -> anyhow::Result<(), AccountTokenResult> {
69    account_token_email_impl(email, op, secret_key_hex, io.into()).await
70}
71
72async fn account_token_email_impl(
73    email: email_address::EmailAddress,
74    op: AccountTokenOperation,
75    secret_key_hex: Option<String>,
76    io: IoSafe<'_>,
77) -> anyhow::Result<(), AccountTokenResult> {
78    if secret_key_hex.is_some() {
79        io.request_account_token_email_secret(AccountTokenEmailRequest {
80            email,
81            secret_key: get_secret_key(secret_key_hex)?,
82            op,
83        })
84        .await
85    } else {
86        io.request_account_token_email(AccountTokenEmailRequest {
87            email,
88            secret_key: get_secret_key(secret_key_hex)?,
89            op,
90        })
91        .await
92    }?
93    .map_err(AccountTokenResult::AccountServerRequstError)?;
94
95    Ok(())
96}
97
98/// Request an account token for the given steam credential.
99/// The token is serialized in hex.
100pub async fn account_token_steam(
101    steam_ticket: Vec<u8>,
102    op: AccountTokenOperation,
103    secret_key_hex: Option<String>,
104    io: &dyn Io,
105) -> anyhow::Result<String, AccountTokenResult> {
106    account_token_steam_impl(steam_ticket, op, secret_key_hex, io.into()).await
107}
108
109async fn account_token_steam_impl(
110    steam_ticket: Vec<u8>,
111    op: AccountTokenOperation,
112    secret_key_hex: Option<String>,
113    io: IoSafe<'_>,
114) -> anyhow::Result<String, AccountTokenResult> {
115    let account_token_hex = if secret_key_hex.is_some() {
116        io.request_account_token_steam_secret(AccountTokenSteamRequest {
117            steam_ticket,
118            secret_key: get_secret_key(secret_key_hex)?,
119            op,
120        })
121        .await
122    } else {
123        io.request_account_token_steam(AccountTokenSteamRequest {
124            steam_ticket,
125            secret_key: get_secret_key(secret_key_hex)?,
126            op,
127        })
128        .await
129    }?
130    .map_err(AccountTokenResult::AccountServerRequstError)?;
131
132    Ok(account_token_hex)
133}