@lo-fi/local-vault
Advanced tools
Comparing version 0.11.1 to 0.11.2
/*! Local-Vault: adapter.cookie.js | ||
v0.11.1 (c) 2024 Kyle Simpson | ||
v0.11.2 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
@@ -4,0 +4,0 @@ */ |
/*! Local-Vault: adapter.idb.js | ||
v0.11.1 (c) 2024 Kyle Simpson | ||
v0.11.2 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
@@ -4,0 +4,0 @@ */ |
/*! Local-Vault: adapter.local-storage.js | ||
v0.11.1 (c) 2024 Kyle Simpson | ||
v0.11.2 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
@@ -4,0 +4,0 @@ */ |
/*! Local-Vault: adapter.opfs.js | ||
v0.11.1 (c) 2024 Kyle Simpson | ||
v0.11.2 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
@@ -4,0 +4,0 @@ */ |
/*! Local-Vault: adapter.session-storage.js | ||
v0.11.1 (c) 2024 Kyle Simpson | ||
v0.11.2 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
@@ -4,0 +4,0 @@ */ |
/*! Local-Data-Lock: ldl.js | ||
v0.11.1 (c) 2024 Kyle Simpson | ||
v0.11.2 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
*/ | ||
import{supportsWebAuthn as e,regDefaults as t,register as r,authDefaults as n,auth as a,verifyAuthResponse as i,packPublicKeyJSON as o,unpackPublicKeyJSON as l,toBase64String as c,fromBase64String as s,toUTF8String as y,fromUTF8String as u,resetAbortReason as p}from"@lo-fi/webauthn-local-client";const d=1,f=sodium.crypto_sign_SEEDBYTES;var k=setMaxLockKeyCacheLifetime(),b=function loadLocalIdentities(){return Object.fromEntries(Object.entries(JSON.parse(window.localStorage.getItem("local-identities")||null)||{}).filter((([e,t])=>"number"==typeof t.lastSeq&&Array.isArray(t.passkeys)&&t.passkeys.length>0&&t.passkeys.every((e=>"string"==typeof e.credentialID&&""!=e.credentialID&&"number"==typeof e.seq&&null!=e.publicKey&&"object"==typeof e.publicKey&&"number"==typeof e.publicKey.algoCOSE&&"string"==typeof e.publicKey.raw&&""!=e.publicKey.raw&&"string"==typeof e.publicKey.spki&&""!=e.publicKey.spki&&"string"==typeof e.hash&&""!=e.hash&&e.hash==computePasskeyEntryHash(e))))).map((([e,t])=>[e,{...t,passkeys:t.passkeys.map((e=>({...e,publicKey:l(e.publicKey)})))}])))}(),g={},L=null,h=new WeakMap;export{e as supportsWebAuthn,o as packPublicKeyJSON,l as unpackPublicKeyJSON,c as toBase64String,s as fromBase64String,y as toUTF8String,u as fromUTF8String,p as resetAbortReason,listLocalIdentities,clearLockKeyCache,removeLocalAccount,getLockKey,generateEntropy,deriveLockKey,lockData,unlockData,setMaxLockKeyCacheLifetime};var K={supportsWebAuthn:e,packPublicKeyJSON:o,unpackPublicKeyJSON:l,toBase64String:c,fromBase64String:s,toUTF8String:y,fromUTF8String:u,resetAbortReason:p,listLocalIdentities:listLocalIdentities,clearLockKeyCache:clearLockKeyCache,removeLocalAccount:removeLocalAccount,getLockKey:getLockKey,generateEntropy:generateEntropy,deriveLockKey:deriveLockKey,lockData:lockData,unlockData:unlockData,setMaxLockKeyCacheLifetime:setMaxLockKeyCacheLifetime};export default K;function listLocalIdentities(){return Object.keys(b)}function cacheLockKey(e,t,r=!1){e in g&&!r||(g[e]={...t,timestamp:Date.now()})}function clearLockKeyCache(e){null!=e?delete g[e]:g={}}function removeLocalAccount(e){delete g[e],delete b[e],storeLocalIdentities()}async function getLockKey({localIdentity:e=c(generateEntropy(15)),username:o="local-user",displayName:l="Local User",relyingPartyID:s=document.location.hostname,relyingPartyName:y="Local Data Lock",addNewPasskey:u=!1,resetLockKey:p=!1,useLockKey:d=null,verify:h=!0,signal:K}={}){var m=null!=e?b[e]:null;if(null!=m){let t=function getCachedLockKey(e){if(e in g&&g[e].timestamp>=Date.now()-k){let{timestamp:t,...r}=g[e];return r}}(e);if(null!=t&&!p){if(u){resetAbortToken(K);let{record:e}=await registerLocalIdentity(t)||{};cleanupExternalSignalHandler(L),L=null,null!=e&&(m.lastSeq=e.lastSeq,m.passkeys=[...m.passkeys,...e.passkeys],storeLocalIdentities())}return Object.freeze({...t,localIdentity:e})}if(delete g[e],p){if(resetAbortToken(K),({record:b[e],lockKey:t}=await registerLocalIdentity(d&&"object"==typeof d?checkLockKey(d):void 0)),cleanupExternalSignalHandler(L),L=null,null==b[e])delete b[e];else if(null!=t)return storeLocalIdentities(),cacheLockKey(e,t),Object.freeze({...t,localIdentity:e})}else{if(u)throw new Error("Encryption/Decryption key not currently cached, unavailable for new passkey");{resetAbortToken(K);let t=n({relyingPartyID:s,mediation:"optional",allowCredentials:m.passkeys.map((({credentialID:e})=>({type:"public-key",id:e}))),signal:L.signal}),r=await a(t);if(cleanupExternalSignalHandler(L),L=null,null!=r){if(h){let e=m.passkeys.find((e=>e.credentialID==r.response.credentialID)),t=null!=e?e.publicKey:null;if(!(null!=t&&await i(r.response,t)))throw new Error("Auth verification failed")}return{...extractLockKey(r),localIdentity:e}}}}}else if(u){resetAbortToken(K);let{record:t,lockKey:r}=await registerLocalIdentity(d&&"object"==typeof d?checkLockKey(d):void 0)||{};if(cleanupExternalSignalHandler(L),L=null,null!=t&&null!=r)return b[e]=t,cacheLockKey(e,r),storeLocalIdentities(),Object.freeze({...r,localIdentity:e})}else{resetAbortToken(K);let t=n({relyingPartyID:s,mediation:"optional",signal:L.signal}),r=await a(t);if(cleanupExternalSignalHandler(L),L=null,null!=r){let t=extractLockKey(r),[n]=Object.entries(b).find((([,e])=>null!=e.passkeys.find((e=>e.credentialID==r.response.credentialID))))||[];if(null!=n){if(delete g[e],m=b[e=n],h){let e=m.passkeys.find((e=>e.credentialID==r.response.credentialID)),t=null!=e?e.publicKey:null;if(!(null!=t&&await i(r.response,t)))throw new Error("Auth verification failed")}cacheLockKey(e,t)}else if(h)throw new Error("Auth verification requested but skipped, against unrecognized passkey (no matching local-identity)");return Object.freeze({...t,localIdentity:e})}}async function registerLocalIdentity(n=deriveLockKey()){try{let i=((b[e]||{}).lastSeq||0)+1,c=new Uint8Array(n.iv.byteLength+2),u=new DataView(new ArrayBuffer(2));u.setInt16(0,i,!1),c.set(n.iv,0),c.set(new Uint8Array(u.buffer),n.iv.byteLength);let p=t({relyingPartyID:s,relyingPartyName:y,user:{id:c,name:o,displayName:l},signal:L.signal}),d=await r(p);if(null!=d)return{record:{lastSeq:i,passkeys:[(a={seq:i,credentialID:d.response.credentialID,publicKey:d.response.publicKey},{...a,hash:computePasskeyEntryHash(a)})]},lockKey:n}}catch(e){throw new Error("Identity/Passkey registration failed",{cause:e})}var a}function extractLockKey(t){try{if(t&&t.response&&isByteArray(t.response.userID)&&t.response.userID.byteLength==f+2){let r=deriveLockKey(t.response.userID.subarray(0,f));return cacheLockKey(e,r),r}throw new Error("Passkey info missing")}catch(e){throw new Error("Chosen passkey did not provide a valid encryption/decryption key",{cause:e})}}}function resetAbortToken(e){if(L&&(cleanupExternalSignalHandler(L),L.aborted||L.abort("Passkey operation abandoned.")),L=new AbortController,null!=e)if(e.aborted)L.abort(e.reason);else{let handlerFn=()=>{cleanupExternalSignalHandler(L),L.abort(e.reason),L=e=handlerFn=null};e.addEventListener("abort",handlerFn),h.set(L,[e,handlerFn])}}function cleanupExternalSignalHandler(e){if(null!=e&&h.has(e)){let[t,r]=h.get(e);t.removeEventListener("abort",r),h.delete(e)}}function generateEntropy(e=16){return sodium.randombytes_buf(e)}function deriveLockKey(e=generateEntropy(f)){try{let t=sodium.crypto_sign_seed_keypair(e);return{keyFormatVersion:d,iv:e,publicKey:t.publicKey,privateKey:t.privateKey,encPK:sodium.crypto_sign_ed25519_pk_to_curve25519(t.publicKey),encSK:sodium.crypto_sign_ed25519_sk_to_curve25519(t.privateKey)}}catch(e){throw new Error("Encryption/decryption key derivation failed.",{cause:e})}}function checkLockKey(e){if(e&&"object"==typeof e){if(e.keyFormatVersion===d)return e;if(isByteArray(e.iv)&&e.iv.byteLength==f)return deriveLockKey(e.iv)}throw new Error("Unrecongnized lock-key")}function lockData(e,t,{outputFormat:r="base64"}={}){try{let n=null==e?null:e instanceof ArrayBuffer?new Uint8Array(e):isByteArray(e)?e:u("object"==typeof e?JSON.stringify(e):"string"==typeof e?e:String(e));if(null==e)throw new Error("Non-empty data required.");let a=sodium.crypto_box_seal(n,t.encPK);return["base64","base-64"].includes(r.toLowerCase())?c(a):a}catch(e){throw new Error("Data encryption failed.",{cause:e})}}function unlockData(e,t,{outputFormat:r="utf8",parseJSON:n=!0}={}){try{let a=sodium.crypto_box_seal_open("string"==typeof e?s(e):e,t.encPK,t.encSK);if(["utf8","utf-8"].includes(r.toLowerCase())){let e=y(a);return n?JSON.parse(e):e}return a}catch(e){throw new Error("Data decryption failed.",{cause:e})}}function storeLocalIdentities(){var e=Object.fromEntries(Object.entries(b).map((([e,t])=>[e,{...t,passkeys:t.passkeys.map((e=>({...e,publicKey:o(e.publicKey)})))}])));Object.keys(e).length>0?window.localStorage.setItem("local-identities",JSON.stringify(e)):window.localStorage.removeItem("local-identities")}function setMaxLockKeyCacheLifetime(e=18e5){return k=Math.max(0,Number(e)||0)}function isByteArray(e){return e instanceof Uint8Array&&e.buffer instanceof ArrayBuffer}function computePasskeyEntryHash(e){let{hash:t,...r}=e;return c(sodium.crypto_hash(JSON.stringify({...r,publicKey:o(r.publicKey)})))} | ||
import{supportsWebAuthn as e,regDefaults as t,register as r,authDefaults as n,auth as a,verifyAuthResponse as i,packPublicKeyJSON as o,unpackPublicKeyJSON as l,toBase64String as c,fromBase64String as s,toUTF8String as y,fromUTF8String as u,resetAbortReason as p}from"@lo-fi/webauthn-local-client";const d=1,f=sodium.crypto_sign_SEEDBYTES;var k=setMaxLockKeyCacheLifetime(),b=function loadLocalIdentities(){return Object.fromEntries(Object.entries(JSON.parse(window.localStorage.getItem("local-identities")||null)||{}).filter((([e,t])=>"number"==typeof t.lastSeq&&Array.isArray(t.passkeys)&&t.passkeys.length>0&&t.passkeys.every((e=>"string"==typeof e.credentialID&&""!=e.credentialID&&"number"==typeof e.seq&&null!=e.publicKey&&"object"==typeof e.publicKey&&"number"==typeof e.publicKey.algoCOSE&&"string"==typeof e.publicKey.raw&&""!=e.publicKey.raw&&"string"==typeof e.publicKey.spki&&""!=e.publicKey.spki&&"string"==typeof e.hash&&""!=e.hash&&e.hash==computePasskeyEntryHash(e))))).map((([e,t])=>[e,{...t,passkeys:t.passkeys.map((e=>({...e,publicKey:l(e.publicKey)})))}])))}(),g={},h=null,L=new WeakMap;export{e as supportsWebAuthn,o as packPublicKeyJSON,l as unpackPublicKeyJSON,c as toBase64String,s as fromBase64String,y as toUTF8String,u as fromUTF8String,p as resetAbortReason,listLocalIdentities,clearLockKeyCache,removeLocalAccount,getLockKey,generateEntropy,deriveLockKey,lockData,unlockData,setMaxLockKeyCacheLifetime};var K={supportsWebAuthn:e,packPublicKeyJSON:o,unpackPublicKeyJSON:l,toBase64String:c,fromBase64String:s,toUTF8String:y,fromUTF8String:u,resetAbortReason:p,listLocalIdentities:listLocalIdentities,clearLockKeyCache:clearLockKeyCache,removeLocalAccount:removeLocalAccount,getLockKey:getLockKey,generateEntropy:generateEntropy,deriveLockKey:deriveLockKey,lockData:lockData,unlockData:unlockData,setMaxLockKeyCacheLifetime:setMaxLockKeyCacheLifetime};export default K;function listLocalIdentities(){return Object.keys(b)}function cacheLockKey(e,t,r=!1){e in g&&!r||(g[e]={...t,timestamp:Date.now()})}function clearLockKeyCache(e){null!=e?delete g[e]:g={}}function removeLocalAccount(e){delete g[e],delete b[e],storeLocalIdentities()}async function getLockKey({localIdentity:e=c(generateEntropy(15)),username:o="local-user",displayName:l="Local User",relyingPartyID:s=document.location.hostname,relyingPartyName:y="Local Data Lock",addNewPasskey:u=!1,resetLockKey:p=!1,useLockKey:d=null,verify:L=!0,signal:K}={}){var m=null!=e?b[e]:null;if(null!=m){let t=function getCachedLockKey(e){var t=Date.now();if(e in g&&g[e].timestamp>=t-Math.min(k,t)){let{timestamp:t,...r}=g[e];return r}}(e);if(null!=t&&!p){if(u){resetAbortToken(K);let{record:e}=await registerLocalIdentity(t)||{};cleanupExternalSignalHandler(h),h=null,null!=e&&(m.lastSeq=e.lastSeq,m.passkeys=[...m.passkeys,...e.passkeys],storeLocalIdentities())}return Object.freeze({...t,localIdentity:e})}if(delete g[e],p){if(resetAbortToken(K),({record:b[e],lockKey:t}=await registerLocalIdentity(d&&"object"==typeof d?checkLockKey(d):void 0)),cleanupExternalSignalHandler(h),h=null,null==b[e])delete b[e];else if(null!=t)return storeLocalIdentities(),cacheLockKey(e,t),Object.freeze({...t,localIdentity:e})}else{if(u)throw new Error("Encryption/Decryption key not currently cached, unavailable for new passkey");{resetAbortToken(K);let t=n({relyingPartyID:s,mediation:"optional",allowCredentials:m.passkeys.map((({credentialID:e})=>({type:"public-key",id:e}))),signal:h.signal}),r=await a(t);if(cleanupExternalSignalHandler(h),h=null,null!=r){if(L){let e=m.passkeys.find((e=>e.credentialID==r.response.credentialID)),t=null!=e?e.publicKey:null;if(!(null!=t&&await i(r.response,t)))throw new Error("Auth verification failed")}return{...extractLockKey(r),localIdentity:e}}}}}else if(u){resetAbortToken(K);let{record:t,lockKey:r}=await registerLocalIdentity(d&&"object"==typeof d?checkLockKey(d):void 0)||{};if(cleanupExternalSignalHandler(h),h=null,null!=t&&null!=r)return b[e]=t,cacheLockKey(e,r),storeLocalIdentities(),Object.freeze({...r,localIdentity:e})}else{resetAbortToken(K);let t=n({relyingPartyID:s,mediation:"optional",signal:h.signal}),r=await a(t);if(cleanupExternalSignalHandler(h),h=null,null!=r){let t=extractLockKey(r),[n]=Object.entries(b).find((([,e])=>null!=e.passkeys.find((e=>e.credentialID==r.response.credentialID))))||[];if(null!=n){if(delete g[e],m=b[e=n],L){let e=m.passkeys.find((e=>e.credentialID==r.response.credentialID)),t=null!=e?e.publicKey:null;if(!(null!=t&&await i(r.response,t)))throw new Error("Auth verification failed")}cacheLockKey(e,t)}else if(L)throw new Error("Auth verification requested but skipped, against unrecognized passkey (no matching local-identity)");return Object.freeze({...t,localIdentity:e})}}async function registerLocalIdentity(n=deriveLockKey()){try{let i=((b[e]||{}).lastSeq||0)+1,c=new Uint8Array(n.iv.byteLength+2),u=new DataView(new ArrayBuffer(2));u.setInt16(0,i,!1),c.set(n.iv,0),c.set(new Uint8Array(u.buffer),n.iv.byteLength);let p=t({relyingPartyID:s,relyingPartyName:y,user:{id:c,name:o,displayName:l},signal:h.signal}),d=await r(p);if(null!=d)return{record:{lastSeq:i,passkeys:[(a={seq:i,credentialID:d.response.credentialID,publicKey:d.response.publicKey},{...a,hash:computePasskeyEntryHash(a)})]},lockKey:n}}catch(e){throw new Error("Identity/Passkey registration failed",{cause:e})}var a}function extractLockKey(t){try{if(t&&t.response&&isByteArray(t.response.userID)&&t.response.userID.byteLength==f+2){let r=deriveLockKey(t.response.userID.subarray(0,f));return cacheLockKey(e,r),r}throw new Error("Passkey info missing")}catch(e){throw new Error("Chosen passkey did not provide a valid encryption/decryption key",{cause:e})}}}function resetAbortToken(e){if(h&&(cleanupExternalSignalHandler(h),h.aborted||h.abort("Passkey operation abandoned.")),h=new AbortController,null!=e)if(e.aborted)h.abort(e.reason);else{let handlerFn=()=>{cleanupExternalSignalHandler(h),h.abort(e.reason),h=e=handlerFn=null};e.addEventListener("abort",handlerFn),L.set(h,[e,handlerFn])}}function cleanupExternalSignalHandler(e){if(null!=e&&L.has(e)){let[t,r]=L.get(e);t.removeEventListener("abort",r),L.delete(e)}}function generateEntropy(e=16){return sodium.randombytes_buf(e)}function deriveLockKey(e=generateEntropy(f)){try{let t=sodium.crypto_sign_seed_keypair(e);return{keyFormatVersion:d,iv:e,publicKey:t.publicKey,privateKey:t.privateKey,encPK:sodium.crypto_sign_ed25519_pk_to_curve25519(t.publicKey),encSK:sodium.crypto_sign_ed25519_sk_to_curve25519(t.privateKey)}}catch(e){throw new Error("Encryption/decryption key derivation failed.",{cause:e})}}function checkLockKey(e){if(e&&"object"==typeof e){if(e.keyFormatVersion===d)return e;if(isByteArray(e.iv)&&e.iv.byteLength==f)return deriveLockKey(e.iv)}throw new Error("Unrecongnized lock-key")}function lockData(e,t,{outputFormat:r="base64"}={}){try{let n=null==e?null:e instanceof ArrayBuffer?new Uint8Array(e):isByteArray(e)?e:u("object"==typeof e?JSON.stringify(e):"string"==typeof e?e:String(e));if(null==e)throw new Error("Non-empty data required.");let a=sodium.crypto_box_seal(n,t.encPK);return["base64","base-64"].includes(r.toLowerCase())?c(a):a}catch(e){throw new Error("Data encryption failed.",{cause:e})}}function unlockData(e,t,{outputFormat:r="utf8",parseJSON:n=!0}={}){try{let a=sodium.crypto_box_seal_open("string"==typeof e?s(e):e,t.encPK,t.encSK);if(["utf8","utf-8"].includes(r.toLowerCase())){let e=y(a);return n?JSON.parse(e):e}return a}catch(e){throw new Error("Data decryption failed.",{cause:e})}}function storeLocalIdentities(){var e=Object.fromEntries(Object.entries(b).map((([e,t])=>[e,{...t,passkeys:t.passkeys.map((e=>({...e,publicKey:o(e.publicKey)})))}])));Object.keys(e).length>0?window.localStorage.setItem("local-identities",JSON.stringify(e)):window.localStorage.removeItem("local-identities")}function setMaxLockKeyCacheLifetime(e=18e5){return k=Math.max(0,Number(e)||0)}function isByteArray(e){return e instanceof Uint8Array&&e.buffer instanceof ArrayBuffer}function computePasskeyEntryHash(e){let{hash:t,...r}=e;return c(sodium.crypto_hash(JSON.stringify({...r,publicKey:o(r.publicKey)})))} |
/*! Local-Vault: lv.js | ||
v0.11.1 (c) 2024 Kyle Simpson | ||
v0.11.2 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
@@ -4,0 +4,0 @@ */ |
{ | ||
"name": "@lo-fi/local-vault", | ||
"description": "Store key-value data encrypted (biometric passkey protected), locally in the client", | ||
"version": "0.11.1", | ||
"version": "0.11.2", | ||
"exports": { | ||
@@ -34,3 +34,3 @@ ".": "./dist/bundlers/lv.mjs", | ||
"dependencies": { | ||
"@lo-fi/local-data-lock": "~0.11.1", | ||
"@lo-fi/local-data-lock": "~0.11.2", | ||
"idb-keyval": "~6.2.1" | ||
@@ -37,0 +37,0 @@ }, |
@@ -28,3 +28,3 @@ # Local Vault | ||
The cryptographic encryption/decryption key is furthermore protected locally in the client in a biometric passkey (i.e., in an authenticator, secure enclave, etc). Users can thus safely access their protected data with a simple biometric passkey authentication -- no troublesome passwords, and no privacy-eroding remote servers! | ||
The [cryptographic encryption/decryption key is furthermore protected locally in the client in a biometric passkey (i.e., in an authenticator, secure enclave, etc)](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#how-does-it-work). Users can thus safely access their protected data with a simple biometric passkey authentication -- no troublesome passwords, and no privacy-eroding remote servers! | ||
@@ -156,17 +156,19 @@ **Local Vault** directly depends on [**Local-Data-Lock**](https://github.com/mylofi/local-data-lock), which depends on [**Webauthn-Local-Client**](https://github.com/mylofi/webauthn-local-client). | ||
A "local vault" is a JS object (JSON compatible), with the following three properties: | ||
A "local vault" is a JS object (JSON compatible), which is actually stored in the [client storage mechanism](#client-side-storage-adapters) you choose, either directly (for IndexedDB) or as a JSON serialized string. | ||
* `accountID` string, holding the ID of the "local account" attached to one or more device passkeys, which themselves hold the encryption/decryption cryptographic key | ||
The local-vault object has the following three properties: | ||
* `rpID` string, holding the "ID" of the "relying party", which for web applications should almost always be the fully-qualified hostname (i.e., `document.location.hostname`) of the webapp | ||
* `accountID` (string): holding the ID of the [*local account* attached to one or more device passkeys](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#registering-a-local-account-and-lock-key-keypair), each of which [hold the (same) encryption/decryption cryptographic key](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#how-does-it-work) | ||
* `data` string, holding the encrypted data in base64 encoding | ||
* `rpID` (string): holds the [*relying party ID*](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#configuring-passkeys), which for web applications should almost always be the fully-qualified hostname (i.e., `document.location.hostname`) of the webapp | ||
This local vault object is stored in the [client storage mechanism](#client-side-storage-adapters) you choose, either directly (for IndexedDB) or as a JSON serialized string. | ||
* `data` (string): holds the encrypted data, in base64 encoding | ||
**NOTE:** This local-vault object is *not* something your code should retrieve or modify in any way, directly. Instead, you'll use the methods on the vault-instance, as described in the next section. | ||
## Setting up a local vault instance | ||
The local vault object described previously is distinct from the vault-instance that you interact with in code. The vault-instance exposes a simple API (`get()`, `set()`, etc) for managing key-value style data access. Encryption and key-management is all handled automatically while interacting with the vault-instance. | ||
The vault-instance, created from a `connect()` call, exposes a simple API (`get()`, `set()`, etc) for managing key-value style data access. Encryption and key-management is all handled automatically while interacting with this vault-instance. | ||
To setup a new vault-instance, use the `connect()` method: | ||
To setup a new vault-instance: | ||
@@ -193,3 +195,3 @@ ```js | ||
Any options set under `keyOptions` are passed along to the underlying [**Local Data Lock** library's `getLockKey()` method](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#registering-a-local-account). | ||
Any [options set under `keyOptions`](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#configuring-passkeys) are passed along to the underlying [**Local Data Lock** library's `getLockKey()` method](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#registering-a-local-account). | ||
@@ -207,3 +209,3 @@ **Note:** The `username` / `displayName` key-options illustrated above are not strictly required, but are strongly recommended; they're only passed along to the biometric passkey, as meta-data for such. The device will often use one or both values in its prompt dialogs, so these values should either be something the user has picked, or at least be something the user will recognize and trust. Also, there may very well be multiple passkeys associated with the same local account, so the username/display-name should be differentiated to help the users know which passkey they're authenticating with. | ||
keyOptions: { | ||
// created by another vault instance, even on | ||
// created by another vault instance, perhaps on | ||
// another device | ||
@@ -228,3 +230,3 @@ useLockKey: existingLockKey | ||
**Note:** If the vault's lock-key (biometric passkey protected) is still in the recent-access cache (default timeout: 30 minutes), a `connect()` call will complete silently. Otherwise, the user will be prompted by the device to re-authenticate with their passkey (to access the lock-key) before unlocking the vault. | ||
**Note:** If the vault's lock-key (biometric passkey protected) is [still in the recent-access cache (default timeout: 30 minutes)](https://github.com/mylofi/local-data-lock?tab=readme-ov-file#change-passkey-cache-lifetime), a `connect()` call will complete silently. Otherwise, the user will be prompted by the device to re-authenticate with their passkey (to access the lock-key) before unlocking the vault. | ||
@@ -231,0 +233,0 @@ ### Discoverable Vaults |
@@ -356,2 +356,3 @@ // note: these module specifiers come from the import-map | ||
catch (err) { | ||
if (intv != null) { clearTimeout(intv); } | ||
logError(err); | ||
@@ -386,2 +387,3 @@ stopSpinner(); | ||
catch (err) { | ||
if (intv != null) { clearTimeout(intv); } | ||
logError(err); | ||
@@ -453,2 +455,3 @@ stopSpinner(); | ||
catch (err) { | ||
if (intv != null) { clearTimeout(intv); } | ||
logError(err); | ||
@@ -495,2 +498,3 @@ stopSpinner(); | ||
catch (err) { | ||
if (intv != null) { clearTimeout(intv); } | ||
logError(err); | ||
@@ -497,0 +501,0 @@ stopSpinner(); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
908506
5491
415