accounts
Version:
Tempo Accounts SDK
80 lines • 3.78 kB
JavaScript
import { PublicKey, WebCryptoP256 } from 'ox';
import { Account as TempoAccount } from 'viem/tempo';
import { KeyUnavailableError } from '../Keystore.js';
/**
* WebCrypto P-256 keystore. The built-in default (see `Keystore.defaults`).
*
* By default the key is generated non-extractable: the private key never has
* a JS-visible encoding, and the handle holds the live `CryptoKey`, which
* persists only through structured-clone storage (`Storage.idb`,
* `Storage.memory`) — on string-based storage the key is session-only.
*
* Pass `extractable: true` for environments without structured-clone storage
* where keys must survive restarts (e.g. React Native, Node, CLI): the
* private key is exported once as a JWK (at-rest protection is the host
* app's storage adapter) and re-imported non-extractable each session. In
* browsers with IndexedDB storage, prefer the non-extractable default.
*
* @example
* ```ts
* import { Keystore, Provider } from 'accounts'
*
* const provider = Provider.create({
* accessKey: { keystores: { p256: Keystore.webCryptoP256({ extractable: true }) } },
* })
* ```
*/
export function webCryptoP256(options = {}) {
const { extractable = false } = options;
return {
requiresStructuredClone: !extractable,
async createKey() {
if (!globalThis.crypto?.subtle)
throw new Error('`webCryptoP256` keystore requires WebCrypto (`crypto.subtle`) support.');
const keyPair = await WebCryptoP256.createKeyPair({ extractable });
const publicKey = PublicKey.toHex(keyPair.publicKey);
if (!extractable)
return {
handle: { keyPair, kind: 'webcrypto-p256' },
publicKey,
};
const jwk = await globalThis.crypto.subtle.exportKey('jwk', keyPair.privateKey);
return { handle: { jwk, kind: 'webcrypto-p256' }, publicKey };
},
async toAccount(record, context) {
const handle = record.handle;
if (handle?.kind !== 'webcrypto-p256')
throw new Error('Unrecognized `webCryptoP256` keystore handle.');
const account = {
access: context.access,
keyAuthorizationManager: context.keyAuthorizationManager,
};
if (handle.keyPair) {
// A live key pair serialized through non-structured-clone storage
// arrives mangled — the key is permanently gone.
if (!isCryptoKey(handle.keyPair.privateKey))
throw new KeyUnavailableError('`webCryptoP256` key material did not survive storage.');
return TempoAccount.fromWebCryptoP256(handle.keyPair, account);
}
if (handle.jwk) {
const privateKey = await globalThis.crypto.subtle
.importKey('jwk', handle.jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign'])
.catch((error) => {
throw new KeyUnavailableError('`webCryptoP256` keystore handle holds unusable key material.', { cause: error });
});
return TempoAccount.fromWebCryptoP256({ privateKey, publicKey: PublicKey.fromHex(record.publicKey) }, account);
}
throw new Error('Unrecognized `webCryptoP256` keystore handle.');
},
};
}
function isCryptoKey(value) {
if (typeof CryptoKey !== 'undefined' && value instanceof CryptoKey)
return true;
// Cross-realm fallback (e.g. keys structured-cloned through IndexedDB).
return (!!value &&
typeof value === 'object' &&
'algorithm' in value &&
value.type === 'private');
}
//# sourceMappingURL=webCryptoP256.js.map