Some explanation on components used for the authentication proxy... ### Show simple page Creates an object of the class [HTTPServer](https://docs.python.org/3/library/http.server.html) 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 = """ hello, world hello, world """ 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 - [Online TOTP Generator](https://totp.danhersam.com/) - [simpleotp on Github](https://github.com/newhouseb/simpleotp)