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