@@ -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
103122pub ( 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+
252308impl 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