Skip to content

API Key Authentication

API keys allow external clients to bypass PIN authentication when accessing protected *.nominate.ai endpoints.

Overview

With API Key:
  Request + X-API-Key header → NGINX → /auth/verify → validate key → 200 → backend

Without API Key (browser):
  Request → NGINX → /auth/verify → 401 → PIN pad → cookie → 200 → backend

Creating API Keys

# Activate environment
source ~/.pyenv/versions/nominates/bin/activate
cd /home/bisenbek/projects/nominate/cbauth

# Create a new API key
python -m app.cli create-key --name "partner-api" --note "For external integration"

Output:

  Created API key: partner-api
  Prefix: cbauth_xK9m...
  Note: For external integration

============================================================
  API KEY (save this - shown only once!):
  cbauth_xK9mPq2wR7nL4jF8vBnM3kH5tY9uW1qE6rT0pL2s
============================================================

  Usage:
    curl -H 'X-API-Key: cbauth_xK9m...' https://api.nominate.ai/...

Important: The full API key is only shown once at creation. Store it securely.

Managing Keys

List Keys

# Active keys only
python -m app.cli list-keys

# Include revoked keys
python -m app.cli list-keys --all

Output:

Name                 Prefix           Active   Last Used            Note
------------------------------------------------------------------------------------------
partner-api          cbauth_xK9m...   yes      2026-01-06T18:55:17  For external integration
mobile-app           cbauth_bT4n...   yes      never                iOS app backend
old-integration      cbauth_mL2p...   REVOKED  2026-01-05T10:30:00  Deprecated

Revoke Keys

# By name
python -m app.cli revoke-key --name "partner-api"

# By prefix
python -m app.cli revoke-key --prefix "cbauth_xK9m"

Revoked keys immediately stop working. The action cannot be undone.

Using API Keys

Header Options

# Option 1: X-API-Key header (recommended)
curl -H "X-API-Key: cbauth_xK9mPq2wR7nL4jF8..." https://models.nominate.ai/health

# Option 2: Authorization Bearer
curl -H "Authorization: Bearer cbauth_xK9mPq2wR7nL4jF8..." https://models.nominate.ai/health

Example: Python

import requests

API_KEY = "cbauth_xK9mPq2wR7nL4jF8..."

response = requests.get(
    "https://models.nominate.ai/health",
    headers={"X-API-Key": API_KEY}
)

Example: JavaScript

const API_KEY = "cbauth_xK9mPq2wR7nL4jF8...";

fetch("https://models.nominate.ai/health", {
  headers: { "X-API-Key": API_KEY }
})

Security Notes

  1. Key Storage: Keys are SHA256 hashed before storage in data/api_keys.json
  2. Constant-Time Comparison: Key validation uses hmac.compare_digest to prevent timing attacks
  3. Usage Tracking: last_used_at is updated on each successful authentication
  4. No Expiration: Keys don't expire automatically - revoke manually when needed
  5. Key Format: cbauth_ prefix + 43 characters of URL-safe base64

Storage Location

Keys are stored in /home/bisenbek/projects/nominate/cbauth/data/api_keys.json

This file is excluded from git via .gitignore.

Troubleshooting

Key returns 401

  1. Check key is not revoked: python -m app.cli list-keys --all
  2. Verify key format starts with cbauth_
  3. Check NGINX is passing headers (see below)

NGINX not passing headers

Ensure /etc/nginx/snippets/pin-gate-auth.conf includes:

proxy_set_header X-API-Key $http_x_api_key;
proxy_set_header Authorization $http_authorization;

Then reload: sudo nginx -t && sudo systemctl reload nginx