System Architecture
Core Features
Data Management
Frontend Components
Extensibility
The following files were used as context for generating this wiki page:
This document outlines the execution flow when a user initiates a "Test Connection" action from the Integration Platform Test Page, specifically focusing on the backend process of decrypting stored credentials. This flow is critical for ensuring that sensitive integration credentials, such as API keys or OAuth tokens, are securely stored and retrieved only when needed, and then correctly decrypted for use by the integration logic.
The process begins with a user interaction on the frontend, triggering an API call to the backend. The backend then retrieves the encrypted credentials from its vault, performs a multi-step decryption process involving key derivation, and finally makes the decrypted credentials available for the connection testing logic. This secure handling of credentials is fundamental to maintaining the integrity and confidentiality of integration data.
The journey begins on the IntegrationPlatformTestPage, a React component responsible for displaying and managing integration connections. This page provides a user interface to view available providers, existing connections, and actions like "Test Connection." When a user clicks the "Test" button associated with a specific connection, it initiates the handleTestConnection function.
Sources: apps/app/src/app/(app)/[orgId]/integrations/platform-test/page.tsx:557-557
The handleTestConnection function is a client-side asynchronous operation defined within the IntegrationPlatformTestPage component. It takes the connectionId and providerSlug as arguments. Its primary role is to make an API call to the backend to test the specified connection. It uses the testConnection mutation provided by the useIntegrationMutations hook, which abstracts the actual HTTP request. Upon receiving a response from the API, it logs the success or failure message to the UI's action log and refreshes the list of connections.
Sources: apps/app/src/app/(app)/[orgId]/integrations/platform-test/page.tsx:590-599
This method is an API endpoint within the ConnectionsController on the backend, accessible via a POST request to /v1/integrations/connections/:id/test. It receives the connectionId from the URL parameter. Its responsibility is to orchestrate the connection testing process.
First, it retrieves the connection details from the database. Crucially, it then calls this.credentialVaultService.getDecryptedCredentials(connection.id) to fetch and decrypt the sensitive credentials associated with that connection. If no credentials are found, it throws an error. For AWS connections, it delegates to a specific testAwsConnection method. For other providers, it attempts to use a testConnection handler defined in the integration's manifest. The outcome (success or failure) is then used to update the connection's status in the database.
Sources: apps/api/src/integration-platform/controllers/connections.controller.ts:608-649
Located in the CredentialVaultService, this asynchronous method is responsible for retrieving the latest encrypted credential version for a given connectionId and then decrypting its payload. It fetches the latestVersion from the credentialRepository. If no version is found, it returns null.
It then iterates through the encryptedPayload of the credential. For each field that is identified as EncryptedData (meaning it contains encrypted, iv, tag, and salt properties), it calls this.decrypt(value) to obtain the plaintext string. Other fields (like token_type or scope for OAuth) are returned as-is. Array values are also handled, with each encrypted item within the array being decrypted individually. The method returns a Record containing all the decrypted credential values.
Sources: apps/api/src/integration-platform/services/credential-vault.service.ts:182-215
This is the core decryption method within the CredentialVaultService. It takes an EncryptedData object as input, which contains the base64-encoded encrypted text, initialization vector (IV), authentication tag, and salt.
The method first retrieves the master SECRET_KEY from environment variables. It then converts the base64-encoded components (encrypted text, IV, tag, salt) back into Node.js Buffer objects. The crucial step here is calling this.deriveKey(secretKey, salt) to generate the symmetric decryption key. With the derived key, IV, and tag, it initializes a createDecipheriv cipher, updates it with the encrypted data, and finalizes the decryption. The resulting plaintext is returned as a UTF-8 string.
Sources: apps/api/src/integration-platform/services/credential-vault.service.ts:74-89
The deriveKey method, also part of the CredentialVaultService, is responsible for securely generating a cryptographic key from a secret and a salt. It uses Node.js's crypto.scryptSync function, a password-based key derivation function (PBKDF) designed to be computationally intensive, thus making brute-force attacks more difficult.
It takes the master secret (the SECRET_KEY from environment variables) and a salt (randomly generated for each encryption) as input. It then applies scryptSync with specific parameters: N (CPU/memory cost factor), r (block size), and p (parallelization factor), and requests a KEY_LENGTH of 32 bytes. The output is a Buffer containing the derived key, which is then used by the decrypt method for the actual decryption process.
Sources: apps/api/src/integration-platform/services/credential-vault.service.ts:68-72
IntegrationPlatformTestPage), crossing the network boundary to the NestJS API gateway (ConnectionsController), and then delving into a dedicated backend service (CredentialVaultService) for secure credential handling. This clear separation of concerns enhances maintainability and security.SECRET_KEY: The getSecretKey method explicitly checks for the SECRET_KEY environment variable. If this critical key is not set, the application will crash during decryption attempts, rendering all encrypted credentials unusable.EncryptedData (encrypted text, IV, tag, salt) is corrupted or tampered with, the decryption process will fail, likely resulting in an Authentication Tag Mismatch error, indicating data integrity compromise.SECRET_KEY: If the SECRET_KEY used for decryption does not match the one used for encryption, the deriveKey function will produce a different key, leading to decryption failure.credentialRepository would prevent decryption.deriveKey method uses scryptSync, a computationally intensive key derivation function. While this is excellent for security (making brute-force attacks harder), its synchronous nature means it blocks the Node.js event loop during execution. For a single connection test, this overhead is acceptable. However, in scenarios requiring high-volume, concurrent decryption operations, this could become a performance bottleneck. The parameters (N, r, p) for scryptSync are chosen to provide a balance between security and acceptable performance.scryptSync with a unique salt for each encryption, along with AES-256-GCM (which provides authenticated encryption), demonstrates robust cryptographic practices for protecting sensitive data at rest. The separation of the master secret key (SECRET_KEY) from the application code and its reliance on environment variables is also a good security practice.