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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions