@@ -98,6 +98,25 @@ pub enum WsError {
98
98
99
99
#[ error( "Unrecognized compression scheme: {scheme:#x}" ) ]
100
100
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
+ }
101
120
}
102
121
103
122
pub ( crate ) struct WsConnection {
@@ -249,6 +268,43 @@ fn request_insert_auth_header(req: &mut http::Request<()>, token: Option<&str>)
249
268
}
250
269
}
251
270
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
+
252
308
impl WsConnection {
253
309
#[ cfg( not( feature = "web" ) ) ]
254
310
pub ( crate ) async fn connect (
@@ -291,7 +347,13 @@ impl WsConnection {
291
347
connection_id : ConnectionId ,
292
348
params : WsParams ,
293
349
) -> 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 ( ) ) ?;
295
357
let sock = tokio_tungstenite_wasm:: connect_with_protocols ( & uri. to_string ( ) , & [ BIN_PROTOCOL ] )
296
358
. await
297
359
. map_err ( |source| WsError :: Tungstenite {
0 commit comments