Skip to content

Instantly share code, notes, and snippets.

@krpeacock
Created August 4, 2022 19:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krpeacock/9203f1c6bbd8c55f2eef5e001223edf2 to your computer and use it in GitHub Desktop.
Save krpeacock/9203f1c6bbd8c55f2eef5e001223edf2 to your computer and use it in GitHub Desktop.
EncryptedIdbStorage implementation using IdbKeyVal
export class EncryptedIdbStorage implements AuthClientStorage {
private initializedDb: IdbKeyVal | undefined;
get _db(): Promise<IdbKeyVal> {
return new Promise(resolve => {
if (this.initializedDb) resolve(this.initializedDb);
IdbKeyVal.create().then(db => {
this.initializedDb = db;
resolve(db);
});
});
}
private storedKey: CryptoKey | undefined;
private get encryptKey() {
return new Promise<CryptoKey>(resolve => {
this._db.then(async db => {
if (this.storedKey) resolve(this.storedKey);
const key =
(await db.get<CryptoKey | undefined>(KEY_ENCRYPTION)) ??
(await crypto.subtle.generateKey(
{
name: 'AES-CBC',
length: 256,
},
false,
['encrypt', 'decrypt'],
));
this.storedKey = key;
await db.set(KEY_ENCRYPTION, key);
resolve(key);
});
});
}
private get iv() {
return new Promise<Uint8Array>(resolve => {
this._db.then(db => {
db.get<Uint8Array | undefined>(KEY_VECTOR).then(async storedIv => {
const iv = storedIv ?? (await crypto.getRandomValues(new Uint8Array(16)));
await db.set(KEY_VECTOR, iv);
resolve(iv);
});
});
});
}
public async get(key: string): Promise<string | null> {
const db = await this._db;
const encryptKey = await await this.encryptKey;
const encrypted = await db.get<ArrayBuffer>(key);
if (encrypted) {
const decoder = new TextDecoder();
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-CBC', iv: await this.iv },
encryptKey,
encrypted,
);
return decoder.decode(decrypted);
}
return null;
}
public async set(key: string, value: string): Promise<void> {
const db = await this._db;
const encoder = new TextEncoder();
const encryptKey = await await this.encryptKey;
try {
await db.set(
key,
await crypto.subtle.encrypt(
{ name: 'AES-CBC', iv: await this.iv },
encryptKey,
encoder.encode(value).buffer,
),
);
} catch (error) {
console.error(error);
}
}
public async remove(key: string): Promise<void> {
const db = await this._db;
await db.remove(key);
}
}
@krpeacock
Copy link
Author

I was going to add this to auth-client, but I've removed it for initial simplicity and code review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment