diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/login.html | 55 | ||||
-rw-r--r-- | src/login.py | 67 |
2 files changed, 122 insertions, 0 deletions
diff --git a/src/login.html b/src/login.html new file mode 100644 index 0000000..cb35622 --- /dev/null +++ b/src/login.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> + +<html> + <head> + <script> + addEventListener('load', () => { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + if (urlParams.has('err')) { + document.body.classList.add('err'); + } + }); + </script> + <style> + .errortext { + display: none; + color: red; + } + .err .errortext { + display: unset; + } + html { + height: 100dvh; + background-color: #EEE; + } + body { + height: 100%; + margin: 0px; + display: flex; + } + .content { + text-align: left; + margin: auto; + padding: 50px; + background-color: #CCC; + } + form > * { + display: block; + margin: 10px 0px; + } + </style> + </head> + <body> + <div class="content"> + <h1>Login Page</h1> + <form method="post" action="auth"> + <div><label for="user">Username: </label><input type="text" name="user"></div> + <div><label for="pass">Password: </label><input type="password" name="pass"></div> + <div><label for="remember">Remember Me: </label><input type="checkbox" name="remember"></div> + <input type="submit" value="Login"> + <div class="errortext">Could not verify login</div> + </form> + </div> + </body> +</html> diff --git a/src/login.py b/src/login.py new file mode 100644 index 0000000..2bfe825 --- /dev/null +++ b/src/login.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +from passlib.apache import HtpasswdFile +from flask import Flask, request, make_response, jsonify, redirect +import python_jwt as jwt +from jwcrypto.jwk import JWK +import datetime +from json import dumps +import argparse + +app = Flask(__name__) + +def authorized_request(): + args = request.form + + user = args['user'] if 'user' in args else None + pswd = args['pass'] if 'pass' in args else None + remember = args['remember'] if 'remember' in args else None + + valid = user is not None and pswd is not None + valid = valid and user in htpasswd.users() + valid = valid and htpasswd.check_password(user, pswd) + + return valid, user, pswd, remember + +@app.route('/auth', methods=['POST']) +def authorize(): + # on success, redirect to /. on failure, redirect to /login + auth, user, _, remember = authorized_request() + if auth: + resp = redirect('/') + if remember: + exp = None + else: + exp = datetime.timedelta(minutes=exptime) + token = jwt.generate_jwt({}, privkey, "EdDSA", exp) + resp.set_cookie('auth', token, max_age=exp) + return resp + # this stuff too + else: + resp = redirect('/login?err') + return resp + +@app.route('/logout', methods=['GET']) +def logout(): + resp = redirect('/login') + resp.delete_cookie('auth') + return resp + +if __name__ == '__main__': + # argparse arguments + parser = argparse.ArgumentParser( + prog='login.py', + description='A web server that handles htpasswd-file JWT auth logic') + parser.add_argument('htpasswd') + parser.add_argument('privkey') + parser.add_argument('-e', '--expireminutes', default=30, type=int) + + args = parser.parse_args() + htpasswd_filename = args.htpasswd + privkey_filename = args.privkey + exptime = args.expireminutes + + htpasswd = HtpasswdFile(htpasswd_filename) + with open(privkey_filename, 'rb') as privkey_file: + privkey = JWK() + privkey.import_from_pem(privkey_file.read()) + app.run(debug=True) |