feat: database context

This commit is contained in:
2025-05-24 11:19:16 +02:00
parent b8f3fa0a32
commit eaf3596580
8 changed files with 165 additions and 28 deletions

30
web/src/util/account.ts Normal file
View File

@ -0,0 +1,30 @@
import { deriveDeviceKey, getDeviceId } from "./deviceId";
import { encryptToken } from "./token";
const storeTokensForAccount = async (
accountId: string,
accessToken: string,
refreshToken: string
) => {
const deviceKeyId = await getDeviceId();
const key = await deriveDeviceKey(deviceKeyId);
const access = await encryptToken(accessToken, key);
const refresh = await encryptToken(refreshToken, key);
const entry = {
accountId,
label: `Account for ${accountId}`,
access: {
data: Array.from(new Uint8Array(access.cipherText)),
iv: Array.from(access.iv),
},
refresh: {
data: Array.from(new Uint8Array(refresh.cipherText)),
iv: Array.from(refresh.iv),
},
updatedAt: new Date().toISOString(),
};
// Save this `entry` in IndexedDB (or use a localforage wrapper)
};

View File

@ -29,3 +29,26 @@ export const getDeviceId = async () => {
return deviceId; // A 64-character hex string
};
export const deriveDeviceKey = async (deviceKeyId: string) => {
const encoder = new TextEncoder();
const baseKey = await crypto.subtle.importKey(
"raw",
encoder.encode(deviceKeyId),
{ name: "PBKDF2" },
false,
["deriveKey"]
);
return crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: encoder.encode("guard_salt"),
iterations: 100000,
hash: "SHA-256",
},
baseKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
};

18
web/src/util/token.ts Normal file
View File

@ -0,0 +1,18 @@
export type EncryptedToken = {
cipherText: ArrayBuffer;
iv: Uint8Array<ArrayBuffer>;
};
export const encryptToken = async (
token: string,
key: CryptoKey
): Promise<EncryptedToken> => {
const encoder = new TextEncoder();
const iv = crypto.getRandomValues(new Uint8Array(12));
const cipherText = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
encoder.encode(token)
);
return { cipherText, iv };
};