bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
80 lines (71 loc) • 2.67 kB
text/typescript
import { Request, Response } from 'express';
import { RequestHandler } from 'express-serve-static-core';
import logger from '../logger';
import { MongoBound } from '../models/base';
import { IWallet } from '../models/wallet';
import { ChainStateProvider } from '../providers/chain-state';
import { Config } from '../services/config';
import { ChainNetwork } from '../types/ChainNetwork';
const secp256k1 = require('secp256k1');
const bitcoreLib = require('bitcore-lib');
export interface VerificationPayload {
message: string;
pubKey: string;
signature: string | string[] | undefined;
}
type SignedApiRequest = ChainNetwork & VerificationPayload;
export function verifyRequestSignature(params: VerificationPayload): boolean {
const { message, pubKey, signature } = params;
const pub = new bitcoreLib.PublicKey(pubKey).toBuffer();
const messageHash = bitcoreLib.crypto.Hash.sha256sha256(Buffer.from(message));
if (typeof signature === 'string') {
return secp256k1.ecdsaVerify(Buffer.from(signature, 'hex'), messageHash, pub);
} else {
throw new Error('Signature must exist');
}
}
type PreAuthRequest = {
params: SignedApiRequest;
} & Request;
export type AuthenticatedRequest = {
wallet?: MongoBound<IWallet>;
} & PreAuthRequest;
const authenticateMiddleware: RequestHandler = async (req: Request, res: Response, next: any) => {
const { chain, network, pubKey } = (req.params as unknown) as SignedApiRequest;
logger.debug('Authenticating request with pubKey: %o', pubKey);
let wallet;
try {
wallet = await ChainStateProvider.getWallet({ chain, network, pubKey });
} catch (err) {
return res.status(500).send('Problem authenticating wallet');
}
try {
if (req.is('application/octet-stream')) {
req.body = JSON.parse(req.body.toString());
}
if (!wallet) {
return res.status(404).send('Wallet not found');
}
Object.assign(req, { wallet });
const walletConfig = Config.for('api').wallets;
if (walletConfig && walletConfig.allowUnauthenticatedCalls) {
return next();
}
const validRequestSignature = verifyRequestSignature({
message: [req.method, req.originalUrl, JSON.stringify(req.body)].join('|'),
pubKey: wallet.pubKey,
signature: req.headers['x-signature']
});
if (!validRequestSignature) {
return res.status(401).send('Authentication failed');
}
return next();
} catch (e: any) {
logger.error('Unexpected error authenticating request: %o', e?.stack || e?.message || e);
return res.status(401).send('Authentication failed');
}
};
export const Auth = {
verifyRequestSignature,
authenticateMiddleware
};