Files
session-security-poc/sspoc/static/js/sspoc.js
Walter Oggioni 852c3e82fc added more users and anonymous API
reduced time window to 3 seconds
2024-02-14 13:29:57 +08:00

144 lines
4.4 KiB
JavaScript

function integerToBytes(integer, numBytes) {
const bytesArray = new Array(numBytes).fill(0);
for (let i = 0; i < numBytes; i++) {
bytesArray[numBytes - i - 1] = integer & 0xff;
integer >>= 8;
}
return Uint8Array.from(bytesArray);
}
function concatenateUInt8Arrays(array1, array2) {
const newArray = new Uint8Array(array1.length + array2.length);
newArray.set(array1);
newArray.set(array2, array1.length);
return newArray;
}
/*
Convert an ArrayBuffer into a string
from https://developer.chrome.com/blog/how-to-convert-arraybuffer-to-and-from-string/
*/
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
/*
Export the given key and write it into the "exported-key" space.
*/
async function exportCryptoKeyPem(key) {
const exported = await window.crypto.subtle.exportKey("spki", key);
const exportedAsString = ab2str(exported);
const exportedAsBase64 = btoa(exportedAsString);
return exportedAsBase64;
}
async function exportCryptoKey(key) {
const exported = await window.crypto.subtle.exportKey("jwk", key);
return JSON.stringify(exported, null, " ");
}
async function createKeyPair() {
const crypto = window.crypto.subtle
const keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([4, 0, 1]),
hash: "SHA-256",
},
true,
["encrypt", "decrypt"]
);
return keyPair;
}
let keyPair = createKeyPair();
let loginForm = document.getElementById('login-form');
let loginButton = document.getElementById('login-button');
let nonce = null
loginButton.addEventListener('click', async evt => {
const publicKey = (await keyPair).publicKey;
const publicKeyPem = await exportCryptoKeyPem(publicKey);
fetch('api/login', {
method: 'POST',
headers: {
'public-key' : publicKeyPem,
'Content-Type': 'application/json'
},
body: JSON.stringify({ username: loginForm.username.value, password: loginForm.password.value})
}).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 encryptedNonce = atob(nonceHeader);
const privateKey = (await keyPair).privateKey;
const crypto = window.crypto.subtle;
const encryptedBuffer = Uint8Array.from(atob(nonceHeader), c => c.charCodeAt(0));
nonce = await crypto.decrypt({ name: "RSA-OAEP" }, privateKey, encryptedBuffer)
.then(it => new Uint8Array(it));
});
});
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');
paragraph.textContent = text;
document.body.appendChild(paragraph);
});
});
let helloButton = document.createElement('button');
helloButton.textContent = 'hello'
div.appendChild(helloButton);
helloButton.addEventListener('click', async evt => {
const token = await computeToken();
let headers = {};
if (token != null) {
headers = {
'x-token': token
};
}
fetch('api/hello', {
method: 'GET',
headers
}).then(response => response.text()).then(text => {
let paragraph = document.createElement('p');
paragraph.textContent = text;
document.body.appendChild(paragraph);
});
});