Skip to content

LAN pairing server shuts down immediately — cancel() called before waiting #194

@bordumb

Description

@bordumb

Bug

auths pair (LAN mode) prints the QR code and debug curl line, but the HTTP server never accepts connections. lsof -i :<port> shows nothing listening. curl localhost:<port>/health returns connection refused.

Root cause

In crates/auths-cli/src/commands/device/pair/lan_server.rs, line 107:

pub async fn wait_for_response(
    self,
    timeout: Duration,
) -> Result<SubmitResponseRequest, auths_sdk::error::PairingError> {
    self.cancel.cancel();  // ← BUG: cancels the server IMMEDIATELY

    self.handle
        .wait_for_response(timeout)
        .await
        ...
}

The CancellationToken::cancel() is called as the first action in wait_for_response. This triggers the cancel_clone.cancelled() branch in the server's tokio::select! (line 65), shutting down the axum server before any connections can be accepted.

The cancellation token is meant for cleanup after the pairing completes or times out — not as the first action when waiting begins.

Flow

  1. LanPairingServer::start() binds a TcpListener, spawns the axum server in a tokio::spawn with a CancellationToken
  2. CLI prints QR code, short code, and debug curl line
  3. server.wait_for_response(expiry_duration).await is called (lan.rs:182)
  4. self.cancel.cancel() fires immediately (lan_server.rs:107)
  5. The tokio::select! in the spawned task sees cancelled() resolve → exits
  6. Server stops listening. No connections ever accepted.

Fix

Move self.cancel.cancel() to after the response is received (or after timeout), not before:

pub async fn wait_for_response(
    self,
    timeout: Duration,
) -> Result<SubmitResponseRequest, auths_sdk::error::PairingError> {
    let result = self.handle
        .wait_for_response(timeout)
        .await
        .map_err(|e| match e {
            auths_pairing_daemon::DaemonError::Pairing(pe) => pe,
            other => auths_sdk::error::PairingError::LocalServerError(other.to_string()),
        });

    // Shut down the server AFTER we have the response (or timed out)
    self.cancel.cancel();
    result
}

Reproduction

auths pair  # starts LAN pairing
# In another terminal:
curl http://127.0.0.1:<port>/health  # connection refused
lsof -i :<port>  # empty

Affected version

Current main and dev-pairingHardening branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions