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
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}