-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Encrypt accounts key in localstorage
- Loading branch information
1 parent
e5b5a6c
commit dcb2a1b
Showing
5 changed files
with
173 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import CryptoJS from 'crypto-js'; | ||
import { createTransform } from "redux-persist"; | ||
|
||
import { getDeviceFingerprint } from "../utils/deviceFingerprint"; | ||
|
||
|
||
// Initialize the key immediately | ||
const encryptionKey = CryptoJS.SHA256(getDeviceFingerprint() + "umami-salt").toString(); | ||
|
||
export const createEncryptionTransform = () => | ||
createTransform( | ||
// Transform state on its way to being serialized and persisted. | ||
(inboundState) => { | ||
if (!encryptionKey) { | ||
// Return unencrypted state if key isn't ready yet | ||
return inboundState; | ||
} | ||
|
||
const serialized = JSON.stringify(inboundState); | ||
return CryptoJS.AES.encrypt(serialized, encryptionKey).toString(); | ||
}, | ||
// Transform state being rehydrated | ||
(outboundState) => { | ||
if (!encryptionKey || !outboundState) { | ||
return outboundState; | ||
} | ||
|
||
try { | ||
const decrypted = CryptoJS.AES.decrypt(outboundState as string, encryptionKey); | ||
const decryptedString = decrypted.toString(CryptoJS.enc.Utf8); | ||
return JSON.parse(decryptedString); | ||
} catch (error) { | ||
console.error('Failed to decrypt state:', error); | ||
return outboundState; | ||
} | ||
}, | ||
{ | ||
// Only transform the accounts state | ||
whitelist: ["accounts"] | ||
} | ||
); | ||
|
||
const isElectron = () => { | ||
return typeof window !== 'undefined' && | ||
(window.process?.type === 'renderer' || window.process?.versions?.electron); | ||
}; | ||
|
||
// In a web browser: | ||
isElectron() // returns false | ||
|
||
// In Electron's renderer process: | ||
isElectron() // returns true because process.type === 'renderer' | ||
|
||
// In Electron's main process: | ||
isElectron() // returns true because process.versions.electron exists |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { v5 as uuidv5 } from 'uuid'; | ||
|
||
let cachedFingerprint: string | null = null; | ||
const NAMESPACE = "1b671a64-40d5-491e-99b0-da01ff1f3341"; // Static UUID namespace | ||
|
||
const isElectron = () => typeof window !== "undefined" && | ||
((window as any).process?.type === "renderer" || (window as any).process?.versions?.electron); | ||
|
||
const getWebFingerprint = (): string => { | ||
// For web browsers | ||
const components = [ | ||
window.navigator.userAgent, | ||
window.screen.height, | ||
window.screen.width, | ||
window.screen.colorDepth, | ||
new Date().getTimezoneOffset() | ||
]; | ||
return components.join("|"); | ||
}; | ||
|
||
const getElectronFingerprint = (): string => { | ||
// For Electron | ||
if (isElectron()) { | ||
const os = window.require("os"); | ||
const components = [ | ||
os.hostname(), | ||
os.platform(), | ||
os.arch(), | ||
os.cpus()[0]?.model, | ||
os.totalmem() | ||
]; | ||
return components.join("|"); | ||
} | ||
throw new Error("Not in Electron environment"); | ||
}; | ||
|
||
export const getDeviceFingerprint = (): string => { | ||
if (cachedFingerprint) { | ||
return cachedFingerprint; | ||
} | ||
|
||
try { | ||
// Get raw fingerprint data based on environment | ||
const rawFingerprint = isElectron() | ||
? getElectronFingerprint() | ||
: getWebFingerprint(); | ||
|
||
// Generate a consistent UUID from the fingerprint data | ||
cachedFingerprint = uuidv5(rawFingerprint, NAMESPACE); | ||
return cachedFingerprint; | ||
|
||
} catch (error) { | ||
// Fallback: Generate a random but persistent fingerprint | ||
const storageKey = 'umami-device-id'; | ||
let fallbackId = localStorage.getItem(storageKey); | ||
|
||
if (!fallbackId) { | ||
fallbackId = uuidv5(Date.now().toString(), NAMESPACE); | ||
localStorage.setItem(storageKey, fallbackId); | ||
} | ||
|
||
cachedFingerprint = fallbackId; | ||
return fallbackId; | ||
} | ||
}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.