lib.rs 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. //! This crate implements Digest Auth headers as specified by IETF RFCs 2069, 2617, and 7616.
  2. //! It can be used in conjunction with libraries like reqwest to access e.g. IP cameras
  3. //! that use this authentication scheme.
  4. //!
  5. //! This library was written for the http client, but since the algorithm is symmetrical,
  6. //! it can be used by the server side as well. Server-side nonce management (generation, timed
  7. //! expiry) and authorization checking is left to user's implementation.
  8. //!
  9. //! The `AuthorizationHeader::digest()` method can be used server-side to replicate the
  10. //! password/body hash; then just check if the computed digest matches what the user sent.
  11. //!
  12. //! # Examples
  13. //!
  14. //! Basic usage:
  15. //!
  16. //! ```
  17. //! use digest_auth::AuthContext;
  18. //!
  19. //! // Value from the WWW-Authenticate HTTP header (usually in a HTTP 401 response)
  20. //! let www_authenticate = r#"Digest realm="http-auth@example.org", qop="auth, auth-int", algorithm=MD5, nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS""#;
  21. //!
  22. //! // Prepare an authorization context. Note that this is a GET request. There are different
  23. //! // constructors available for POST or other request types. You can re-use it, but
  24. //! // it's cheap to create a fresh one each time, as the struct uses references only.
  25. //! let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html");
  26. //! // For this test, we inject a custom cnonce. It's generated for you otherwise
  27. //! // - you don't need `mut` in that case and needn't worry about this at all.
  28. //! context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ");
  29. //!
  30. //! // Parse the prompt header. You can inspect the parsed object, its fields are public.
  31. //! let mut prompt = digest_auth::parse(www_authenticate).unwrap();
  32. //!
  33. //! // Compute a value for the Authorization header that we'll send back to the server
  34. //! let answer = prompt.respond(&context).unwrap().to_string();
  35. //! assert_eq!(answer, r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, nc=00000001, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="8ca523f5e9506fed4657c9700eebdbec", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", algorithm=MD5"#);
  36. //!
  37. //! // The `prompt` variable is mutable, because the 'nc' counter (nonce reuse count)
  38. //! // is inside the struct and updated automatically.
  39. //!
  40. //! // You can re-use it for subsequent requests, assuming the server allows nonce re-use.
  41. //! // Some poorly implemented servers will reject it and give you 401 again, in which case
  42. //! // you should parse the new "WWW-Authenticate" header and use that instead.
  43. //!
  44. //! let answer2 = prompt.respond(&context).unwrap().to_string();
  45. //! // notice how the 'response' field changed - the 'nc' counter is included in the hash
  46. //! assert_eq!(answer2, r#"Digest username="Mufasa", realm="http-auth@example.org", nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", uri="/dir/index.html", qop=auth, nc=00000002, cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", response="4b5d595ecf2db9df612ea5b45cd97101", opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS", algorithm=MD5"#);
  47. //! ```
  48. mod digest;
  49. mod enums;
  50. mod error;
  51. pub use error::{Error, Result};
  52. pub use crate::digest::{AuthContext, AuthorizationHeader, WwwAuthenticateHeader};
  53. pub use crate::enums::*;
  54. /// Parse the WWW-Authorization header value.
  55. /// It's just a convenience method to call [`WwwAuthenticateHeader::parse()`](struct.WwwAuthenticateHeader.html#method.parse).
  56. pub fn parse(www_authorize: &str) -> Result<WwwAuthenticateHeader> {
  57. WwwAuthenticateHeader::parse(www_authorize)
  58. }
  59. #[cfg(test)]
  60. mod test {
  61. use crate::{AuthContext, Error};
  62. #[test]
  63. fn test_parse_respond() {
  64. let src = r#"
  65. Digest
  66. realm="http-auth@example.org",
  67. qop="auth, auth-int",
  68. algorithm=MD5,
  69. nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
  70. opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS"
  71. "#;
  72. let mut context = AuthContext::new("Mufasa", "Circle of Life", "/dir/index.html");
  73. context.set_custom_cnonce("f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ");
  74. let mut prompt = crate::parse(src).unwrap();
  75. let answer = prompt.respond(&context).unwrap();
  76. let str = answer.to_string().replace(", ", ",\n ");
  77. assert_eq!(
  78. str,
  79. r#"
  80. Digest username="Mufasa",
  81. realm="http-auth@example.org",
  82. nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
  83. uri="/dir/index.html",
  84. qop=auth,
  85. nc=00000001,
  86. cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ",
  87. response="8ca523f5e9506fed4657c9700eebdbec",
  88. opaque="FQhe/qaU925kfnzjCev0ciny7QMkPqMAFRtzCUYo5tdS",
  89. algorithm=MD5
  90. "#
  91. .trim()
  92. );
  93. }
  94. #[test]
  95. fn test_cast_error() {
  96. let _m: Box<dyn std::error::Error> = Error::UnknownAlgorithm("Uhhh".into()).into();
  97. }
  98. }