UNPKG

@didtools/pkh-solana

Version:

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

108 lines (107 loc) 4.38 kB
import { AccountId } from 'caip'; import { toString } from 'uint8arrays/to-string'; import { randomString } from '@stablelib/random'; import { Cacao, SiwsMessage } from '@didtools/cacao'; export const SOLANA_TESTNET_CHAIN_REF = '4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z' // Solana testnet ; export const SOLANA_DEVNET_CHAIN_REF = 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1' // Solana devnet ; export const SOLANA_MAINNET_CHAIN_REF = '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp' // Solana mainnet beta ; export const VERSION = '1'; export const CHAIN_NAMESPACE = 'solana'; export const chainIdMap = { mainnet: SOLANA_MAINNET_CHAIN_REF, testnet: SOLANA_TESTNET_CHAIN_REF, devnet: SOLANA_DEVNET_CHAIN_REF }; export var SolanaWebAuth; (function(SolanaWebAuth) { async function getAuthMethod(solProvider, 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, solProvider, account); }; } /** * Get a configured authMethod for a Solana account in a web based environment */ // eslint-disable-next-line @typescript-eslint/require-await SolanaWebAuth.getAuthMethod = getAuthMethod; })(SolanaWebAuth || (SolanaWebAuth = {})); export var SolanaNodeAuth; (function(SolanaNodeAuth) { async function getAuthMethod(ethProvider, account, appName) { const domain = appName; return async (opts)=>{ opts.domain = domain; return createCACAO(opts, ethProvider, account); }; } /** * Get a configured authMethod for a Solana account in a node based environment */ // eslint-disable-next-line @typescript-eslint/require-await SolanaNodeAuth.getAuthMethod = getAuthMethod; })(SolanaNodeAuth || (SolanaNodeAuth = {})); export function assertSupportedProvider(solProvider) { const p = solProvider; if (p.signMessage === null || p.signMessage === undefined) { throw new Error(`Unsupported provider; provider must implement signMessage`); } } export function assertSupportedConnection(solConnection) { const c = solConnection; if (c.getGenesisHash === null || c.getGenesisHash === undefined) { throw new Error(`Unsupported provider; provider must implement getGenesisHash`); } } async function sign(solProvider, message) { assertSupportedProvider(solProvider); const { signature } = await solProvider.signMessage(message, 'utf8'); return toString(signature, 'base58btc'); } async function createCACAO(opts, solProvider, account) { const now = new Date(); const oneWeekLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); const siwsMessage = new SiwsMessage({ domain: opts.domain, address: account.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: account.chainId.reference, resources: opts.resources }); const signData = siwsMessage.signMessage(); const signature = await sign(solProvider, signData); siwsMessage.signature = signature; return Cacao.fromSiwsMessage(siwsMessage); } export async function requestChainId(solConnection) { assertSupportedConnection(solConnection); const genesisHash = await solConnection.getGenesisHash(); return genesisHash.slice(0, 32); } /** * Helper function to get an accountId (CAIP10) for an Solana account by Solana Connection interface, Connection must implement 'getGenesisHash()' */ export async function getAccountId(solConnection, address) { const solChainId = await requestChainId(solConnection); const chainId = `${CHAIN_NAMESPACE}:${solChainId}`; return new AccountId({ address, chainId }); } /** * Helper function to get an accountId (CAIP10) for an Solana account by network string 'mainet' | 'testnet' | 'devenet' */ export function getAccountIdByNetwork(network, address) { const chainId = `${CHAIN_NAMESPACE}:${chainIdMap[network]}`; return new AccountId({ address, chainId }); }