ddnet_account_client/
credential_auth_token.rs

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