CODE-EXPLAINED.md 4.4 KB

Some explanation on components used for the authentication proxy...

Show simple page

Creates an object of the class HTTPServer which takes as parameter:

  • a tuple of (host, port) to bind. Empty host binds to all interfaces.
  • an object of the class BaseHTTPRequestHandlerto to answer http requests, e.g. GET, POST, ...
from http.server import BaseHTTPRequestHandler, HTTPServer
httpd = HTTPServer(('', 8000), BaseHTTPRequestHandler)
httpd.serve_forever()

To actually answer http requests, a new class has to be defined which inherits BaseHTTPRequestHandler an overwrites the functions to handle different http request types such a GET, POST, ...

from http.server import BaseHTTPRequestHandler, HTTPServer

HOST='localhost'
PORT=8000

s = """<html>
<head><title>hello, world</title></head>
<body>hello, world</body>
</html>"""

class OtpRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200) #OK
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(bytes(s, "utf-8"))

httpd = HTTPServer((HOST, PORT), OtpRequestHandler)
httpd.serve_forever()

To test: curl http://localhost:8000/

Print POST data

  • To read data received via post we need to obtain the length of the post data from the http-header first.
  • Form data is sent with the content-type application/x-www-form-urlencoded
from http.server import BaseHTTPRequestHandler, HTTPServer

class OtpRequestHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers['Content-Length'])
        print(self.headers['content-type'])
        print(self.rfile.read(length))
        
httpd = HTTPServer(('localhost', 8000), OtpRequestHandler)
httpd.serve_forever()

To Test: curl -d "hello, world" -X POST http://localhost:8000/

Parse HTML form data

  • parse_qs parses the form data into a dictionary.
  • Form values are stored as list in the dicionary, which has to be converted to a string
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs

class OtpRequestHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers['Content-Length'])
        parms = parse_qs(self.rfile.read(length).decode("utf-8"))
        print("User: " + ''.join(parms["user"]) )
        print("OTP: "  + ''.join(parms["otp"]) )

httpd = HTTPServer(('localhost', 8000), OtpRequestHandler)
httpd.serve_forever()

To Test: curl -d "user=jdoe&otp=204177" -X POST http://localhost:8000/

Set Cookie

from http.server import BaseHTTPRequestHandler, HTTPServer
from http.cookies import SimpleCookie
import secrets

cookie = SimpleCookie()
cookie['token'] = secrets.token_urlsafe(16)
#cookie["token"]["domain"] = "s-up.net"
cookie["token"]["path"] = "/"
#cookie["token"]["secure"] = True
cookie["token"]["httponly"] = True
cookies.Morsel._reserved.setdefault('samesite', 'SameSite')
cookie["token"]["samesite"] = "strict"
cookie["token"]["expires"] = 60 * 60 * 6 # 6h

class OtpRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200) #OK
        self.send_header("Content-type", "text/html")
        self.send_header('Set-Cookie', cookie.output(header=''))
        self.end_headers()
        self.wfile.write(bytes("Hello", "utf-8"))

httpd = HTTPServer(('localhost', 8000), OtpRequestHandler)
httpd.serve_forever()

Get Cookie

from http.server import BaseHTTPRequestHandler, HTTPServer
from http.cookies import SimpleCookie
import secrets

class OtpRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200) #OK
        self.send_header("Content-type", "text/html")
        self.end_headers()
        cookies = SimpleCookie(self.headers.get('Cookie'))
        token = cookies['token'].value
        self.wfile.write(bytes(token, "utf-8"))
        print(token)

httpd = HTTPServer(('localhost', 8000), OtpRequestHandler)
httpd.serve_forever()

TOTP

import pyotp
import time

# Get a random secret
secret=pyotp.random_base32()
print(secret)

# Calculate OTP
otp=pyotp.TOTP(secret).now()
print(otp)

# Verify OTP, should be true
pyotp.TOTP(secret).verify(otp)

# Verify OTP again after 30s, should be false
time.sleep(30)
pyotp.TOTP(secret).verify(otp)

References