How to Securely Store APIMart API Keys in the Browser for GPT-Image2

Use the helper functions in src/apimartClient.js to store APIMart API keys in localStorage under the namespace gpt-image-2-apimart-key:v1, with built-in validation, masking, and secure retrieval patterns that prevent accidental exposure.

The GPT-Image2 library by freestylefly delegates all browser-side credential management to a dedicated storage module. This design keeps secrets out of memory when possible, validates input before persistence, and provides safe display utilities that never reveal full keys to the UI.

LocalStorage Implementation in apimartClient.js

All storage operations live in src/apimartClient.js. The module wraps window.localStorage in a browserStorage() helper that returns null when storage APIs are unavailable (privacy modes, disabled cookies, etc.).

Key functions exported from this file:

  • getStoredApimartKey() – Reads the raw key, trims whitespace, returns empty string on failure
  • saveStoredApimartKey(key) – Validates length (≤ 512 bytes), rejects empty strings and line breaks, returns boolean success
  • clearStoredApimartKey() – Removes the entry entirely
  • maskApimartKey(key) – Returns display-safe string like ••••••••abcd

The storage key is hardcoded as gpt-image-2-apimart-key:v1 to avoid collisions with other applications.

Storing and Retrieving API Keys

Saving a New Key

Before persisting, the library enforces three validation rules:

  1. Key must be non-empty
  2. Key must not exceed 512 bytes
  3. Key must not contain newline characters
import { saveStoredApimartKey, getStoredApimartKey } from './apimartClient.js';

function handleLogin(rawKey) {
  const success = saveStoredApimartKey(rawKey);
  if (!success) {
    console.error('Key rejected: empty, too long, or contains line breaks');
    return false;
  }
  
  // Verify against APIMart servers before confirming to user
  const storedKey = getStoredApimartKey();
  return verifyPersonalApimartKey(storedKey);
}

Retrieving for API Calls

Always check for existence before making requests. The submitPersonalGeneration function (also in apimartClient.js) requires the key as an explicit parameter rather than accessing storage internally—this keeps dependencies explicit and testable.

import { getStoredApimartKey, submitPersonalGeneration } from './apimartClient.js';

async function generateImage(prompt, language) {
  const apiKey = getStoredApimartKey();
  if (!apiKey) {
    throw new Error('APIMart API key not configured');
  }
  
  return submitPersonalGeneration(prompt, apiKey, language);
}

Masking and UI Safety

The maskApimartKey() function prevents accidental exposure in DOM or logs. It preserves only the last four characters:

import { maskApimartKey } from './apimartClient.js';

function updateSettingsUI() {
  const key = getStoredApimartKey();
  const display = maskApimartKey(key); // "••••••••a3f9"
  
  document.getElementById('key-preview').textContent = display;
}

Critical rule: Never interpolate the raw key into template strings, console.log, or exception messages. The masking function is the only approved path to string representation.

Clearing Credentials on Logout

Explicit cleanup prevents stale keys in shared browser profiles:

import { clearStoredApimartKey } from './apimartClient.js';

function logoutUser() {
  clearStoredApimartKey();
  // Optionally redirect or refresh state
  window.location.href = '/';
}

Security Architecture in the Source Code

The implementation addresses five common attack vectors:

Risk Mitigation in Code
XSS via storage errors All reads/wraps wrapped in try...catch; failures silently return safe defaults
Overlong input Byte-length check rejects keys > 512 bytes before storage
Log leakage No console.* statements include the raw key; boolean returns hide values
Network interception All APIMart calls use HTTPS only (https://api.apimart.ai per shared/apimart.js)
** shoulder-surfing** Masking function reveals maximum 4 characters

The constants and request builders in shared/apimart.js enforce TLS at the transport layer, while src/apimartClient.test.js contains unit tests verifying correct key handling and failure modes.

Complete Integration Example

// app.js — typical GPT-Image2 client integration
import {
  getStoredApimartKey,
  saveStoredApimartKey,
  clearStoredApimartKey,
  maskApimartKey,
  verifyPersonalApimartKey,
  submitPersonalGeneration,
} from './src/apimartClient.js';

export class ApiKeyManager {
  async authenticate(rawKey) {
    const saved = saveStoredApimartKey(rawKey);
    if (!saved) throw new Error('Invalid key format');
    
    // Server-side verification catches revoked or mistyped keys
    const valid = await verifyPersonalApimartKey(getStoredApimartKey());
    if (!valid) {
      clearStoredApimartKey();
      throw new Error('Key rejected by APIMart');
    }
    return true;
  }
  
  async generate(prompt, language = 'en') {
    const key = getStoredApimartKey();
    if (!key) throw new Error('Authentication required');
    return submitPersonalGeneration(prompt, key, language);
  }
  
  get maskedKey() {
    return maskApimartKey(getStoredApimartKey());
  }
  
  deauthenticate() {
    clearStoredApimartKey();
  }
}

Summary

  • Storage location: Browser localStorage under gpt-image-2-apimart-key:v1 (source)
  • Validation: Non-empty, ≤ 512 bytes, no line breaks before persistence
  • Masking: Always use maskApimartKey() for UI display—never expose raw keys
  • Cleanup: Call clearStoredApimartKey() on logout or credential rotation
  • Transport: HTTPS enforced via APIMART_API_BASE_URL in shared/apimart.js
  • Testing: Verify behavior with src/apimartClient.test.js

Frequently Asked Questions

What happens if localStorage is disabled or unavailable?

The browserStorage() helper returns null when window.localStorage throws or is undefined. All dependent functions gracefully degrade: getStoredApimartKey() returns empty string, saveStoredApimartKey() returns false, and clearStoredApimartKey() becomes a no-op. Applications should detect this state and prompt for temporary session-based authentication or guide users to enable storage.

Can I store the APIMart key somewhere other than localStorage?

The current implementation in src/apimartClient.js hardcodes the storage backend to localStorage. For alternative storage (IndexedDB, sessionStorage, or secure contexts like WebAuthn), you would need to fork the module and replace browserStorage() while maintaining the same validation and masking interfaces. The test suite in src/apimartClient.test.js provides the contract your replacement must satisfy.

How does the library prevent the API key from appearing in browser devtools?

Three mechanisms work together: (1) maskApimartKey() is the only export that converts keys to display strings, (2) no function logs the raw key to console, and (3) storage operations catch exceptions without re-throwing key contents. However, localStorage contents remain visible in the Application tab—this is inherent to client-side storage. For production deployments requiring stronger guarantees, proxy requests through your own backend rather than calling APIMart directly from the browser.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →