@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
JavaScript
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
});
}