Skip to main content

asana_cli/api/
auth.rs

1//! Authentication helpers for the Asana API client.
2
3use secrecy::{ExposeSecret, SecretString};
4use std::fmt;
5
6/// Wrapper around a Personal Access Token (PAT) ensuring secret handling.
7#[derive(Clone, Debug)]
8pub struct AuthToken(SecretString);
9
10impl AuthToken {
11    /// Construct a new token wrapper.
12    #[must_use]
13    pub const fn new(token: SecretString) -> Self {
14        Self(token)
15    }
16
17    /// Expose the token contents.
18    #[must_use]
19    pub fn expose(&self) -> &str {
20        self.0.expose_secret()
21    }
22}
23
24impl From<SecretString> for AuthToken {
25    fn from(value: SecretString) -> Self {
26        Self::new(value)
27    }
28}
29
30impl fmt::Display for AuthToken {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        f.write_str("<redacted token>")
33    }
34}
35
36/// Trait for providing access tokens; enables testing without touching the
37/// concrete configuration layer.
38pub trait TokenProvider: Send + Sync {
39    /// Obtain a fresh Personal Access Token.
40    fn personal_access_token(&self) -> SecretString;
41}
42
43/// Simple token provider that always returns the same token.
44#[derive(Clone, Debug)]
45pub struct StaticTokenProvider {
46    token: AuthToken,
47}
48
49impl StaticTokenProvider {
50    /// Create a new provider from a token.
51    #[must_use]
52    pub const fn new(token: AuthToken) -> Self {
53        Self { token }
54    }
55}
56
57impl TokenProvider for StaticTokenProvider {
58    fn personal_access_token(&self) -> SecretString {
59        SecretString::new(self.token.expose().to_owned().into())
60    }
61}
62
63impl From<AuthToken> for StaticTokenProvider {
64    fn from(token: AuthToken) -> Self {
65        Self::new(token)
66    }
67}