fix: resolve config mismatch + auth security hardening

P0: config.py defined hanzo_commerce_* but billing code accessed
settings.commerce_* — causes AttributeError at runtime. Renamed fields
with AliasChoices for backward compat with HANZO_COMMERCE_* env vars.

P1: deps.py silently swallowed JWT errors (bare pass) — now logs them.
Virtual API key used fake UUID for missing projects — now raises 404.
Updated secrets.yaml to replace stale Stripe references with Commerce.
This commit is contained in:
2026-02-12 10:44:47 -08:00
parent e12b34a4cf
commit 82c099d128
3 changed files with 26 additions and 12 deletions
+10 -4
View File
@@ -1,6 +1,7 @@
"""API dependencies."""
import hashlib
import logging
import uuid
from typing import Annotated
@@ -14,6 +15,7 @@ from bootnode.core.cache import redis_client
from bootnode.db.models import ApiKey, Project, User
from bootnode.db.session import get_db
logger = logging.getLogger(__name__)
settings = get_settings()
@@ -63,9 +65,9 @@ async def verify_api_key_or_token(
)
project = proj_result.scalar_one_or_none()
return None, user, project
except jwt.InvalidTokenError:
except jwt.InvalidTokenError as e:
logger.debug("JWT verification failed: %s", e)
# Not a valid JWT, try as API key
pass
# Try API key
key_hash = hashlib.sha256(
@@ -120,10 +122,14 @@ async def verify_api_key(
# For JWT auth, create a virtual API key with user permissions
if user:
# Return a mock API key object for JWT-authenticated users
if not project:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No project found. Create a project first.",
)
virtual_key = ApiKey(
id=uuid.uuid4(),
project_id=project.id if project else uuid.UUID('00000000-0000-0000-0000-000000000001'),
project_id=project.id,
name=f"User: {user.email}",
key_hash="jwt-auth",
key_prefix="jwt_",
+15 -5
View File
@@ -3,7 +3,7 @@
from functools import lru_cache
from typing import Literal
from pydantic import Field, PostgresDsn, RedisDsn
from pydantic import AliasChoices, Field, PostgresDsn, RedisDsn
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -108,10 +108,20 @@ class Settings(BaseSettings):
webhook_timeout: int = 30
webhook_max_retries: int = 5
# Hanzo Commerce (Stripe-based billing)
hanzo_commerce_url: str = "https://commerce.hanzo.ai"
hanzo_commerce_api_key: str = ""
hanzo_commerce_webhook_secret: str = ""
# Hanzo Commerce (Square-based billing)
# Accepts both COMMERCE_URL and HANZO_COMMERCE_URL env vars for backward compat
commerce_url: str = Field(
default="https://commerce.hanzo.ai",
validation_alias=AliasChoices("commerce_url", "hanzo_commerce_url"),
)
commerce_api_key: str = Field(
default="",
validation_alias=AliasChoices("commerce_api_key", "hanzo_commerce_api_key"),
)
commerce_webhook_secret: str = Field(
default="",
validation_alias=AliasChoices("commerce_webhook_secret", "hanzo_commerce_webhook_secret"),
)
# ERC-4337 Bundler
bundler_private_key: str = ""
+1 -3
View File
@@ -34,9 +34,7 @@ type: Opaque
# stringData intentionally empty - use KMS or kubectl create secret
# Required secrets (inject via KMS, kubectl, or sealed-secrets):
# database-url, redis-url, jwt-secret, api-key-salt
# stripe-secret-key, stripe-webhook-secret, stripe-publishable-key
# stripe-product-id, stripe-price-payg
# stripe-price-growth-base, stripe-price-growth-metered
# commerce-url, commerce-api-key, commerce-webhook-secret
stringData: {}
---
# Chain configuration (non-sensitive)