Skip to content

Commit ddb6e90

Browse files
committed
Add token verification for the wasm sdk websocket connection
1 parent 9ec9ff6 commit ddb6e90

File tree

3 files changed

+69
-4
lines changed

3 files changed

+69
-4
lines changed

Cargo.lock

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/sdk/Cargo.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ web = [
1414
"dep:getrandom",
1515
"dep:gloo-console",
1616
"dep:gloo-storage",
17+
"dep:js-sys",
1718
"dep:rustls-pki-types",
1819
"dep:tokio-tungstenite-wasm",
1920
"dep:wasm-bindgen",
@@ -45,11 +46,14 @@ futures-util = { version = "0.3", default-features = false, features = ["sink",
4546
getrandom = { version = "0.3.2", features = ["wasm_js"], optional = true }
4647
gloo-console = { version = "0.3.0", optional = true }
4748
gloo-storage = { version = "0.3.0", optional = true }
49+
js-sys = { version = "0.3", optional = true }
4850
rustls-pki-types = { version = "1.11.0", features = ["web"], optional = true }
4951
tokio-tungstenite-wasm = { version = "0.6.0", optional = true }
5052
wasm-bindgen = { version = "0.2.100", optional = true }
5153
wasm-bindgen-futures = { version = "0.4.45", optional = true }
52-
web-sys = { version = "0.3.77", features = [ "Document", "HtmlDocument", "Window" ], optional = true}
54+
web-sys = { version = "0.3.77", features = [
55+
"Document", "Headers", "HtmlDocument", "RequestInit", "Response", "Window"
56+
], optional = true}
5357

5458
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
5559
home.workspace = true

crates/sdk/src/websocket.rs

+63-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,25 @@ pub enum WsError {
9898

9999
#[error("Unrecognized compression scheme: {scheme:#x}")]
100100
UnknownCompressionScheme { scheme: u8 },
101+
102+
#[cfg(feature = "web")]
103+
#[error("Token verification error: {0}")]
104+
TokenVerification(String),
105+
}
106+
107+
#[cfg(feature = "web")]
108+
impl From<wasm_bindgen::JsValue> for WsError {
109+
fn from(js: wasm_bindgen::JsValue) -> Self {
110+
use wasm_bindgen::JsCast;
111+
if let Some(err) = js.dyn_ref::<js_sys::Error>() {
112+
// If it's a JS `Error` grab its .message()
113+
WsError::TokenVerification(err.message().into())
114+
} else {
115+
// Try to coerce to a JS string first; otherwise debug‑print
116+
let s = js.as_string().unwrap_or_else(|| format!("{:?}", js));
117+
WsError::TokenVerification(s)
118+
}
119+
}
101120
}
102121

103122
pub(crate) struct WsConnection {
@@ -249,6 +268,43 @@ fn request_insert_auth_header(req: &mut http::Request<()>, token: Option<&str>)
249268
}
250269
}
251270

271+
#[cfg(feature = "web")]
272+
async fn fetch_ws_token(host: &Uri, auth_token: &str) -> Result<String, WsError> {
273+
use js_sys::Reflect;
274+
use wasm_bindgen::{JsCast, JsValue};
275+
use wasm_bindgen_futures::JsFuture;
276+
use web_sys::{Headers, RequestInit, Response};
277+
278+
let url = format!("{}v1/identity/websocket-token", host);
279+
280+
// Build the Request
281+
let headers = Headers::new()?;
282+
headers.set("Authorization", &format!("Bearer {auth_token}"))?;
283+
let req_opts = RequestInit::new();
284+
req_opts.set_method("POST");
285+
req_opts.set_headers(&headers);
286+
287+
// Send the request
288+
let window = web_sys::window().ok_or_else(|| WsError::TokenVerification("No global window object".into()))?;
289+
let resp: Response = JsFuture::from(window.fetch_with_str_and_init(&url, &req_opts))
290+
.await?
291+
.dyn_into()?;
292+
if !resp.ok() {
293+
return Err(WsError::TokenVerification(format!(
294+
"HTTP error: {} {}",
295+
resp.status(),
296+
resp.status_text()
297+
)));
298+
}
299+
300+
// Parse the response
301+
let json = JsFuture::from(resp.json()?).await?;
302+
let token_js = Reflect::get(&json, &JsValue::from_str("token"))?;
303+
token_js
304+
.as_string()
305+
.ok_or_else(|| WsError::TokenVerification("`token` parsing failed".into()))
306+
}
307+
252308
impl WsConnection {
253309
#[cfg(not(feature = "web"))]
254310
pub(crate) async fn connect(
@@ -291,7 +347,13 @@ impl WsConnection {
291347
connection_id: ConnectionId,
292348
params: WsParams,
293349
) -> Result<Self, WsError> {
294-
let uri = make_uri(host, db_name, connection_id, params, token)?;
350+
let token = if let Some(auth_token) = token {
351+
Some(fetch_ws_token(&host, auth_token).await?)
352+
} else {
353+
None
354+
};
355+
356+
let uri = make_uri(host, db_name, connection_id, params, token.as_deref())?;
295357
let sock = tokio_tungstenite_wasm::connect_with_protocols(&uri.to_string(), &[BIN_PROTOCOL])
296358
.await
297359
.map_err(|source| WsError::Tungstenite {

0 commit comments

Comments
 (0)