Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions agent_output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Agent created: TestAgent
ID: agent_6382d61c
DID: did:key:06ffeafa6f71c7bd83ad21ce4fb56e2c
4 changes: 4 additions & 0 deletions mandate_output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Intent Mandate issued for agent agent_6382d61c
Mandate ID: mandate_be7dc63fcbb70f38
Budget: 100 USD
Saved to: src/mandates/mandate_be7dc63fcbb70f38.jwt
25 changes: 24 additions & 1 deletion scripts/ocp-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,30 @@ program.command('x402:settle')
return;
}

// Simulation of x402 settlement
// Simulation of x402 settlement with fiduciary validation
const signingKey = process.env.MANDATE_SIGNING_KEY || 'default-secret-key';
const mandateService = new MandateService({ signingKey });

try {
const decodedMandate = await mandateService.verifyMandate(mandateToken);
const amountNum = parseFloat(amount);

// Validate budget
if (decodedMandate.max_budget && amountNum > decodedMandate.max_budget.value) {
console.error(`Fiduciary Validation Failed: Amount ${amountNum} exceeds mandate budget of ${decodedMandate.max_budget.value} ${decodedMandate.max_budget.currency}`);
return;
}

// Validate expiration
if (decodedMandate.exp < Math.floor(Date.now() / 1000)) {
console.error('Fiduciary Validation Failed: Mandate has expired');
return;
}
} catch (error) {
console.error(`Fiduciary Validation Failed: ${error.message}`);
return;
}

const settlementId = `x402_${crypto.randomBytes(8).toString('hex')}`;
const txHash = `0x${crypto.randomBytes(32).toString('hex')}`;

Expand Down
7 changes: 7 additions & 0 deletions src/agents/agent_6382d61c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"id": "agent_6382d61c",
"name": "TestAgent",
"did": "did:key:06ffeafa6f71c7bd83ad21ce4fb56e2c",
"wallet_address": "0x9fab4a57fb46aafd8ef02cbdaa192bfa14aa10e7",
"created_at": "2026-04-22T19:23:42.590Z"
}
1 change: 1 addition & 0 deletions src/mandates/mandate_be7dc63fcbb70f38.jwt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkaWQ6d2ViOm9wZW4tY29tbWVyY2UtcHJvdG9jb2wuaW8iLCJzdWIiOiJkaWQ6a2V5OmFnZW50XzYzODJkNjFjIiwidXNlcl9kaWQiOiJkaWQ6a2V5OnVzZXItbG9jYWwiLCJhZ2VudF9kaWQiOiJkaWQ6a2V5OmFnZW50XzYzODJkNjFjIiwibWFuZGF0ZV9pZCI6Im1hbmRhdGVfYmU3ZGM2M2ZjYmI3MGYzOCIsIm1heF9idWRnZXQiOnsidmFsdWUiOjEwMCwiY3VycmVuY3kiOiJVU0QifSwiZXhwIjoxNzc2OTcyMjIyLCJwdXJwb3NlX2NvZGUiOiJDTElfSVNTVUVEIiwiYWxsb3dlZF9tZXJjaGFudHMiOltdLCJpYXQiOjE3NzY4ODU4MjIsInR5cGUiOiJpbnRlbnRfbWFuZGF0ZSJ9.gDTkeD6IbOAQ04Yi0mX-lJLwNru3y4GfXMz4Rc999kQ
60 changes: 35 additions & 25 deletions src/services/tokenization.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class TokenizationService {
this.baseURL = config.baseURL || process.env.TOKENIZATION_BASE_URL || 'https://api.basistheory.com';
this.tenantId = config.tenantId || process.env.TOKENIZATION_TENANT_ID;
this.timeout = config.timeout || 30000;
this.strictMandateMode = config.strictMandateMode !== undefined ?
config.strictMandateMode : (process.env.STRICT_MANDATE_MODE === 'true');

if (!this.apiKey) {
throw new Error('Tokenization API key is required');
Expand Down Expand Up @@ -318,38 +320,46 @@ class TokenizationService {
* @returns {Promise<string>} Signature
*/
async signWithToken(tokenId, dataToSign, mandate, context = {}) {
try {
// Zero Trust Validation: Verify mandate if provided
if (mandate) {
const decodedMandate = await this.mandateService.verifyMandate(mandate);

// Validate budget if context amount is provided
if (context.amount) {
// Check Intent Mandate budget
if (decodedMandate.max_budget && context.amount > decodedMandate.max_budget.value) {
throw new Error(`Zero Trust Validation Failed: Amount ${context.amount} exceeds mandate budget of ${decodedMandate.max_budget.value}`);
}
// Check Cart Mandate total price
if (decodedMandate.total_price && context.amount !== decodedMandate.total_price) {
throw new Error(`Zero Trust Validation Failed: Amount ${context.amount} does not match cart mandate total of ${decodedMandate.total_price}`);
}
// Zero Trust Validation: Verify mandate BEFORE entering try/catch simulation block
if (mandate) {
let decodedMandate;
try {
decodedMandate = await this.mandateService.verifyMandate(mandate);
} catch (error) {
if (error.message.includes('jwt expired')) {
throw new Error('Zero Trust Validation Failed: Mandate has expired');
}
throw new Error(`Zero Trust Validation Failed: ${error.message}`);
}

// Validate merchant if context merchant is provided
if (context.merchant && decodedMandate.allowed_merchants?.length > 0) {
if (!decodedMandate.allowed_merchants.includes(context.merchant)) {
throw new Error(`Zero Trust Validation Failed: Merchant ${context.merchant} not authorized by mandate`);
}
// Validate budget if context amount is provided
if (context.amount) {
// Check Intent Mandate budget
if (decodedMandate.max_budget && context.amount > decodedMandate.max_budget.value) {
throw new Error(`Zero Trust Validation Failed: Amount ${context.amount} exceeds mandate budget of ${decodedMandate.max_budget.value}`);
}
// Check Cart Mandate total price
if (decodedMandate.total_price && context.amount !== decodedMandate.total_price) {
throw new Error(`Zero Trust Validation Failed: Amount ${context.amount} does not match cart mandate total of ${decodedMandate.total_price}`);
}
}

// Validate expiration
if (decodedMandate.exp < Math.floor(Date.now() / 1000)) {
throw new Error('Zero Trust Validation Failed: Mandate has expired');
// Validate merchant if context merchant is provided
if (context.merchant && decodedMandate.allowed_merchants?.length > 0) {
if (!decodedMandate.allowed_merchants.includes(context.merchant)) {
throw new Error(`Zero Trust Validation Failed: Merchant ${context.merchant} not authorized by mandate`);
}
} else if (process.env.STRICT_MANDATE_MODE === 'true') {
throw new Error('Zero Trust Validation Failed: Mandate required for signing in strict mode');
}

// Validate expiration
if (decodedMandate.exp < Math.floor(Date.now() / 1000)) {
throw new Error('Zero Trust Validation Failed: Mandate has expired');
}
} else if (this.strictMandateMode) {
throw new Error('Zero Trust Validation Failed: Mandate required for signing in strict mode');
}

try {
// In a real implementation, this would call a Basis Theory Reactor
// providing the tokenId. The Reactor would securely retrieve the
// secret and sign the data without exposing the key.
Expand Down
83 changes: 83 additions & 0 deletions tests/unit/tokenization.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const TokenizationService = require('../../src/services/tokenization');
const MandateService = require('../../src/services/mandate');
const jwt = require('jsonwebtoken');

describe('TokenizationService', () => {
let tokenizationService;
const signingKey = 'test-secret';
const apiKey = 'test-key';

beforeEach(() => {
tokenizationService = new TokenizationService({
apiKey,
mandateConfig: { signingKey },
strictMandateMode: true
});
process.env.NODE_ENV = 'test';
});

describe('signWithToken - Zero Trust Validation', () => {
it('should throw error if mandate is required but not provided in strict mode', async () => {
await expect(tokenizationService.signWithToken('token_123', 'data'))
.rejects.toThrow('Zero Trust Validation Failed: Mandate required for signing in strict mode');
});

it('should throw error if amount exceeds mandate budget', async () => {
const mandateService = new MandateService({ signingKey });
const mandate = await mandateService.issueIntentMandate({
userDid: 'did:key:user',
agentDid: 'did:key:agent',
maxBudget: 100
});

await expect(tokenizationService.signWithToken('token_123', 'data', mandate, { amount: 150 }))
.rejects.toThrow('Zero Trust Validation Failed: Amount 150 exceeds mandate budget of 100');
});

it('should throw error if merchant is not authorized', async () => {
const mandateService = new MandateService({ signingKey });
const mandate = await mandateService.issueIntentMandate({
userDid: 'did:key:user',
agentDid: 'did:key:agent',
maxBudget: 1000,
allowedMerchants: ['did:key:merchant_a']
});

await expect(tokenizationService.signWithToken('token_123', 'data', mandate, { merchant: 'did:key:merchant_b' }))
.rejects.toThrow('Zero Trust Validation Failed: Merchant did:key:merchant_b not authorized by mandate');
});

it('should throw error if mandate has expired', async () => {
const mandateService = new MandateService({ signingKey });
const mandate = await mandateService.issueIntentMandate({
userDid: 'did:key:user',
agentDid: 'did:key:agent',
maxBudget: 1000,
expiration: Math.floor(Date.now() / 1000) - 100 // Expired 100s ago
});

await expect(tokenizationService.signWithToken('token_123', 'data', mandate))
.rejects.toThrow('Zero Trust Validation Failed: Mandate has expired');
});

it('should successfully sign if mandate is valid', async () => {
const mandateService = new MandateService({ signingKey });
const mandate = await mandateService.issueIntentMandate({
userDid: 'did:key:user',
agentDid: 'did:key:agent',
maxBudget: 1000,
allowedMerchants: ['did:key:merchant_a']
});

const signature = await tokenizationService.signWithToken(
'token_123',
'data',
mandate,
{ amount: 500, merchant: 'did:key:merchant_a' }
);

expect(signature).toContain('0x_mock_signature');
expect(signature).toContain('validated_by_mandate');
});
});
});
91 changes: 91 additions & 0 deletions tests/unit/web3.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const Web3Service = require('../../src/services/web3');
const TokenizationService = require('../../src/services/tokenization');
const MandateService = require('../../src/services/mandate');

describe('Web3Service', () => {
let web3Service;
let tokenizationService;
const signingKey = 'test-secret';
const apiKey = 'test-key';

beforeEach(() => {
tokenizationService = new TokenizationService({
apiKey,
mandateConfig: { signingKey },
strictMandateMode: true
});
web3Service = new Web3Service(tokenizationService);
process.env.NODE_ENV = 'test';
});

describe('executeX402Settlement', () => {
it('should successfully execute settlement with a valid mandate', async () => {
const mandateService = new MandateService({ signingKey });
const mandate = await mandateService.issueIntentMandate({
userDid: 'did:key:user',
agentDid: 'did:key:agent',
maxBudget: 1000
});

const result = await web3Service.executeX402Settlement({
keyTokenId: 'token_123',
to: '0xRecipient',
amount: 500,
stablecoin: 'USDC',
mandate
});

expect(result.status).toBe('finalized');
expect(result.amount).toBe(500);
expect(result.stablecoin).toBe('USDC');
});

it('should fail if mandate budget is exceeded', async () => {
const mandateService = new MandateService({ signingKey });
const mandate = await mandateService.issueIntentMandate({
userDid: 'did:key:user',
agentDid: 'did:key:agent',
maxBudget: 100
});

await expect(web3Service.executeX402Settlement({
keyTokenId: 'token_123',
to: '0xRecipient',
amount: 500,
stablecoin: 'USDC',
mandate
})).rejects.toThrow('Zero Trust Validation Failed: Amount 500 exceeds mandate budget of 100');
});

it('should fail if no mandate is provided in strict mode', async () => {
await expect(web3Service.executeX402Settlement({
keyTokenId: 'token_123',
to: '0xRecipient',
amount: 500,
stablecoin: 'USDC'
})).rejects.toThrow('Zero Trust Validation Failed: Mandate required for signing in strict mode');
});
});

describe('sendTransaction', () => {
it('should successfully sign and "broadcast" transaction with valid mandate', async () => {
const mandateService = new MandateService({ signingKey });
const mandate = await mandateService.issueIntentMandate({
userDid: 'did:key:user',
agentDid: 'did:key:agent',
maxBudget: 1000
});

const result = await web3Service.sendTransaction({
keyTokenId: 'token_123',
to: '0xRecipient',
value: '0.1',
mandate,
context: { amount: 500 } // Simulation context
});

expect(result.status).toBe('pending');
expect(result.signedData).toBeDefined();
});
});
});
Loading