diff --git a/web/src/App.tsx b/web/src/App.tsx index d058e48..e851183 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,7 +1,14 @@ -import type { FC } from "react"; +import { useEffect, useState, type FC } from "react"; +import { getDeviceId } from "./util/deviceId"; const App: FC = () => { - return
Hello
; + const [deviceId, setDeviceId] = useState(""); + + useEffect(() => { + getDeviceId().then((id) => setDeviceId(id)); + }, []); + + return
{deviceId}
; }; export default App; diff --git a/web/src/util/deviceId.ts b/web/src/util/deviceId.ts new file mode 100644 index 0000000..8aa4134 --- /dev/null +++ b/web/src/util/deviceId.ts @@ -0,0 +1,31 @@ +export const getDeviceId = async () => { + const existing = localStorage.getItem("guard-device-id"); + if (existing) return existing; + + const fingerprintParts = [ + navigator.userAgent, // Browser and OS + screen.width + "x" + screen.height, // Screen resolution + screen.colorDepth, // Color depth + Intl.DateTimeFormat().resolvedOptions().timeZone, // Time zone + navigator.platform, // OS platform + navigator.hardwareConcurrency, // Number of CPU cores + navigator.language, // Primary language + navigator.maxTouchPoints, // Touch capability + ]; + console.log(fingerprintParts); + + const rawFingerprint = fingerprintParts.join("|"); + const encoder = new TextEncoder(); + const data = encoder.encode(rawFingerprint); + + const hashBuffer = await crypto.subtle.digest("SHA-256", data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + + const deviceId = hashArray + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + + localStorage.setItem("guard-device-id", deviceId); + + return deviceId; // A 64-character hex string +};