Skip to main content

bitwarden_server_communication_config/wasm/
client.rs

1use wasm_bindgen::prelude::*;
2
3use crate::{
4    AcquiredCookie, ServerCommunicationConfig, ServerCommunicationConfigClient,
5    SetCommunicationTypeRequest,
6    wasm::{
7        JsServerCommunicationConfigPlatformApi, JsServerCommunicationConfigRepository,
8        RawJsServerCommunicationConfigPlatformApi, RawJsServerCommunicationConfigRepository,
9    },
10};
11
12/// JavaScript wrapper for ServerCommunicationConfigClient
13///
14/// This provides TypeScript access to the server communication configuration client,
15/// allowing clients to check bootstrap requirements and retrieve cookies for HTTP requests.
16#[wasm_bindgen(js_name = ServerCommunicationConfigClient)]
17pub struct JsServerCommunicationConfigClient {
18    client: ServerCommunicationConfigClient<
19        JsServerCommunicationConfigRepository,
20        JsServerCommunicationConfigPlatformApi,
21    >,
22}
23
24#[wasm_bindgen(js_class = ServerCommunicationConfigClient)]
25impl JsServerCommunicationConfigClient {
26    /// Creates a new ServerCommunicationConfigClient with a JavaScript repository and platform API
27    ///
28    /// The repository should be backed by StateProvider (or equivalent
29    /// storage mechanism) for persistence.
30    ///
31    /// # Arguments
32    ///
33    /// * `repository` - JavaScript implementation of the repository interface
34    /// * `platform_api` - JavaScript implementation of the platform API interface
35    #[wasm_bindgen(constructor)]
36    pub fn new(
37        repository: RawJsServerCommunicationConfigRepository,
38        platform_api: RawJsServerCommunicationConfigPlatformApi,
39    ) -> Self {
40        let js_repository = JsServerCommunicationConfigRepository::new(repository);
41        let js_platform_api = JsServerCommunicationConfigPlatformApi::new(platform_api);
42        Self {
43            client: ServerCommunicationConfigClient::new(js_repository, js_platform_api),
44        }
45    }
46
47    /// Retrieves the server communication configuration for a domain
48    ///
49    /// If no configuration exists, returns a default Direct bootstrap configuration.
50    ///
51    /// # Arguments
52    ///
53    /// * `domain` - The server domain (e.g., "vault.acme.com")
54    ///
55    /// # Errors
56    ///
57    /// Returns an error if the repository fails to retrieve the configuration.
58    #[wasm_bindgen(js_name = getConfig)]
59    pub async fn get_config(&self, domain: String) -> Result<ServerCommunicationConfig, String> {
60        self.client.get_config(domain).await
61    }
62
63    /// Determines if cookie bootstrapping is needed for this domain
64    ///
65    /// # Arguments
66    ///
67    /// * `domain` - The server domain (e.g., "vault.acme.com")
68    #[wasm_bindgen(js_name = needsBootstrap)]
69    pub async fn needs_bootstrap(&self, domain: String) -> bool {
70        self.client.needs_bootstrap(domain).await
71    }
72
73    /// Returns all cookies that should be included in requests to this server
74    ///
75    /// Returns an array of [cookie_name, cookie_value] pairs.
76    ///
77    /// # Arguments
78    ///
79    /// * `domain` - The server domain (e.g., "vault.acme.com")
80    #[wasm_bindgen(js_name = cookies)]
81    #[deprecated(
82        note = "Use get_cookies() instead, which will acquire cookies if not present in the config"
83    )]
84    #[allow(deprecated)]
85    pub async fn cookies(&self, domain: String) -> Result<JsValue, JsError> {
86        #[allow(deprecated)]
87        let cookies = self.client.cookies(domain).await;
88        serde_wasm_bindgen::to_value(&cookies).map_err(|e| JsError::new(&e.to_string()))
89    }
90
91    /// Returns all cookies that should be included in requests to this server
92    /// and triggers cookie acquisition if needed
93    ///
94    /// # Arguments
95    ///
96    /// * `domain` - The server domain (e.g., "vault.acme.com")
97    #[wasm_bindgen(js_name = getCookies)]
98    pub async fn get_cookies(&self, domain: String) -> Result<Vec<AcquiredCookie>, JsError> {
99        let cookies = self.client.get_cookies(domain).await;
100        cookies.map_err(|e| JsError::new(&e.to_string()))
101    }
102
103    /// Sets the server communication configuration for a domain
104    ///
105    /// This method saves the provided communication configuration to the repository.
106    /// Typically called when receiving the `/api/config` response from the server.
107    /// Previously acquired cookies are preserved automatically.
108    ///
109    /// # Arguments
110    ///
111    /// * `domain` - The server domain (e.g., "vault.acme.com")
112    /// * `request` - The server communication configuration to store
113    ///
114    /// # Errors
115    ///
116    /// Returns an error if the repository save operation fails
117    #[deprecated(
118        note = "Use set_communication_type_v2() instead, which extracts the domain from the config"
119    )]
120    #[allow(deprecated)]
121    #[wasm_bindgen(js_name = setCommunicationType)]
122    pub async fn set_communication_type(
123        &self,
124        domain: String,
125        request: SetCommunicationTypeRequest,
126    ) -> Result<(), String> {
127        #[allow(deprecated)]
128        self.client.set_communication_type(domain, request).await
129    }
130
131    /// Sets the server communication configuration using the domain from the config
132    ///
133    /// Extracts the `cookie_domain` from the `SsoCookieVendor` config and uses it as the
134    /// storage key. If the config is `Direct` or `cookie_domain` is not set, the call
135    /// is silently ignored.
136    ///
137    /// # Arguments
138    ///
139    /// * `config` - The server communication configuration to store
140    ///
141    /// # Errors
142    ///
143    /// Returns an error if the repository save operation fails
144    #[wasm_bindgen(js_name = setCommunicationTypeV2)]
145    pub async fn set_communication_type_v2(
146        &self,
147        request: SetCommunicationTypeRequest,
148    ) -> Result<(), String> {
149        self.client.set_communication_type_v2(request).await
150    }
151
152    /// Acquires a cookie from the platform and saves it to the repository
153    ///
154    /// This method calls the platform API to trigger cookie acquisition (e.g., browser
155    /// redirect to IdP), then validates and stores the acquired cookie in the repository.
156    ///
157    /// # Arguments
158    ///
159    /// * `domain` - The server domain (e.g., "vault.acme.com")
160    ///
161    /// # Errors
162    ///
163    /// Returns an error if:
164    /// - Cookie acquisition was cancelled by the user
165    /// - Server configuration doesn't support SSO cookies (Direct bootstrap)
166    /// - Acquired cookie name doesn't match expected name
167    /// - Repository operations fail
168    #[wasm_bindgen(js_name = acquireCookie)]
169    pub async fn acquire_cookie(&self, domain: String) -> Result<Vec<AcquiredCookie>, String> {
170        self.client
171            .acquire_cookie(&domain)
172            .await
173            .map_err(|e| e.to_string())
174    }
175}