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)