Skip to content

fix: return a 401/407 as the final INVITE response when no credential is set - #144

Open
tgeorge06 wants to merge 1 commit into
restsend:mainfrom
tgeorge06:fix/auth-challenge-without-credential
Open

tgeorge06 wants to merge 1 commit into
restsend:mainfrom
tgeorge06:fix/auth-challenge-without-credential

Conversation

@tgeorge06

Copy link
Copy Markdown

Bug

When an outbound INVITE is challenged with 401 Unauthorized or 407 Proxy Authentication Required and the InviteOption has no credential, do_invite returns (dialog, None). The caller never sees the challenge. It can't tell "auth required" apart from other failures that return no response, and it can't read the status or the WWW-Authenticate / Proxy-Authenticate header.

The cause is in InviteDialog::process_invite: the no-credential branch moves the dialog to Terminated(ProxyAuthRequired) and then continues without setting final_response. The client transaction terminates right after sending the ACK, so the loop ends and the function returns None. The branch that handles a second challenge after an authenticated retry already sets final_response and breaks; the no-credential branch doesn't.

The deprecated ClientInviteDialog::process_invite has the same copy of this code and the same bug. It is still public, so it is fixed too.

Reproduction

New tests in src/dialog/tests/test_invite_auth_challenge.rs use real UDP UAC and UAS endpoints on loopback. On main:

test ...::test_invite_401_without_credential_is_final_response ... FAILED
test ...::test_invite_407_without_credential_is_final_response ... FAILED
test ...::test_legacy_client_dialog_401_without_credential_is_final_response ... FAILED
the 401 Unauthorized challenge must be the final response
the 407 Proxy Authentication Required challenge must be the final response

RFC

RFC 3261 §22.2 and §22.3: when a UAC gets a 401/407 it MAY retry with credentials. When it has none, it can't retry, so the challenge is the final response to the INVITE and should reach the application, the same as any other final non-2xx response (§13.2.2.3).

Fix

In the no-credential branch, set final_response = Some(resp) and break instead of continue, the same as the second-challenge branch. Nothing else changes:

  • the dialog still terminates with TerminatedReason::ProxyAuthRequired, and that state is still emitted;
  • the ACK is sent by the transaction layer as before;
  • the path with a credential configured (one authenticated retry, and a second challenge treated as final) is unchanged.

The diff is 7 lines in each of invite_dialog.rs and client_dialog.rs. The debug log now says "auth challenge" and includes the status, because it used to say "407" for 401s too.

Tests

Six new tests:

  • 401 and 407 without a credential: the challenge comes back as the final response, the dialog ends in ProxyAuthRequired, and no retry is sent.
  • The same check for 401 through the deprecated ClientInviteDialog::process_invite.
  • 401 and 407 with a credential: exactly one retry, carrying Authorization for 401 and Proxy-Authorization for 407, and the caller gets the 200.
  • The authenticated retry is challenged again: that second challenge is the final response, and no third INVITE is sent.

Results: cargo fmt --all -- --check is clean. cargo build has no warnings. cargo test passes 336 lib tests and 65 doctests (main passes 330 + 65). The new tests also passed 20 repeated runs.

Compatibility / risk

Low. The only behaviour change: callers that used to get None for a challenged INVITE with no credential now get Some(401/407). do_invite and do_invite_async only treat a 2xx as success, so the dialog-layer registry works as before. Code that checked resp.is_none() to catch this case should check the status code instead, which is what callers already do for other final failures.

… is set

When an INVITE was challenged and the dialog had no credential,
process_invite terminated the dialog with ProxyAuthRequired but kept
looping without recording the response. The client transaction ends
right after the ACK, so do_invite returned (id, None) and the caller
lost the status and the challenge.

Per RFC 3261 §22.2/§22.3 the UAC may retry with credentials; when it
cannot, the 401/407 is the final response. Record it and stop, as the
second-challenge branch already does. Applied to both InviteDialog and
the deprecated ClientInviteDialog. The authenticated retry path is
unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant