OptStuff

Create API Key Flow

Architecture documentation — API key creation flow, dual-key system, AES-256-GCM encryption, form validation, and database schema.

When a user creates an API key, the system generates a dual-key pair, encrypts the secret key with AES-256-GCM, and presents both keys in a one-time success screen. This document covers the full flow: UI interaction, backend processing, encryption, and database storage.

Overview

Users create API keys to authenticate image transformation requests. Each API key consists of a public key (pk_..., for identification) and a secret key (sk_..., for URL signing). The full public key can be viewed and copied from the API key list. The secret key is only shown once at creation or rotation time.

Image source domain restrictions are configured at the project level (in Settings → Domain Security), not per API key.

Flow Diagram

Key Concepts

API Key Structure

ComponentFormatDescription
Public Keypk_ + 16 bytes (base64url) = 25 charsPublic identifier, stored in plaintext. Used in URL ?key= param
Secret Keysk_ + 32 bytes (base64url)Used for HMAC-SHA256 URL signing, stored encrypted

Encryption

AspectDetails
AlgorithmAES-256-GCM (authenticated encryption)
Key DerivationHKDF (RFC 5869) from master secret
StorageOnly secretKey is encrypted; publicKey is stored in plaintext

Form Fields

FieldRequiredDescription
Key NameYesDescriptive name (e.g., "Production", "Development")
ExpirationNoOptional expiry date for automatic key invalidation

User Journey

1. Open Create Dialog

User navigates to a project's API Keys section and clicks the "Create API Key" button.

2. Fill Out Form

The dialog presents a form with:

  1. Key Name — Input field for a descriptive name
  2. Expiration — Select dropdown with preset options (30 days, 90 days, etc.)

3. Submit Form

When the user clicks "Create Key":

  1. Frontend validates inputs (name required)
  2. Sends request to apiKey.create tRPC mutation
  3. Button shows loading state during request

4. Backend Processing

The backend:

  1. Generates cryptographically secure publicKey and secretKey
  2. Encrypts secretKey using AES-256-GCM (publicKey is stored in plaintext)
  3. Stores values in database
  4. Returns publicKey and plaintext secretKey to client

Every mutation runs through protected tRPC procedures and project-level access verification before key operations are allowed.

5. Success Screen

Dialog transitions to success view showing:

  1. Secret Key — Full secret key with copy button and warning
  2. Public Key — Public identifier for URL ?key= parameter
  3. Usage Example — Code snippet showing how to sign URLs

Important: The secret key is only shown once. Users must copy and save it securely.

The public key (pk_...) can always be copied later from the API key list page (displayed masked with a copy button).

6. Complete

User clicks "Done" to close the dialog. The API key list automatically refreshes to show the new key.

Rotation uses a single DB transaction to revoke the old key and insert the replacement key atomically:

  1. Verify project access
  2. Revoke old key (revokedAt = now)
  3. Insert new key (inherits name, expiration, and per-key limit values)
  4. Invalidate old public-key cache (best effort)
  5. Return new plaintext secret once via UI dialog

Error Scenarios

ScenarioBehavior
Empty key nameSubmit button disabled
Network / server errorDialog remains on form step; user can retry (no dedicated inline error banner yet)

Form Validation Rules

FieldValidation
Key NameRequired, non-empty after trim
ExpirationOptional, must be future date if set
FilePurpose
src/modules/project-detail/ui/components/create-api-key-dialog.tsxDialog component with form and success states
src/modules/project-detail/ui/components/expiration-select.tsxExpiration dropdown component
src/server/lib/api-key.tsKey generation and encryption functions
src/server/api/routers/apiKey.tsBackend tRPC router for API key operations
src/modules/project-detail/ui/components/api-key-types.tsTypeScript types for API key data

Database Schema

The api_key table stores:

ColumnTypeDescription
idUUIDPrimary key
projectIdUUIDForeign key to projects table
nameStringUser-defined key name
publicKeyStringPublic identifier (plaintext, unique, indexed)
secretKeyStringSecret key (AES-256-GCM encrypted)
expiresAtTimestampOptional expiration date
createdAtTimestampCreation timestamp
lastUsedAtTimestampLast usage timestamp
revokedAtTimestampRevocation timestamp (null if active)

Security Considerations

  1. Secret Key Display — Only shown once at creation or rotation, never retrievable again
  2. Public Key Display — Public key (pk_...) is available in the API key list (masked display with copy button). Since it's a public identifier without signing capability, this is safe to expose
  3. Encryption at Rest — Secret key encrypted using HKDF-derived AES-256-GCM; public key stored in plaintext
  4. Domain Restrictions — Image source domains are managed at the project level, shared across all keys
  5. Expiration — Optional automatic key invalidation
  6. Audit TrailcreatedAt, lastUsedAt, revokedAt timestamps

Last updated on

On this page