added more users and anonymous API

reduced time window to 3 seconds
This commit is contained in:
Walter Oggioni
2024-02-14 13:29:57 +08:00
parent 68c41b9b95
commit 852c3e82fc
2 changed files with 73 additions and 18 deletions

View File

@@ -23,7 +23,10 @@ class SessionData:
app = Flask(__name__) app = Flask(__name__)
users = { users = {
"user": "password", "user1": "password",
"user2": "password",
"user3": "password",
"user4 ": "password",
} }
sessions: Dict[bytes, SessionData] = dict() sessions: Dict[bytes, SessionData] = dict()
@@ -67,8 +70,9 @@ def token_required(f):
response.status = 401 response.status = 401
response.data = "Token is invalid" response.data = "Token is invalid"
return response return response
current_tick: int = int(time()) // 10 current_tick: int = int(time()) // 3
valid_tokens = [ valid_tokens = [
sha256(session.nonce + (current_tick + 1).to_bytes(8)).digest(),
sha256(session.nonce + current_tick.to_bytes(8)).digest(), sha256(session.nonce + current_tick.to_bytes(8)).digest(),
sha256(session.nonce + (current_tick - 1).to_bytes(8)).digest() sha256(session.nonce + (current_tick - 1).to_bytes(8)).digest()
] ]
@@ -86,20 +90,34 @@ def login():
response = flask.Response() response = flask.Response()
if request.headers.get('Content-Type') != 'application/json': if request.headers.get('Content-Type') != 'application/json':
response.status = 415 response.status = 415
response.data = "Wrong request content type"
return response return response
payload = json.loads(request.data) payload = json.loads(request.data)
user = payload.get('username') user = payload.get('username')
if not user: if not user:
response.status = 401 response.status = 401
response.data = "Missing username from request"
return response return response
password = users.get(user) password = users.get(user)
if not password or password != payload.get('password'): if not password:
response.status = 401 response.status = 401
response.data = "Wrong username"
return response return response
suppliedPassword = payload.get('password')
if not suppliedPassword:
response.status = 401
response.data = "Missing password from request"
return response
elif suppliedPassword != password:
response.status = 401
response.data = "Wrong password"
return response
sr = random.SystemRandom() sr = random.SystemRandom()
nonce = sr.randbytes(16) nonce = sr.randbytes(16)
public_key_header = request.headers.get('public-key', None) public_key_header = request.headers.get('public-key', None)
if not public_key_header: if not public_key_header:
response.data = "Missing public key header"
response.status = 400 response.status = 400
return response return response
pem_key = f'-----BEGIN PUBLIC KEY-----\n{public_key_header}\n-----END PUBLIC KEY-----\n' pem_key = f'-----BEGIN PUBLIC KEY-----\n{public_key_header}\n-----END PUBLIC KEY-----\n'
@@ -142,11 +160,14 @@ def send_css(path):
return send_from_directory('static/css', path) return send_from_directory('static/css', path)
@app.route('/api/hello') @app.route('/api/whoami')
@token_required @token_required
def send_hello(user): def whoami(user):
return f'hello {user}' return f'hello {user}'
@app.route('/api/hello')
def hello():
return 'hello anonymous'
def main(): def main():
app.run(host='0.0.0.0', port=1443, ssl_context='adhoc') app.run(host='0.0.0.0', port=1443, ssl_context='adhoc')

View File

@@ -67,6 +67,11 @@ loginButton.addEventListener('click', async evt => {
}, },
body: JSON.stringify({ username: loginForm.username.value, password: loginForm.password.value}) body: JSON.stringify({ username: loginForm.username.value, password: loginForm.password.value})
}).then(async response => { }).then(async response => {
if (!response.ok) {
let paragraph = document.createElement('p');
paragraph.textContent = await response.text();
document.body.appendChild(paragraph);
}
const nonceHeader = response.headers.get('nonce'); const nonceHeader = response.headers.get('nonce');
const encryptedNonce = atob(nonceHeader); const encryptedNonce = atob(nonceHeader);
const privateKey = (await keyPair).privateKey; const privateKey = (await keyPair).privateKey;
@@ -74,26 +79,55 @@ loginButton.addEventListener('click', async evt => {
const encryptedBuffer = Uint8Array.from(atob(nonceHeader), c => c.charCodeAt(0)); const encryptedBuffer = Uint8Array.from(atob(nonceHeader), c => c.charCodeAt(0));
nonce = await crypto.decrypt({ name: "RSA-OAEP" }, privateKey, encryptedBuffer) nonce = await crypto.decrypt({ name: "RSA-OAEP" }, privateKey, encryptedBuffer)
.then(it => new Uint8Array(it)); .then(it => new Uint8Array(it));
return response.text(); });
}).then(text => { });
async function computeToken() {
if(nonce != null) {
const crypto = window.crypto.subtle;
const epochTick = Math.floor(new Date().getTime() / 3000);
const data = concatenateUInt8Arrays(nonce, integerToBytes(epochTick, 8));
const hash = new Uint8Array(await crypto.digest("SHA-256", data));
const token = btoa(Array.from(hash, byte => String.fromCharCode(byte)).join(''));
return token;
} else {
return null;
}
}
let div = document.createElement('div');
document.body.appendChild(div);
let whoamiButton = document.createElement('button');
whoamiButton.textContent = 'whoami'
div.appendChild(whoamiButton);
whoamiButton.addEventListener('click', async evt => {
const token = await computeToken();
let headers = {};
if (token != null) {
headers = {
'x-token': token
};
}
fetch('api/whoami', {
method: 'GET',
headers
}).then(response => response.text()).then(text => {
let paragraph = document.createElement('p'); let paragraph = document.createElement('p');
paragraph.textContent = text; paragraph.textContent = text;
document.body.appendChild(paragraph); document.body.appendChild(paragraph);
}); });
}); });
let button = document.createElement('button'); let helloButton = document.createElement('button');
button.textContent = 'Press me' helloButton.textContent = 'hello'
document.body.appendChild(button); div.appendChild(helloButton);
button.addEventListener('click', async evt => { helloButton.addEventListener('click', async evt => {
let header = {}; const token = await computeToken();
if(nonce != null) { let headers = {};
const crypto = window.crypto.subtle; if (token != null) {
const epochTick = Math.floor(new Date().getTime() / 10000)
const data = concatenateUInt8Arrays(nonce, integerToBytes(epochTick, 8))
const hash = new Uint8Array(await crypto.digest("SHA-256", data));
const token = btoa(Array.from(hash, byte => String.fromCharCode(byte)).join(''));
headers = { headers = {
'x-token': token 'x-token': token
}; };