Search code examples
pythonhttpsocketsauthenticationconfirmation

How to confirm user/pass for http authorization?


A server listens packet and we send http GET request packet to this listener.If we use auth header with username/pass server does not accept connection and it fails.Is there any way to parse this auth header info (username/pass) on listener ? Because we want to perform authentication based on user/pass comparison

NOTE : Without auth header in GET packet http listener accept connection and it works fine

HTTP PACKET LISTENER

import socket

serverSocket = socket(AF_INET, SOCK_STREAM)
serverPort = 8080
serverSocket.bind(("127.0.0.1", serverPort))
serverSocket.listen(1)

while True:
    print('Ready to serve...')
    try :
         connectionSocket, addr = serverSocket.accept()
    except :
        print (f"Socket error occured for 127.0.0.1 {serverPort} ")

HTTP CLIENT

import requests
from requests.auth import HTTPBasicAuth

r = requests.get('http://127.0.0.1:8080',auth = HTTPBasicAuth('user', 'pass'))

Thank you for your helps !


Solution

  • Here is a working example of what you need.

    tl;dr: as pointed out in comments, with sockets you are working at the transport level. The HTTP Basic Auth lies at a higher level in the TCP/IP (or OSI) stack. If you do not want to embrace the HTTP protocol (do you?), you need to process requests and headers manually, mimicking the HTTP protocol. Indeed, python requests manages full-fledged HTTP requests.

    I slightly modified your code to parse http headers and to manage a HTTP-like auth. There you go (comments and explanation in the code):

    import socket, base64
    from http.server import BaseHTTPRequestHandler
    from io import BytesIO
    
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverPort = 8080
    serverSocket.bind(("127.0.0.1", serverPort))
    serverSocket.listen(1)
    
    # Auth section
    user = 'user'
    password = 'pass'
    # The token you want the client to provide (string)
    server_token = base64.b64encode(bytes(f'{user}:{password}','utf-8')).decode('utf-8')
    
    
    # Use this simple class to parse you HTTP headers
    # Read more here: https://stackoverflow.com/a/5955949/4820341
    class HTTPRequest(BaseHTTPRequestHandler):
        def __init__(self, request_text):
            self.rfile = BytesIO(request_text)
            self.raw_requestline = self.rfile.readline()
            self.error_code = self.error_message = None
            self.parse_request()
    
        def send_error(self, code, message):
            self.error_code = code
            self.error_message = message
    
    
    
    while True:
        print('Ready to serve...')
    
        connectionSocket, addr = serverSocket.accept()
    
        data = connectionSocket.recv(1024)
    
        # Those are your data coming from the client
        print(data.decode('utf-8'))
    
        # parse your headers
        http_headers = HTTPRequest(data)
    
        try:
    
            # get the incoming auth token
            client_token = http_headers.headers['Authorization'].strip('Basic ')
    
            if server_token != client_token:
                connectionSocket.sendall(bytes("HTTP/1.1 401 Unauthorized\n\n" + 'Wrong credetials', 'utf-8'))
            else:
                # process the request and do your stuff here
                connectionSocket.sendall(bytes("HTTP/1.1 200 OK\n\n" + 'Ok, all is fine here', 'utf-8'))
    
        except AttributeError:
            connectionSocket.sendall(bytes("HTTP/1.1 401 Unauthorized\n\n" + 'No credentials provided', 'utf-8'))
    
        finally:
            connectionSocket.close()
    

    Here is how a requests.get with auth looks like server side:

    Ready to serve...
    GET / HTTP/1.1
    Host: 127.0.0.1:8080
    User-Agent: python-requests/2.26.0
    Accept-Encoding: gzip, deflate
    Accept: */*
    Connection: keep-alive
    Authorization: Basic dXNlcjpwYXNz
    

    And now, let's see it in action:

    >>> r = requests.get('http://127.0.0.1:8080',auth = HTTPBasicAuth('user', 'pass'))
    >>> r.status_code
    200
    >>> r.text
    'Ok, all is fine here'
    >>>
    >>>
    >>> r = requests.get('http://127.0.0.1:8080',auth = HTTPBasicAuth('user', 'wrongpass'))
    >>> r.status_code
    401
    >>> r.text
    'wrong credentials'
    >>>
    >>>
    >>> r = requests.get('http://127.0.0.1:8080')
    >>> r.status_code
    401
    >>> r.text
    'No credentials provided'