From aa8f7c5dab2afa9e74c3597f3f42a3ab18725d33 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Sat, 31 Jan 2026 17:36:04 +0100 Subject: [PATCH] hash email address --- js/services/auth.js | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/js/services/auth.js b/js/services/auth.js index 8febe87..0bffb7b 100644 --- a/js/services/auth.js +++ b/js/services/auth.js @@ -3,6 +3,7 @@ * * No email addresses, no personal data * User remembers only their UUID + * Email stored in Directus is hash(uuid)@domain - UUID cannot be recovered */ import { directus } from './directus.js' @@ -13,6 +14,7 @@ class AuthService { constructor() { this.currentUser = null this.listeners = new Set() + this.hashCache = new Map() } /** @@ -20,12 +22,10 @@ class AuthService { * @returns {string} UUID v4 */ generateUuid() { - // Use native if available (secure contexts) if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID() } - // Fallback for non-secure contexts return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0 const v = c === 'x' ? r : (r & 0x3 | 0x8) @@ -34,12 +34,34 @@ class AuthService { } /** - * Converts UUID to fake email for Directus - * @param {string} uuid - User UUID - * @returns {string} Fake email address + * Hashes a string using SHA-256 + * @param {string} str - String to hash + * @returns {Promise} Hex-encoded hash */ - uuidToEmail(uuid) { - return `${uuid}@${AUTH_DOMAIN}` + async hashString(str) { + if (this.hashCache.has(str)) { + return this.hashCache.get(str) + } + + const encoder = new TextEncoder() + const data = encoder.encode(str) + const hashBuffer = await crypto.subtle.digest('SHA-256', data) + const hashArray = Array.from(new Uint8Array(hashBuffer)) + const hash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('') + + this.hashCache.set(str, hash) + return hash + } + + /** + * Converts UUID to hashed email for Directus + * The UUID cannot be recovered from the hash + * @param {string} uuid - User UUID + * @returns {Promise} Hashed email address + */ + async uuidToEmail(uuid) { + const hash = await this.hashString(uuid) + return `${hash.substring(0, 32)}@${AUTH_DOMAIN}` } /** @@ -48,16 +70,14 @@ class AuthService { */ async createAccount() { const uuid = this.generateUuid() - const email = this.uuidToEmail(uuid) + const email = await this.uuidToEmail(uuid) try { await directus.register(email, uuid) - // Try auto-login (may fail if verification required) const loginResult = await this.login(uuid) if (!loginResult.success) { - // Registration worked but login failed (verification pending) return { uuid, success: true, @@ -83,7 +103,7 @@ class AuthService { * @returns {Promise<{success: boolean, error?: string}>} */ async login(uuid) { - const email = this.uuidToEmail(uuid) + const email = await this.uuidToEmail(uuid) try { await directus.login(email, uuid)