zerokey
Version:
Zero-knowledge cross-domain secret sharing library using ECDH encryption
127 lines (125 loc) • 4.46 kB
text/typescript
/**
* Validated and required parameters for the zero-knowledge secret sharing flow.
* These are stored globally after validation for use during the secret transfer process.
*/
interface ZerokeyParams {
publicKey: string;
redirect: string;
state: string;
}
/**
* Configuration options for the secret server.
*/
interface SecretServerOptions {
/**
* Optional callback to validate the redirect URL.
* If provided, must return true for the URL to be accepted.
* This provides an additional security layer to prevent unauthorized domains
* from requesting secrets.
*
* @param url - The redirect URL to validate
* @returns True if the URL should be allowed, false otherwise
*
* @example
* // Only allow specific domain
* validateCallbackUrl: (url) => url.startsWith('https://myapp.com')
*
* @example
* // Allow multiple domains
* validateCallbackUrl: (url) => {
* const allowed = ['https://app1.com', 'https://app2.com'];
* return allowed.some(domain => url.startsWith(domain));
* }
*/
validateCallbackUrl?: (url: string) => boolean;
}
/**
* Global window extension to store validated zerokey parameters.
* This allows the parameters to persist between initialization and secret setting.
*/
declare global {
interface Window {
zerokeyParams?: ZerokeyParams;
}
}
/**
* Initializes the zero-knowledge secret server handler.
* This function should be called when the secret server page loads.
* It parses the query parameters, validates them, and prepares the system to receive a secret.
*
* The function expects the following query parameters:
* - publicKey: RSA public key for encrypting the secret
* - redirect: URL to redirect to after processing
* - state: CSRF protection state parameter
*
* If a secret has already been set via `setSecret()` before initialization,
* it will immediately process and redirect with the encrypted secret.
*
* @param {SecretServerOptions} options - Optional configuration for the secret server
* @param {Function} options.validateCallbackUrl - Optional callback to validate redirect URLs
* @returns {void}
*
* @throws {Error} Logs errors if required parameters are missing or invalid
*
* @example
* // Basic initialization
* import { initSecretServer } from 'zerokey/server';
*
* document.addEventListener('DOMContentLoaded', () => {
* initSecretServer();
* });
*
* @example
* // With domain validation for security
* initSecretServer({
* validateCallbackUrl: (url) => url.startsWith('https://myapp.com')
* });
*
* @example
* // Allow multiple trusted domains
* initSecretServer({
* validateCallbackUrl: (url) => {
* const trustedDomains = [
* 'https://app.example.com',
* 'https://staging.example.com',
* 'http://localhost:3000' // for development
* ];
* return trustedDomains.some(domain => url.startsWith(domain));
* }
* });
*/
declare function initSecretServer(options?: SecretServerOptions): void;
/**
* Sets the secret that will be encrypted and transferred to the requesting application.
* This function should be called after the user has authenticated and the server
* has determined what secret (e.g., API key, token) to share.
*
* If `initSecretServer()` has already been called and valid parameters are present,
* this will immediately encrypt the secret and redirect. Otherwise, it stores the
* secret until initialization occurs.
*
* The secret is encrypted client-side using the public key provided in the query parameters,
* ensuring the secret server never sees the public key and the requesting app never sees
* the plaintext secret - maintaining zero-knowledge properties.
*
* @param {string} secret - The plaintext secret to be encrypted and transferred
* @returns {void}
*
* @throws {Error} Logs an error if the secret is empty
*
* @example
* // After user authentication:
* const userApiKey = await generateApiKeyForUser(userId);
* setSecret(userApiKey);
* // User is automatically redirected with encrypted secret
*
* @example
* // In a form submission handler:
* document.getElementById('secret-form').addEventListener('submit', (e) => {
* e.preventDefault();
* const secret = document.getElementById('secret-input').value;
* setSecret(secret);
* });
*/
declare function setSecret(secret: string): void;
export { initSecretServer, setSecret };