Skip to main content

Custom JavaScript actions

Discover how to add custom JavaScript action to Vault

Custom actions are JavaScript functions loaded into Vault (using bundles) that can be invoked when needed using the Invoke Action API. In the function code you can interact with data stored in your Vault instance without exposing it outside of the Vault which helps you meet regulatory and security requirements.

The action JavaScript function receives the Invoke Action API request body as a parameter while the function returned value is returned as the API response body. Both the request and response body are treated in the function as simple JavaScript objects while Vault deserialized and serialized them from and into JSON in the API.

Custom actions JavaScript code is loaded into Vault in a bundle. This page describes how to manage and invoke custom actions. For information on creating a bundle, see Bundles.

For simple use cases Vault includes a built-in action to make an HTTP call.

Manage custom actions

The action code needs to be added with a bundle. See bundles and Add Bundle for more details.

Custom actions are added by the Add Action operation and deleted by the Delete Action operation. To update the action's JavaScript code, you update the bundle using the Update Bundle operation.

Write an action

A handler accepts a context argument and returns an object. The returned object is the response body in JSON format. See Bundle handlers for the full handler specification and details of global functions.

The action runs using a role configured by the Add Action operation, not the role of the user or JWT that invoked the action. This is done deliberatly to allow you to authorize Vault to access data on your behalf without exposing the data to the backend or requestor.

vault object methods

vault.crypto.decrypt

Used to decrypt objects. The function's signature is a compatible subset of the Decrypt operation request. Reason, Custom audit data and enforcement are the same of the caller's.

Signature: (params: DecryptInput) => Promise<[]DecryptOutputObject>;

Where types are defined as:

type DecryptInput = {
collection: string;
requestBody: DecryptionRequest[];
options?: string[];
}

type DecryptionRequest = {
encryptedObject: {
ciphertext: string;
scope?: string;
};
props: string[];
}

type DecryptOutputObject = {
fields: { [key:string]: unknown };
metadata?: {
expiration: string;
scope: string;
tags: []string;
type: "randomized" | "deterministic";
};
}

vault.deref

Dereference receives a key-value object with each string value being a vault global identifier.

It evaluates the global identifier and returns a new object with the same keys and the evaluated values of the global identifiers.

Signature: <P extends { [key:string]: string }>(params: P): Promise<{ [K in keyof P]: unknown }>;

For example:

const { maskedNumber, customer } = await vault.deref({
maskedNumber: 'pvlt:detoeknize:credit_cards:number.mask:bb5e17ce-38b1-4b3f-9b4b-40801f9672d1:',
customer: 'pvlt:read_object:customers::bb5e17ce-38b1-4b3f-9b4b-40801f9672d1:',
});

console.log(maskedNumber); // '************1234'
console.log(customer); // { id: 'bb5e17ce-38b1-4b3f-9b4b-40801f9672d1', name: 'John Doe' }

Example action: Decrypt an object and send it to a payment processor

const cardProcessorURL = 'https://example.com';

module.exports.get_cc_card = {
type: 'action',
description: 'Get credit card.',
async handler(context) {
const { body } = context;
// Validate the input
if (
body === null ||
typeof body !== 'object' ||
!('encryptedParams' in body) ||
typeof body.encryptedParams !== 'string' ||
!('reveal' in body) ||
typeof body.reveal !== 'boolean'
) {
return { ok: false, message: 'Invalid input. Expecting a string.' };
}

// Decrypt the params to get the card reference and the auth details.
const { params } = await vault.crypto.decrypt({ collection, requestBody: [
{encryptedObject: { ciphertext: body.encryptedParams }},
]});
const { cc_ref, service_auth } = params[0].fields;

try {
// Send API call to card processor API.
const response = await fetch(`${cardProcessorURL}/api/cards/${cc_ref}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${service_auth}`,
},
});

// Validate the response
if (!response.ok) {
return { ok: false, message: 'Failed to retrieve card' };
}

// Parse the response as JSON
const { cc_number, expiry_date, cvv } = await response.json();

// Return a success response
return {
ok: true,
card: body.reveal
? {
number: cc_number,
exp: expiry_date,
cvv,
}
: {
number: `•••• •••• •••• ${cc_number.slice(-4)}`,
exp: '••/••',
cvx2: '•••',
},
};
} catch (error) {
// Handle errors
console.log(error);
return { ok: false, message: 'Failed to retrieve card' };
}
},
};