1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use reqwest::header::HeaderMap;

/// Default v1 Passage Auth API base
pub const PASSAGE_AUTH_API_BASE: &str = "https://auth.passage.id/v1";

#[derive(Clone, Debug)]
pub struct Config {
    api_base: String,
    api_key: Option<String>,
    app_id: String,
    app_auth_origin: String,
    // TODO: We should probably store the key instead of a string for earlier
    // validation and performance
    pub_jwk: Option<String>,
    user_bearer_token: Option<String>,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            api_base: PASSAGE_AUTH_API_BASE.into(),
            // TODO: We could load this from passage using the app_id
            app_auth_origin: std::env::var("PASSAGE_APP_AUTH_ORIGIN")
                .unwrap_or_else(|_| "".to_string()),
            api_key: std::env::var("PASSAGE_API_KEY").ok(),
            // TODO: Maybe make this required
            app_id: std::env::var("PASSAGE_APP_ID").unwrap_or_else(|_| "".to_string()),
            // TODO: We could load this from passage using the app_id
            pub_jwk: std::env::var("PASSAGE_PUB_JWK").ok(),
            user_bearer_token: None,
        }
    }
}

impl Config {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn with_api_base(mut self, api_base: String) -> Self {
        self.api_base = api_base;
        self
    }

    pub fn with_api_key(mut self, api_key: String) -> Self {
        if api_key.is_empty() {
            return self;
        }
        self.api_key = Some(api_key);
        self
    }

    pub fn with_app_id(mut self, app_id: String) -> Self {
        self.app_id = app_id;
        self
    }

    pub fn with_app_auth_origin(mut self, auth_origin: String) -> Self {
        self.app_auth_origin = auth_origin;
        self
    }

    pub fn with_pub_jwk(mut self, pub_jwk: String) -> Self {
        if pub_jwk.is_empty() {
            return self;
        }
        self.pub_jwk = Some(pub_jwk);
        self
    }

    pub fn with_user_bearer_token(mut self, user_bearer_token: String) -> Self {
        self.user_bearer_token = Some(user_bearer_token);
        self
    }

    pub fn pub_jwk(&self) -> Option<&String> {
        self.pub_jwk.as_ref()
    }

    pub fn url(&self, path: &str) -> String {
        format!(
            "{}{}",
            self.api_base,
            path.replace("{app_id}", &self.app_id)
        )
    }

    pub fn query(&self) -> Vec<(&str, &str)> {
        vec![]
    }

    /// TODO: Can probably just return a token and the http client can handle
    /// the header
    pub fn bearer_auth(&self) -> HeaderMap {
        let mut headers = HeaderMap::new();
        if let Some(token) = &self.user_bearer_token {
            headers.insert(
                "Authorization",
                format!("Bearer {}", token).parse().unwrap(),
            );
        }
        headers
    }

    /// TODO: Remove once we have `passage-manage` crate
    pub fn api_key_auth(&self) -> HeaderMap {
        let mut headers = HeaderMap::new();
        if let Some(api_key) = &self.api_key {
            headers.insert(
                "Authorization",
                format!("Bearer {}", api_key).parse().unwrap(),
            );
        }
        headers
    }

    pub fn app_auth_origin(&self) -> &str {
        &self.app_auth_origin
    }

    pub fn app_id(&self) -> &str {
        &self.app_id
    }
}