1use std::sync::Arc;
13
14use async_trait::async_trait;
15use openapp_sdk_common::ApiKey;
16use reqwest::header::{AUTHORIZATION, HeaderName};
17
18use crate::error::SdkError;
19
20pub const API_KEY_HEADER: HeaderName = HeaderName::from_static("x-api-key");
22
23#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct AuthToken {
27 pub header: HeaderName,
28 pub value: String,
29}
30
31impl AuthToken {
32 #[must_use]
34 pub fn api_key(token: impl Into<String>) -> Self {
35 Self {
36 header: API_KEY_HEADER,
37 value: token.into(),
38 }
39 }
40
41 #[must_use]
43 pub fn bearer(token: impl AsRef<str>) -> Self {
44 Self {
45 header: AUTHORIZATION,
46 value: format!("Bearer {}", token.as_ref()),
47 }
48 }
49}
50
51#[async_trait]
53pub trait TokenProvider: Send + Sync + std::fmt::Debug {
54 async fn token(&self) -> Result<AuthToken, SdkError>;
57}
58
59pub type SharedTokenProvider = Arc<dyn TokenProvider>;
61
62#[derive(Debug, Clone)]
64pub struct StaticApiKey {
65 key: ApiKey,
66}
67
68impl StaticApiKey {
69 #[must_use]
71 pub fn new(key: ApiKey) -> Self {
72 Self { key }
73 }
74
75 pub fn from_raw(token: impl Into<String>) -> Result<Self, SdkError> {
77 Ok(Self::new(ApiKey::parse(token)?))
78 }
79
80 #[must_use]
82 pub fn api_key(&self) -> &ApiKey {
83 &self.key
84 }
85}
86
87#[async_trait]
88impl TokenProvider for StaticApiKey {
89 async fn token(&self) -> Result<AuthToken, SdkError> {
90 Ok(AuthToken::api_key(self.key.as_str()))
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[tokio::test]
99 async fn static_provider_emits_x_api_key_with_full_token() {
100 let provider = StaticApiKey::from_raw("https://openapp.house_openapp_SECRET").unwrap();
101 let token = provider.token().await.unwrap();
102 assert_eq!(token.header.as_str(), "x-api-key");
103 assert_eq!(token.value, "https://openapp.house_openapp_SECRET");
104 }
105
106 #[test]
107 fn bearer_token_uses_authorization_header() {
108 let token = AuthToken::bearer("jwt.payload.sig");
109 assert_eq!(token.header, AUTHORIZATION);
110 assert_eq!(token.value, "Bearer jwt.payload.sig");
111 }
112}