feat(launchpad): implement secure email hashing for access control

This commit introduces a more secure method for verifying user
access to the internal launchpad by hashing email addresses.

- Replaces the plain-text email list with SHA-256 hashes.
- Adds a script to generate these hashes from `iap-users.txt`.
- Updates the launchpad HTML to hash the input email and compare
 it against the `allowed-hashes.json` file.
- Updates Makefile to generate hashes before deploy and serve.
- Adds .keep file for krow_client folder.
This commit is contained in:
bwnyasse
2026-01-10 11:48:54 -05:00
parent ba938be7ad
commit a5a4eae255
7 changed files with 59 additions and 11 deletions

View File

@@ -0,0 +1,5 @@
[
"1b2e22bdec8f6493bf71ee535b6db6b4b5cd2d373f0ffb25524e229f3b5b7f5f",
"e075ff357ef35be2d55b0e383d59c5256980c492ada7ab84c84b2bb5ac26a73f",
"994b31c1aef3d59fe59bc3b8e1dec860a6fb3c73cbf41bdf45028e2c1ecbcf7a"
]

View File

@@ -5,4 +5,10 @@
# Both internal (@krowwithus.com) and external emails are supported.
user:admin@krowwithus.com
# External users - Oloodi employees
user:boris@oloodi.com
user:achintha.isuru@oloodi.com
# External users - Legendary employees

View File

@@ -398,19 +398,20 @@
async function checkAccess(email) {
try {
// Fetch the whitelist
const res = await fetch('iap-users.txt');
// 1. Fetch the secure list of hashes
const res = await fetch('allowed-hashes.json');
if (!res.ok) return false;
const text = await res.text();
const allowedHashes = await res.json();
// Parse lines
const lines = text.split('\n');
const allowedEmails = lines
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'))
.map(line => line.replace(/^user:/, '').trim());
return allowedEmails.includes(email);
// 2. Hash the user's email (SHA-256)
const normalizedEmail = email.trim().toLowerCase();
const msgBuffer = new TextEncoder().encode(normalizedEmail);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
// 3. Compare
return allowedHashes.includes(hashHex);
} catch (e) {
console.error("Failed to check access list:", e);
return false;