Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: HTTP Digest auth support #821

Open
majorcs opened this issue May 12, 2022 · 2 comments
Open

Feature request: HTTP Digest auth support #821

majorcs opened this issue May 12, 2022 · 2 comments

Comments

@majorcs
Copy link

majorcs commented May 12, 2022

It would be nice to support HTTP Digest authentication.

It could be easily implemented in the _handshake.py.handshake() by checking for the 401 status and the Authorization response header. I made a very dirty hack just to test it and works well (this supports only the most simple Digest auth).

--- _handshake.py	2022-05-11 11:24:42.203249509 +0200
+++ new/_handshake.py	2022-05-12 10:28:12.773080587 +0200
@@ -19,6 +19,7 @@
 import hashlib
 import hmac
 import os
+import www_authenticate
 from base64 import encodebytes as base64encode
 from http import client as HTTPStatus
 from ._cookiejar import SimpleCookieJar
@@ -33,7 +34,8 @@
 VERSION = 13
 
 SUPPORTED_REDIRECT_STATUSES = (HTTPStatus.MOVED_PERMANENTLY, HTTPStatus.FOUND, HTTPStatus.SEE_OTHER,)
-SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS,)
+SUPPORTED_UNAUTHORIZED_STATUSES = (HTTPStatus.UNAUTHORIZED,)
+SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + SUPPORTED_UNAUTHORIZED_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS,)
 
 CookieJar = SimpleCookieJar()
 
@@ -55,8 +57,30 @@
     dump("request header", header_str)
 
     status, resp = _get_resp_headers(sock)
+    if status in SUPPORTED_UNAUTHORIZED_STATUSES:
+        auth_header = www_authenticate.parse(resp.get('www-authenticate'))
+        digest = auth_header.get('digest')
+        if digest:
+            h1 = hashlib.md5(f"{username}:{digest['realm']}:{password}".encode('utf-8')).hexdigest()
+            h2 = hashlib.md5(f"GET:{url}".encode('utf-8')).hexdigest()
+            rs = h1 + ":" + digest['nonce'] + ":" + h2
+            response = hashlib.md5(rs.encode('utf-8')).hexdigest()
+
+            h = options.get('header', [])
+            h += [f'Authorization: Digest username="{username}",realm={digest["realm"]},nonce={digest["nonce"]},uri={url},nc=00000001,response={response}']
+            options['header'] = h
+            headers, key = _get_handshake_headers(resource, url, hostname, port, options)
+
+            header_str = "\r\n".join(headers)
+            send(sock, header_str)
+            dump("request header", header_str)
+
+            status, resp = _get_resp_headers(sock)
+            
+            
     if status in SUPPORTED_REDIRECT_STATUSES:
         return handshake_response(status, resp, None)
+        
     success, subproto = _validate(resp, key, options.get("subprotocols"))
     if not success:
         raise WebSocketException("Invalid WebSocket Header")
@engn33r
Copy link
Collaborator

engn33r commented May 12, 2022

Nice work! We can combine this with an older example from issue #470 and make a PR out of it. I am unlikely to prioritize this in the next few weeks since I am not using an HTTP auth proxy, so if someone wants to submit a PR, it would be very welcome.

@engn33r
Copy link
Collaborator

engn33r commented Aug 25, 2022

I don't often implement proxies with digest auth so I want to crowdsource an answer for which common proxy I should test the HTTP digest auth implementation against. Please add your comment here to vote for a proxy. I want to implement this feature in parallel with adding a proxy into the CI unit tests, because right now the CI does not use an actual proxy during testing and therefore the code coverage don't fully cover the proxy code, which should be fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants