From c62cafc2023b1bae1f8a8edf82fd12a31173b40c Mon Sep 17 00:00:00 2001 From: Oliver Hofmann Date: Tue, 28 Apr 2026 10:23:37 +0200 Subject: [PATCH] Store key_prefix for readable key display instead of masked hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last-4 of the SHA-256 hash was meaningless for identification. Now storing the first 12 chars of the plaintext key as key_prefix, displayed as 'sk-aBcDeFgH••••••••' — consistent with what the user sees at creation time and how GitHub/OpenAI handle it. --- backend/crud.py | 1 + backend/models.py | 1 + backend/schemas.py | 1 + frontend/src/main.jsx | 4 ++-- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/crud.py b/backend/crud.py index 36b96f8..260d0d8 100644 --- a/backend/crud.py +++ b/backend/crud.py @@ -40,6 +40,7 @@ def create_api_key( db_key = APIKey( name=name, key=_hash_api_key(raw_key), + key_prefix=raw_key[:12], expires_at=expires_at, daily_tokens=daily_tokens, monthly_tokens=monthly_tokens, diff --git a/backend/models.py b/backend/models.py index 8a0b8ac..cdf03c2 100644 --- a/backend/models.py +++ b/backend/models.py @@ -10,6 +10,7 @@ class APIKey(Base): id = Column(Integer, primary_key=True, index=True) name = Column(String) key = Column(String, unique=True, index=True) + key_prefix = Column(String) is_active = Column(Boolean, default=True) created_at = Column(DateTime(timezone=True), default=_now) expires_at = Column(DateTime(timezone=True), nullable=True) diff --git a/backend/schemas.py b/backend/schemas.py index 80edc38..51b7ad8 100644 --- a/backend/schemas.py +++ b/backend/schemas.py @@ -14,6 +14,7 @@ class APIKey(BaseModel): id: int name: str key: str + key_prefix: Optional[str] = None is_active: bool created_at: datetime expires_at: Optional[datetime] = None diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 581f89c..ac81ca1 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom/client'; import axios from 'axios'; import './styles.css'; -const maskKey = (key) => `••••••••${key.slice(-4)}`; +const displayKey = (prefix) => prefix ? `${prefix}••••••••` : '••••••••••••'; function authHeaders(token) { return { Authorization: `Bearer ${token}` }; @@ -298,7 +298,7 @@ function App() { {key.id} {key.name} - {maskKey(key.key)} + {displayKey(key.key_prefix)} {key.is_active ? 'Aktiv' : 'Inaktiv'} {key.expires_at ? new Date(key.expires_at).toLocaleDateString('de-DE', { timeZone: 'Europe/Berlin' }) : '∞'} {key.daily_tokens ?? '∞'}