Update HTTPDigest to match RFC 7616#9825
Update HTTPDigest to match RFC 7616#9825ordinary-jamie wants to merge 4 commits intofastapi:masterfrom
Conversation
4cd2a2b to
67dc386
Compare
67dc386 to
4d3cad0
Compare
HTTP digest access authentication requires the use of a nonce value to prevent replay attacks. This commit introduces a simple stateless nonce for that purpose, following the format recommended by RFC7616 (section 3.3) with a ':' delimited string of timestamp in miliseconds, resource, and server secret data. Note, the nonce utility hash-and-truncates the secret and request URL path to limit the size of the nonce to 48 chars. Also note, the request URL path is used to describe the resource as opposed to the recommended ETag for compatibility reasons.
RFC7616 (section 3.3) requires that "If a server receives a request for an access-protected object, and an acceptable Authorization header field is not sent, the server responds with a "401 Unauthorized" status code..."
This commit introduces a utility to calculate the HTTP digest access authentication consistent with the core elements in RFC 7616. This is an initial implementation of the utility, as many considerations will need to be considered for more a robust implementation, in particular quoted-string handling.
4d3cad0 to
54b1abc
Compare
This is an initial implementation of HTTP Digest Access Authentication consistent to [RFC 7616](https://datatracker.ietf.org/doc/html/rfc7616). As the RFC only provides guidance, and does not enforce all elements of the standard, this commit only implements the crucial aspects to ensure the challenge-response negotiation is at least usable. Notably, non-quoted usernames, and proxy-authentication is not yet supported by this change. Furthermore, this commit uses a stateless-Nonce generation as to avoid overcomplicating the design, future changes may wish to address this to allow servers to validate the nonce is indeed one-time usage.
54b1abc to
ba1a737
Compare
|
LGTM!! |
| self.auto_error = auto_error | ||
|
|
||
| async def __call__( | ||
| opqaue_data = realm.encode() if realm else os.urandom(32) |
There was a problem hiding this comment.
Is "opqaue_data" a typo for "opaque_data" ?
|
Reminder for Sebastián: we discussed this on July 18th on Slack |
|
This pull request has a merge conflict that needs to be resolved. |
|
Thanks a lot for all the effort put into this! 🙌 I'm conflicted about implementing HTTPDigest, having a lot of extra code that we then need to maintain, for a standard that is mostly obsolete. We added docs explaining that the current implementation is only a stub to wire up the OpenAPI but doesn't implement all the protocol. For now, I'll prefer to pass on this, and wait for users to come with a specific use case that defnitely requires HTTP Digest, and confirm that there are a notorious amount of users/use cases needing it before adding it to the code base. So, for now, I'll pass on this. Thanks again! 🍰 |
🎯 Aim
Closes #1476
🤨 Summary of changes
MD5(timestamp_in_ms:resource:secret). Note, the RFC recommends the header fieldETAGinstead ofresource/URI, however since there is no mentions ofETAGin FastAPI, the PR chooses to use the more readily availablerequest.url.path401 UNAUTHORISEDinstead of403 FORBIDDEN, see RFC 7616, sec 3.3 'the server responds with a "401 Unauthorized" status code and a WWW-Authenticate header field with Digest scheme'HTTPDigestclass to correctly follow the challenge-response framework laid out by RFC 7616.📋 Implementation notes
HTTPDigestCredentials: The resolved-dependency will be of typeHTTPDigestCredentials, which will have anauthenticate(username: str, password: str) -> boolmethod. Because the Digest response is a hashed field, the server can only authenticate a user by comparing the hashed credentials. This method makes this easier for developers.Not implemented
auth-int. However, this requires hashing the entity-body of the request for which we do not have readily available. "the hash of the entity body, not the message body -- it is computed before any transfer encoding is applied by the sender and after it has been removed by the recipient. Note that this includes multipart boundaries and embedded header fields in each part of any multipart content-type."ncfield to allow servers to detect request replays by maintaining its own copy of this count. However, because FastAPI is primarily for RESTful APIs, this PR chooses to not implement this as it would require stateful servers.username*digest field. This PR does not support this.✅ Testing: Manually with CURL
We can validate this implementation is consistent with the RFC by using
curl --digest.First we create a simple server:
Then CURL the running server
References