UNPKG

@didtools/pkh-ethereum

Version:

Implements support to authenticate, authorize and verify with Ethereum accounts as a did:pkh with SIWE(X) and CACAO. Primarly used with `did-session` and `@didtools/cacao`.

83 lines (82 loc) 3.36 kB
import { Cacao, SiweMessage } from '@didtools/cacao'; import { randomString } from '@stablelib/random'; import { AccountId } from 'caip'; import { safeSend, normalizeAccountId } from './utils.js'; import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils'; /** * SIWX Version */ export const VERSION = '1'; /** * CAIP2 for ethereum, used in CAIP10 (acountId) */ export const CHAIN_NAMESPACE = 'eip155'; function encodeHexStr(str) { return `0x${bytesToHex(utf8ToBytes(str))}`; } export var EthereumWebAuth; (function(EthereumWebAuth) { async function getAuthMethod(ethProvider, account) { if (typeof window === 'undefined') throw new Error('Web Auth method requires browser environment'); const domain = window.location.hostname; return async (opts)=>{ opts.domain = domain; return createCACAO(opts, ethProvider, account); }; } /** * Get a configured authMethod for an Ethereum account in a web based environment */ // eslint-disable-next-line @typescript-eslint/require-await EthereumWebAuth.getAuthMethod = getAuthMethod; })(EthereumWebAuth || (EthereumWebAuth = {})); export var EthereumNodeAuth; (function(EthereumNodeAuth) { async function getAuthMethod(ethProvider, account, appName) { if (!appName) throw new Error('Node Auth method requires an application name gethAuthMethod(ethProvider, account, appName)'); return async (opts)=>{ opts.domain = appName; return createCACAO(opts, ethProvider, account); }; } /** * Get a configured authMethod for an Ethereum account in a Node based environment */ // eslint-disable-next-line @typescript-eslint/require-await EthereumNodeAuth.getAuthMethod = getAuthMethod; })(EthereumNodeAuth || (EthereumNodeAuth = {})); async function createCACAO(opts, ethProvider, account) { const now = new Date(); const oneWeekLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); const normAccount = normalizeAccountId(account); const siweMessage = new SiweMessage({ domain: opts.domain, address: normAccount.address, statement: opts.statement ?? 'Give this application access to some of your data on Ceramic', uri: opts.uri, version: VERSION, nonce: opts.nonce ?? randomString(10), issuedAt: now.toISOString(), expirationTime: opts.expirationTime ?? oneWeekLater.toISOString(), chainId: normAccount.chainId.reference, resources: opts.resources }); const signature = await safeSend(ethProvider, 'personal_sign', [ encodeHexStr(siweMessage.signMessage({ eip55: true })), normAccount.address ]); siweMessage.signature = signature; return Cacao.fromSiweMessage(siweMessage); } async function requestChainId(provider) { const chainIdHex = await safeSend(provider, 'eth_chainId', []); return parseInt(chainIdHex, 16); } /** * Helper function to get an accountId (CAIP10) for an Ethereum account, uses ethProvider to get chainId/network */ export async function getAccountId(ethProvider, address) { const ethChainId = await requestChainId(ethProvider); const chainId = `${CHAIN_NAMESPACE}:${ethChainId}`; return new AccountId({ address: address.toLowerCase(), chainId }); }