import axios from 'axios';
import sodium from 'libsodium-wrappers';
import { v4 as uuidv4 } from 'uuid';
export class BecknClient {
public bppUri: string;
public bapId: string;
public bapUri: string;
private privateKey: string;
private publicKey: string;
private uniqueKeyId: string;
constructor(config: {
bppUri: string;
bapId: string;
bapUri: string;
privateKey: string;
publicKey: string;
uniqueKeyId: string;
}) {
this.bppUri = config.bppUri;
this.bapId = config.bapId;
this.bapUri = config.bapUri;
this.privateKey = config.privateKey;
this.publicKey = config.publicKey;
this.uniqueKeyId = config.uniqueKeyId;
}
private async createAuthorizationHeader(body: any): Promise<string> {
await sodium.ready;
const created = Math.floor(Date.now() / 1000);
const expires = created + 3600; // 1 hour expiry
// 1. Generate BLAKE-512 hash of the body
const digest = sodium.crypto_generichash(64, JSON.stringify(body));
const digestBase64 = sodium.to_base64(digest, sodium.base64_variants.ORIGINAL);
// 2. Create the signing string
const signingString = `(created): ${created}\n(expires): ${expires}\ndigest: BLAKE-512=${digestBase64}`;
// 3. Sign the string with Ed25519
const privateKeyBytes = sodium.from_base64(this.privateKey, sodium.base64_variants.ORIGINAL);
const signature = sodium.crypto_sign_detached(signingString, privateKeyBytes);
const signatureBase64 = sodium.to_base64(signature, sodium.base64_variants.ORIGINAL);
// 4. Construct the header
return `Signature keyId="${this.bapId}|${this.uniqueKeyId}|ed25519",algorithm="ed25519",created="${created}",expires="${expires}",headers="(created) (expires) digest",signature="${signatureBase64}"`;
}
public async sendRequest(action: string, body: any) {
const authHeader = await this.createAuthorizationHeader(body);
try {
const response = await axios.post(`${this.bppUri}/${action}`, body, {
headers: {
'Content-Type': 'application/json',
'Authorization': authHeader,
},
});
return response.data;
} catch (error) {
console.error(`Error sending ${action} to BPP:`, error);
throw error;
}
}
}