bitwarden_server_communication_config/wasm/
js_platform_api.rs

1use bitwarden_threading::ThreadBoundRunner;
2use wasm_bindgen::prelude::*;
3
4use crate::{AcquiredCookie, ServerCommunicationConfigPlatformApi};
5
6#[wasm_bindgen(typescript_custom_section)]
7const TS_CUSTOM_TYPES: &'static str = r#"
8/**
9 * Platform API interface for acquiring SSO cookies.
10 * 
11 * Platform clients implement this interface to handle cookie acquisition
12 * through browser redirects or other platform-specific mechanisms.
13 */
14export interface ServerCommunicationConfigPlatformApi {
15    /**
16     * Acquires cookies for the given hostname.
17     * 
18     * This typically involves redirecting to an IdP login page and extracting
19     * cookies from the load balancer response. For sharded cookies, returns
20     * multiple entries with names like "CookieName-0", "CookieName-1", etc.
21     * 
22     * @param hostname The server hostname (e.g., "vault.acme.com")
23     * @returns An array of AcquiredCookie objects, or undefined if acquisition failed or was cancelled
24     */
25    acquireCookies(hostname: string): Promise<AcquiredCookie[] | undefined>;
26}
27"#;
28
29#[wasm_bindgen]
30extern "C" {
31    /// JavaScript interface for the ServerCommunicationConfigPlatformApi
32    #[wasm_bindgen(
33        js_name = ServerCommunicationConfigPlatformApi,
34        typescript_type = "ServerCommunicationConfigPlatformApi"
35    )]
36    pub type RawJsServerCommunicationConfigPlatformApi;
37
38    /// Acquires cookies for a hostname
39    #[wasm_bindgen(catch, method, structural, js_name = acquireCookies)]
40    pub async fn acquire_cookies(
41        this: &RawJsServerCommunicationConfigPlatformApi,
42        hostname: String,
43    ) -> Result<JsValue, JsValue>;
44}
45
46/// Thread-safe JavaScript implementation of ServerCommunicationConfigPlatformApi
47///
48/// This wrapper ensures the JavaScript platform API can be safely used across
49/// threads in WASM environments by using ThreadBoundRunner to pin operations
50/// to the main thread.
51pub struct JsServerCommunicationConfigPlatformApi(
52    ThreadBoundRunner<RawJsServerCommunicationConfigPlatformApi>,
53);
54
55impl JsServerCommunicationConfigPlatformApi {
56    /// Creates a new JsServerCommunicationConfigPlatformApi wrapping the raw JavaScript platform
57    /// API
58    pub fn new(platform_api: RawJsServerCommunicationConfigPlatformApi) -> Self {
59        Self(ThreadBoundRunner::new(platform_api))
60    }
61}
62
63impl Clone for JsServerCommunicationConfigPlatformApi {
64    fn clone(&self) -> Self {
65        Self(self.0.clone())
66    }
67}
68
69#[async_trait::async_trait]
70impl ServerCommunicationConfigPlatformApi for JsServerCommunicationConfigPlatformApi {
71    async fn acquire_cookies(&self, hostname: String) -> Option<Vec<AcquiredCookie>> {
72        self.0
73            .run_in_thread(move |platform_api| async move {
74                let js_value = platform_api
75                    .acquire_cookies(hostname)
76                    .await
77                    .map_err(|e| format!("{e:?}"))?;
78
79                if js_value.is_undefined() || js_value.is_null() {
80                    return Ok(None);
81                }
82
83                let cookies: Vec<AcquiredCookie> =
84                    serde_wasm_bindgen::from_value(js_value).map_err(|e| e.to_string())?;
85                Ok(Some(cookies))
86            })
87            .await
88            .ok()
89            .and_then(|result: Result<Option<Vec<AcquiredCookie>>, String>| result.ok())
90            .flatten()
91    }
92}