fdsafs
This commit is contained in:
+141
-17
@@ -84,6 +84,9 @@
|
||||
this.rawSeed = null;
|
||||
this.isSessionReady = false;
|
||||
this.hwFingerprint = null;
|
||||
try {
|
||||
this.hwFingerprint = localStorage.getItem('f0ck_anon_hw_fp') || null;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +95,13 @@
|
||||
*/
|
||||
async getHardwareFingerprint() {
|
||||
if (this.hwFingerprint) return this.hwFingerprint;
|
||||
try {
|
||||
const cached = localStorage.getItem('f0ck_anon_hw_fp');
|
||||
if (cached) {
|
||||
this.hwFingerprint = cached;
|
||||
return this.hwFingerprint;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
let glVendor = '';
|
||||
@@ -117,16 +127,67 @@
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// WebGPU adapter architecture (modern GPUs)
|
||||
let gpuArch = '';
|
||||
try {
|
||||
if (navigator.gpu) {
|
||||
const adapter = await navigator.gpu.requestAdapter();
|
||||
if (adapter && adapter.info) {
|
||||
gpuArch = [adapter.info.architecture, adapter.info.vendor, adapter.info.description].filter(Boolean).join(':');
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// CPU & Memory
|
||||
const concurrency = navigator.hardwareConcurrency || 0;
|
||||
const memory = navigator.deviceMemory || 0;
|
||||
const platform = navigator.platform || '';
|
||||
|
||||
// Display, Gamut & Dynamic Range
|
||||
const screenInfo = [
|
||||
window.screen ? window.screen.width : 0,
|
||||
window.screen ? window.screen.height : 0,
|
||||
window.screen ? window.screen.availWidth : 0,
|
||||
window.screen ? window.screen.availHeight : 0,
|
||||
window.screen ? window.screen.colorDepth : 0,
|
||||
window.screen ? window.screen.pixelDepth : 0,
|
||||
window.devicePixelRatio || 1
|
||||
window.devicePixelRatio || 1,
|
||||
window.matchMedia && window.matchMedia('(color-gamut: p3)').matches ? 'p3' : (window.matchMedia && window.matchMedia('(color-gamut: rec2020)').matches ? 'rec2020' : 'srgb'),
|
||||
window.matchMedia && window.matchMedia('(dynamic-range: high)').matches ? 'hdr' : 'sdr'
|
||||
].join('x');
|
||||
|
||||
// Input & Peripheral Hardware (touch, mouse, stylus)
|
||||
const touchPoints = (typeof navigator !== 'undefined' && 'maxTouchPoints' in navigator) ? navigator.maxTouchPoints : 0;
|
||||
const pointerType = (window.matchMedia && window.matchMedia('(pointer: fine)').matches) ? 'fine' : ((window.matchMedia && window.matchMedia('(pointer: coarse)').matches) ? 'coarse' : 'none');
|
||||
const hoverType = (window.matchMedia && window.matchMedia('(hover: hover)').matches) ? 'hover' : 'none';
|
||||
const inputHardware = `${touchPoints}:${pointerType}:${hoverType}`;
|
||||
|
||||
// 2D Canvas Font Rasterizer & Subpixel Geometry
|
||||
let canvasFp = '';
|
||||
try {
|
||||
const c2d = document.createElement('canvas');
|
||||
c2d.width = 240;
|
||||
c2d.height = 60;
|
||||
const ctx = c2d.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.textBaseline = 'alphabetic';
|
||||
ctx.fillStyle = '#f60';
|
||||
ctx.fillRect(10, 5, 60, 20);
|
||||
ctx.fillStyle = '#069';
|
||||
ctx.font = '14pt Arial, sans-serif';
|
||||
ctx.fillText('f0ck.dev <canvas> 😃', 4, 35);
|
||||
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
|
||||
ctx.font = '16pt Times, serif';
|
||||
ctx.fillText('f0ck.dev <canvas> 😃', 20, 48);
|
||||
const imgData = ctx.getImageData(0, 0, 240, 60).data;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < imgData.length; i += 4) {
|
||||
sum = (sum * 31 + imgData[i] + imgData[i+1] + imgData[i+2] + imgData[i+3]) >>> 0;
|
||||
}
|
||||
canvasFp = sum.toString(16);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// Web Audio DSP Floating-Point Math
|
||||
let audioFp = '';
|
||||
try {
|
||||
const AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
|
||||
@@ -159,14 +220,26 @@
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// FPU Math Microarchitecture Precision
|
||||
const mathPrecision = [
|
||||
Math.tan(-1e300).toString().substring(0, 10),
|
||||
Math.sinh(1).toString().substring(0, 10),
|
||||
Math.acos(0.123456789).toString().substring(0, 10)
|
||||
].join(';');
|
||||
|
||||
const rawHardware = [
|
||||
glVendor,
|
||||
glRenderer,
|
||||
glLimits,
|
||||
gpuArch,
|
||||
concurrency,
|
||||
memory,
|
||||
platform,
|
||||
screenInfo,
|
||||
audioFp
|
||||
inputHardware,
|
||||
canvasFp,
|
||||
audioFp,
|
||||
mathPrecision
|
||||
].join('~~~');
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
@@ -174,6 +247,9 @@
|
||||
const hashBuf = await window.crypto.subtle.digest('SHA-256', data);
|
||||
const hashHex = bytesToHex(new Uint8Array(hashBuf));
|
||||
this.hwFingerprint = `HW:${hashHex}`;
|
||||
try {
|
||||
localStorage.setItem('f0ck_anon_hw_fp', this.hwFingerprint);
|
||||
} catch (e) {}
|
||||
return this.hwFingerprint;
|
||||
} catch (err) {
|
||||
console.warn('[ANON_SSH] Failed to compute hardware fingerprint:', err);
|
||||
@@ -483,7 +559,7 @@
|
||||
localStorage.setItem(STORAGE_KEY_PUB, this.pubkey);
|
||||
localStorage.setItem(STORAGE_KEY_FP, this.fingerprint);
|
||||
|
||||
await this.ensureSession(true);
|
||||
await this.ensureSession(true, isExplicitLogin);
|
||||
return this.getIdentity();
|
||||
}
|
||||
|
||||
@@ -514,7 +590,11 @@
|
||||
/**
|
||||
* Authenticate to backend and establish/refresh anonymous session
|
||||
*/
|
||||
async ensureSession(force = false) {
|
||||
async ensureSession(force = false, isExplicitLogin = false) {
|
||||
if (window.location.pathname === '/banned') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If user is already logged in as a real registered user, don't overwrite session!
|
||||
if (window.f0ckSession && window.f0ckSession.user && !window.f0ckSession.is_anon) {
|
||||
return;
|
||||
@@ -550,8 +630,15 @@
|
||||
reason: data.reason,
|
||||
expires: data.expires
|
||||
});
|
||||
if (window.location.pathname !== '/banned') {
|
||||
window.location.href = data.redirect || '/banned';
|
||||
this.clearStoredIdentity();
|
||||
this.updateNavUI(false);
|
||||
// ONLY redirect to /banned if user was explicitly trying to login!
|
||||
if (isExplicitLogin) {
|
||||
const anonModal = document.getElementById('anon-ssh-modal');
|
||||
if (anonModal) anonModal.style.display = 'none';
|
||||
if (window.location.pathname !== '/banned') {
|
||||
window.location.href = data.redirect || '/banned';
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -594,6 +681,9 @@
|
||||
setTombstone(tombstone) {
|
||||
try {
|
||||
localStorage.setItem('f0ck_anon_tombstone', JSON.stringify(tombstone));
|
||||
if (tombstone && tombstone.banned) {
|
||||
document.cookie = `f0ck_banned=${encodeURIComponent(JSON.stringify(tombstone))}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -653,6 +743,7 @@
|
||||
localStorage.removeItem(STORAGE_KEY_PRIV);
|
||||
localStorage.removeItem(STORAGE_KEY_PUB);
|
||||
localStorage.removeItem(STORAGE_KEY_FP);
|
||||
document.cookie = 'f0ck_banned=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
|
||||
this.cryptoKey = null;
|
||||
this.rawSeed = null;
|
||||
this.rawPub = null;
|
||||
@@ -669,11 +760,27 @@
|
||||
if (window.f0ckSession && window.f0ckSession.enable_anonymous_access === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If client has a ban tombstone, redirect to /banned directly on login attempt
|
||||
const tombstone = this.getTombstone();
|
||||
if (tombstone && tombstone.banned) {
|
||||
const anonModal = document.getElementById('anon-ssh-modal');
|
||||
if (anonModal) anonModal.style.display = 'none';
|
||||
if (window.location.pathname !== '/banned') {
|
||||
window.location.href = '/banned';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!this.pubkey) {
|
||||
await this.generateIdentity();
|
||||
await this.generateIdentity(true);
|
||||
} else {
|
||||
await this.ensureSession(true, true);
|
||||
}
|
||||
await this.ensureSession(true);
|
||||
|
||||
if (!this.isSessionReady) return;
|
||||
|
||||
// Sync any guest favorites saved in localStorage to this anon account
|
||||
if (window.f0ckGuestFavs && typeof window.f0ckGuestFavs.importToAccount === 'function') {
|
||||
await window.f0ckGuestFavs.importToAccount();
|
||||
@@ -698,12 +805,16 @@
|
||||
this.clearStoredIdentity();
|
||||
await fetch('/api/v2/anon/logout', { method: 'POST', credentials: 'same-origin' }).catch(() => {});
|
||||
} finally {
|
||||
window.location.href = '/';
|
||||
this.updateNavUI(false);
|
||||
if (typeof window.showToastNotification === 'function') {
|
||||
window.showToastNotification('Logged out from anonymous session');
|
||||
}
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update visitor navbar UI elements
|
||||
* Update navigation bar elements based on anonymous authentication state
|
||||
*/
|
||||
updateNavUI(isAnon) {
|
||||
const icon = document.getElementById('nav-visitor-icon');
|
||||
@@ -765,8 +876,12 @@
|
||||
* Initialize on page startup
|
||||
*/
|
||||
async init() {
|
||||
if (window.location.pathname === '/banned') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.f0ckSession && window.f0ckSession.enable_anonymous_access === false) {
|
||||
if (window.f0ckSession.is_anon) {
|
||||
if (window.f0ckSession.is_anon && window.f0ckSession.logged_in) {
|
||||
await fetch('/api/v2/anon/logout', { method: 'POST', credentials: 'same-origin' }).catch(() => {});
|
||||
window.location.reload();
|
||||
return;
|
||||
@@ -780,6 +895,15 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// If client has a ban tombstone, do NOT attempt background session handshake on page load/reload!
|
||||
const tombstone = this.getTombstone();
|
||||
if (tombstone && tombstone.banned) {
|
||||
this.clearStoredIdentity();
|
||||
this.updateNavUI(false);
|
||||
this.attachUIListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
const storedPriv = localStorage.getItem(STORAGE_KEY_PRIV);
|
||||
const storedPub = localStorage.getItem(STORAGE_KEY_PUB);
|
||||
const storedFp = localStorage.getItem(STORAGE_KEY_FP);
|
||||
@@ -808,12 +932,12 @@
|
||||
|
||||
// User previously logged in as anonymous with this key
|
||||
// If backend session is already active, avoid redundant session churn
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon) {
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon && window.f0ckSession.logged_in) {
|
||||
this.isSessionReady = true;
|
||||
this.updateNavUI(true);
|
||||
} else {
|
||||
await this.ensureSession();
|
||||
this.updateNavUI(true);
|
||||
// Unauthenticated guest state: do NOT auto-login as anonymous on page load!
|
||||
this.updateNavUI(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[ANON_SSH] Failed to restore stored key:', err);
|
||||
@@ -822,7 +946,7 @@
|
||||
}
|
||||
} else {
|
||||
// Clean guest state! Do NOT auto-generate or establish session!
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon) {
|
||||
if (window.f0ckSession && window.f0ckSession.is_anon && window.f0ckSession.logged_in) {
|
||||
// Stale backend anon session cookie without corresponding local key: clear cookie
|
||||
await fetch('/api/v2/anon/logout', { method: 'POST', credentials: 'same-origin' }).catch(() => {});
|
||||
window.location.reload();
|
||||
@@ -853,7 +977,7 @@
|
||||
}
|
||||
|
||||
const logoutTarget = e.target.closest('#nav-anon-logout-btn, a[href="/logout"]');
|
||||
if (logoutTarget && ((window.f0ckSession && window.f0ckSession.is_anon) || this.pubkey)) {
|
||||
if (logoutTarget && ((window.f0ckSession && window.f0ckSession.is_anon && window.f0ckSession.logged_in) || this.pubkey)) {
|
||||
e.preventDefault();
|
||||
this.logoutAnonymous();
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user