feat(security): add phase8 hardening with rate limits, audit logs, and auth-first simulator flow

This commit is contained in:
2026-01-24 18:45:00 +00:00
parent 6d6c77f77e
commit f6d66c3650
11 changed files with 355 additions and 5 deletions

View File

@@ -123,6 +123,25 @@
</p>
<div class="grid">
<section class="panel">
<h2>Auth</h2>
<label>Name</label>
<input id="authName" placeholder="optional display name" />
<label>Email</label>
<input id="authEmail" placeholder="you@example.com" />
<label>Password</label>
<input id="authPassword" type="password" placeholder="password" />
<div class="row">
<button id="signUpBtn" class="alt">Sign Up</button>
<button id="signInBtn">Sign In</button>
</div>
<div class="row">
<button id="sessionBtn" class="alt">Check Session</button>
<button id="signOutBtn" class="danger">Sign Out</button>
</div>
<pre id="authState"></pre>
</section>
<section class="panel">
<h2>Device Bootstrap</h2>
<label>Device Name</label>
@@ -186,6 +205,7 @@
<script src="/socket.io/socket.io.js"></script>
<script>
const state = {
session: null,
device: null,
deviceToken: null,
socket: null,
@@ -209,6 +229,7 @@
};
const render = () => {
$('authState').textContent = JSON.stringify({ session: state.session }, null, 2);
$('deviceState').textContent = JSON.stringify({ device: state.device, hasToken: Boolean(state.deviceToken) }, null, 2);
$('clientState').textContent = JSON.stringify({ lastStreamSessionId: state.lastStreamSessionId }, null, 2);
$('cameraState').textContent = JSON.stringify(
@@ -222,6 +243,64 @@
);
};
const getAuthPayload = () => ({
name: $('authName').value.trim() || undefined,
email: $('authEmail').value.trim(),
password: $('authPassword').value,
});
$('signUpBtn').addEventListener('click', async () => {
try {
const authPayload = getAuthPayload();
const payload = await authFetch('/api/auth/sign-up/email', {
method: 'POST',
body: JSON.stringify(authPayload),
});
log('sign up', payload);
$('sessionBtn').click();
} catch (error) {
log('sign up failed', { error: error.message });
}
});
$('signInBtn').addEventListener('click', async () => {
try {
const authPayload = getAuthPayload();
const payload = await authFetch('/api/auth/sign-in/email', {
method: 'POST',
body: JSON.stringify({ email: authPayload.email, password: authPayload.password }),
});
log('sign in', payload);
$('sessionBtn').click();
} catch (error) {
log('sign in failed', { error: error.message });
}
});
$('sessionBtn').addEventListener('click', async () => {
try {
const payload = await authFetch('/api/auth/get-session');
state.session = payload?.session ? payload : null;
render();
log('session check', payload);
} catch (error) {
state.session = null;
render();
log('session check failed', { error: error.message });
}
});
$('signOutBtn').addEventListener('click', async () => {
try {
const payload = await authFetch('/api/auth/sign-out', { method: 'POST', body: JSON.stringify({}) });
state.session = null;
render();
log('sign out', payload);
} catch (error) {
log('sign out failed', { error: error.message });
}
});
const authFetch = async (url, options = {}) => {
const response = await fetch(url, {
credentials: 'include',
@@ -337,6 +416,7 @@
$('registerBtn').addEventListener('click', async () => {
try {
if (!state.session) throw new Error('Authenticate first');
const role = $('role').value;
const name = $('deviceName').value.trim();
const payload = await authFetch('/devices/register', {
@@ -370,6 +450,24 @@
log('loaded saved device', parsed);
});
const auditPanel = document.createElement('section');
auditPanel.className = 'panel';
auditPanel.style.marginTop = '16px';
auditPanel.innerHTML = `
<h2>Audit Logs</h2>
<button id="fetchAuditBtn" class="alt">Fetch My Device Audit Logs</button>
`;
document.querySelector('.page').appendChild(auditPanel);
$('fetchAuditBtn').addEventListener('click', async () => {
try {
const payload = await deviceFetch('/audit/device');
log('audit logs', payload);
} catch (error) {
log('audit fetch failed', { error: error.message });
}
});
$('loadSavedBtn').addEventListener('click', async () => {
try {
if (!state.device?.id) return;