Skip to main content

respond

Function respond 

Source
pub fn respond(
    www_authenticate: &str,
    ctx: &RequestContext<'_>,
    credentials: Credentials,
) -> Result<String>
Expand description

One-shot challenge->response: computes the Authorization value for a single request without keeping an Authenticator around.

Prefer Authenticator when the same credentials answer multiple requests in one session (Digest’s nc must advance) — this is a thin convenience over Authenticator::from_challenge(..)?.authorization(..).

Examples found in repository?
examples/server_verify.rs (line 44)
30fn digest() {
31    let verifier = Verifier::new(
32        Credentials::Digest {
33            username: "admin".into(),
34            password: "hunter2".into(),
35        },
36        REALM,
37    );
38    let challenge = verifier.challenge();
39    println!("[digest] challenge: {challenge}");
40
41    let ctx = RequestContext::new("DESCRIBE", "rtsp://cam/live");
42
43    // Correct credential: respond() answers the challenge, verify() accepts.
44    let correct = respond(&challenge, &ctx, Credentials::new("admin", "hunter2"))
45        .expect("respond computes an Authorization value");
46    let outcome = verify(&verifier, &correct, &ctx);
47    println!("[digest] correct password  -> {outcome:?}");
48    assert_eq!(outcome, AuthResult::Ok);
49
50    // Wrong credential: same challenge, wrong password -> rejected.
51    let wrong = respond(&challenge, &ctx, Credentials::new("admin", "WRONG"))
52        .expect("respond computes an Authorization value even for a wrong password");
53    let outcome = verify(&verifier, &wrong, &ctx);
54    println!("[digest] wrong password    -> {outcome:?}");
55    assert_eq!(outcome, AuthResult::Unauthorized);
56}
57
58/// Basic (RFC 7617): same accept/reject shape, briefly.
59fn basic() {
60    let verifier = Verifier::new(
61        Credentials::Basic {
62            username: "admin".into(),
63            password: "hunter2".into(),
64        },
65        REALM,
66    );
67    let challenge = verifier.challenge();
68    println!("[basic] challenge: {challenge}");
69
70    let ctx = RequestContext::new("GET", "/stream/media.m3u8");
71    let correct =
72        respond(&challenge, &ctx, Credentials::new("admin", "hunter2")).expect("responds");
73    assert_eq!(verify(&verifier, &correct, &ctx), AuthResult::Ok);
74    println!("[basic] correct password   -> Ok");
75
76    let wrong = respond(&challenge, &ctx, Credentials::new("admin", "WRONG")).expect("responds");
77    assert_eq!(verify(&verifier, &wrong, &ctx), AuthResult::Unauthorized);
78    println!("[basic] wrong password     -> Unauthorized");
79}
80
81/// Bearer (RFC 6750): no challenge round-trip needed, but still an
82/// accept/reject pair — a wrong token must not verify.
83fn bearer() {
84    let verifier = Verifier::new(Credentials::bearer("right-token"), REALM);
85    let challenge = verifier.challenge();
86    println!("[bearer] challenge: {challenge}");
87
88    let ctx = RequestContext::new("GET", "/stream/media.m3u8");
89    let correct = respond(&challenge, &ctx, Credentials::bearer("right-token")).expect("responds");
90    assert_eq!(verify(&verifier, &correct, &ctx), AuthResult::Ok);
91    println!("[bearer] correct token     -> Ok");
92
93    let wrong = respond(&challenge, &ctx, Credentials::bearer("wrong-token")).expect("responds");
94    assert_eq!(verify(&verifier, &wrong, &ctx), AuthResult::Unauthorized);
95    println!("[bearer] wrong token       -> Unauthorized");
96}
More examples
Hide additional examples
examples/client_respond.rs (lines 20-24)
18fn main() {
19    // --- Basic (RFC 7617): a one-shot respond(), no session state needed.
20    let value = respond(
21        "Basic realm=\"cameras\"",
22        &RequestContext::new("GET", "/stream/media.m3u8"),
23        Credentials::new("admin", "hunter2"),
24    )
25    .expect("Basic responds to any challenge shape");
26    println!("[basic]  Authorization: {value}");
27    assert!(value.starts_with("Basic "));
28
29    // --- Digest (RFC 7616): parses the server's nonce/realm/qop out of the
30    // challenge, then computes HA1/HA2/response. Demonstrated with an
31    // Authenticator (not the one-shot respond()) since a real session reuses
32    // it across requests so the nonce count (`nc`) advances correctly.
33    let digest_challenge = "Digest realm=\"cameras\", \
34        nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", qop=\"auth\", algorithm=MD5";
35    let mut auth =
36        Authenticator::from_challenge(digest_challenge, Credentials::new("admin", "hunter2"))
37            .expect("challenge parses");
38    let value = auth
39        .authorization(&RequestContext::new(
40            "DESCRIBE",
41            "rtsp://camera.example.com/live",
42        ))
43        .expect("computes a Digest Authorization value");
44    println!("[digest] Authorization: {value}");
45    assert!(value.starts_with("Digest "));
46
47    // A second request on the same Authenticator advances `nc` — the
48    // computed value differs even though nothing else about the request
49    // changed (RFC 7616 §3.3 requires a fresh `nc` per request).
50    let second = auth
51        .authorization(&RequestContext::new(
52            "DESCRIBE",
53            "rtsp://camera.example.com/live",
54        ))
55        .expect("computes a second Digest Authorization value");
56    assert_ne!(value, second, "nc must advance across requests");
57    println!("[digest] Authorization (2nd request, nc advanced): {second}");
58
59    // --- Bearer (RFC 6750): no challenge round-trip needed at all — the
60    // challenge value is ignored, the token is sent verbatim.
61    let value = respond(
62        "ignored — Bearer needs no challenge round-trip",
63        &RequestContext::new("GET", "/stream/media.m3u8"),
64        Credentials::bearer("mytoken123"),
65    )
66    .expect("Bearer always responds");
67    println!("[bearer] Authorization: {value}");
68    assert_eq!(value, "Bearer mytoken123");
69}