0

I am building out my first Rust client/server application and struggling to get my client/server talking to each other over a websocket connection.

Any help would be highly appreciated!

Problem Overview

  • I am using axum with axum-login on the server.
  • I am using reqwest and ewebsock on the clients.
  • I am using websockets to transport data between the server and connected clients.
  • The client code needs to build and run on wasm and native targets.
  • Everything works perfectly with the wasm client.
  • The native client fails when opening the websocket conneciton with a 401 Unauthorized.

Connection process

The first three steps are exactly the same for wasm and native.

Wasm client (works)

  • Client send HTTP POST request to http://localhost:8080/login with login credentials
  • Login succeeds.
  • Client opens websocket connection via ws://localhost:8080/websocket using ewebsock crate.
  • Connection opens successfully.

Native client (fails)

  • Client send HTTP POST request to http://localhost:8080/login with login credentials
  • Login succeeds.
  • Client opens websocket connection via ws://localhost:8080/websocket using ewebsock crate.
  • Connection rejected with a 401 Unauthorized response.

I can not understand why the 401 is happening only for the native client. I suspect an issue with storing the users authentication cookie but as far as I can tell, ewebsock (which depends on tokio-tungstenite) does not read the browser cookies while opening the websocket connection anyways (?).

Maybe someone who has a working user auth system with an axum web server using websockets as the transport layer could shed some light on how they got things working?

Relevant dependencies

Client

  • reqwest to make http calls to backend to authorize the user.
    • enabled cookie_store on native
  • ewebsock to establish a websocket connection once the user has authenticated.

Server

Code

Servers main function - axum::Router (paired down):

let app = Router::new()
        .route("/websocket", get(websocket_handler))
        .route_layer(RequireAuthorizationLayer::<UserId, User, UserRole>::login())
        .route("/login", post(login_handler))
        .layer(TimeoutLayer::new(Duration::from_secs(4)))
        .layer(auth_layer)
        .layer(session_layer)
        .layer(CorsLayer::permissive())
        .layer(LiveReloadLayer::new())
        .with_state(app_state);

pub async fn websocket_handler(
    ws: WebSocketUpgrade,
    State(app_state): State<AppState>,
) -> impl IntoResponse {
    ws.on_upgrade(|socket| websocket(socket, app_state.websocket_state))
}

How can I fix the 401 Unauthorized response on native?

Other notes

  • If I remove the requirement for authorization, both the wasm and native clients connect successfully.
  • I am using axum_sessions for session management (removed this code from the snippet above).
  • I still strongly suspect the issue is with cookie management - but I am not sure how to open this connection elegantly (i.e. using the same code paths) for both wasm and native.

I have tried enabling the tls feature on ewebsock crate and changing the connection url to wss://localhost/websocket. This fails.

I have tried enabling the cookie_store in reqwest - but this has no effect. I do not beleive there is a way for ewebsock to access that information anyways.

Bryan Reilly
  • 114
  • 4
  • 1
    Where is the clients code? Please provide a [mre]. We can't effectively help debug code we can't see. – cafce25 Apr 27 '23 at 05:02
  • 1
    According to the documentation for [`ewebsock::connect`](https://docs.rs/ewebsock/latest/ewebsock/fn.connect.html), it uses the browser's `WebSocket` API, so it has access to the browser's cookie jar. But I don't see any way to give it access to the `reqwest` cookie store on native. – Jmb Apr 27 '23 at 06:38
  • I think the cookie set by the server during the login request is not included in the subsequent requests, here `WebSocket handshake`. Please check. – Jishan Shaikh Apr 28 '23 at 05:11

0 Answers0