- Add common generateEncryptedId() utility in @multica/utils - All Device IDs now use same encryption algorithm (40 hex chars) - Web: store encrypted format directly in localStorage - Desktop: use shared utility, accept encrypted ID from Web - Hub: use shared utility for hub-id generation - Telegram: use shared utility for device ID generation - Gateway hook: use encrypted format for client connections Algorithm: sha256(sha256(uuid).slice(0,32)).slice(0,8) + sha256(uuid).slice(0,32) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
/**
|
|
* Device ID management for Multica Web
|
|
* Stores encrypted format directly (40 hex chars)
|
|
*/
|
|
|
|
const DEVICE_ID_KEY = 'MULTICA_DEVICE_ID'
|
|
|
|
// SHA-256 hash function (using Web Crypto API)
|
|
async function sha256(text: string): Promise<string> {
|
|
const buffer = new TextEncoder().encode(text)
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
|
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
|
|
}
|
|
|
|
// Generate encrypted device ID (40 hex chars)
|
|
async function generateEncryptedDeviceId(): Promise<string> {
|
|
const uuid = crypto.randomUUID()
|
|
const firstHash = (await sha256(uuid)).slice(0, 32)
|
|
return (await sha256(firstHash)).slice(0, 8) + firstHash
|
|
}
|
|
|
|
// Validate encrypted ID format (40 hex characters)
|
|
function isValidEncryptedId(id: string): boolean {
|
|
return typeof id === 'string' && /^[a-f0-9]{40}$/i.test(id)
|
|
}
|
|
|
|
// Cached promise for async generation
|
|
let deviceIdPromise: Promise<string> | null = null
|
|
|
|
/**
|
|
* Get or create Device ID (encrypted 40-char format)
|
|
* Stored in localStorage, ready to use directly
|
|
*/
|
|
export async function getOrCreateDeviceId(): Promise<string> {
|
|
if (typeof window === 'undefined') return ''
|
|
|
|
const existing = localStorage.getItem(DEVICE_ID_KEY)
|
|
|
|
// If already encrypted format, return as-is
|
|
if (existing && isValidEncryptedId(existing)) {
|
|
return existing
|
|
}
|
|
|
|
// Generate new encrypted ID
|
|
if (!deviceIdPromise) {
|
|
deviceIdPromise = generateEncryptedDeviceId().then((id) => {
|
|
localStorage.setItem(DEVICE_ID_KEY, id)
|
|
return id
|
|
})
|
|
}
|
|
|
|
return deviceIdPromise
|
|
}
|
|
|