fix(api-token): hash tokens at rest and show plaintext only once

Store API tokens as SHA-256 hashes instead of plaintext and return the token value only in the create response. List no longer exposes the token, and the UI drops the Show/Copy buttons in favor of a one-time reveal modal at creation.

Match hashes the presented bearer token before the constant-time compare, and a migration hashes any pre-existing plaintext rows in place so existing tokens keep authenticating. Docs and translations updated.
This commit is contained in:
MHSanaei
2026-06-03 22:57:50 +02:00
parent 7a72aeda7a
commit 4813a2fe00
21 changed files with 145 additions and 65 deletions
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "مثل central-panel-a",
"apiTokenNameRequired": "الاسم مطلوب",
"apiTokenEmpty": "لا توجد رموز بعد — أنشئ واحدًا لمصادقة الروبوتات أو اللوحات البعيدة.",
"apiTokenDeleteWarning": "أي عميل يستخدم هذا الرمز سيفقد المصادقة فورًا."
"apiTokenDeleteWarning": "أي عميل يستخدم هذا الرمز سيفقد المصادقة فورًا.",
"apiTokenCreatedTitle": "تم إنشاء الرمز",
"apiTokenCreatedNotice": "انسخ هذا الرمز الآن. لأسباب أمنية لا يتم تخزينه بصيغة قابلة للقراءة ولن يتم عرضه مرة أخرى."
},
"toasts": {
"modifySettings": "تم تغيير المعلمات.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "e.g. central-panel-a",
"apiTokenNameRequired": "Name is required",
"apiTokenEmpty": "No tokens yet — create one to authenticate bots or remote panels.",
"apiTokenDeleteWarning": "Any caller using this token will stop authenticating immediately."
"apiTokenDeleteWarning": "Any caller using this token will stop authenticating immediately.",
"apiTokenCreatedTitle": "Token created",
"apiTokenCreatedNotice": "Copy this token now. For security it is not stored in readable form and will not be shown again."
},
"toasts": {
"modifySettings": "The parameters have been changed.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "por ejemplo central-panel-a",
"apiTokenNameRequired": "El nombre es obligatorio",
"apiTokenEmpty": "Aún no hay tokens — crea uno para autenticar bots o paneles remotos.",
"apiTokenDeleteWarning": "Cualquier cliente que use este token dejará de autenticarse inmediatamente."
"apiTokenDeleteWarning": "Cualquier cliente que use este token dejará de autenticarse inmediatamente.",
"apiTokenCreatedTitle": "Token creado",
"apiTokenCreatedNotice": "Copia este token ahora. Por seguridad, no se almacena de forma legible y no se volverá a mostrar."
},
"toasts": {
"modifySettings": "Los parámetros han sido modificados.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "مثلاً central-panel-a",
"apiTokenNameRequired": "نام الزامی است",
"apiTokenEmpty": "هنوز توکنی وجود ندارد — برای احراز هویت ربات‌ها یا پنل‌های راه دور یکی بسازید.",
"apiTokenDeleteWarning": "هر کلاینتی که از این توکن استفاده می‌کند بلافاصله احراز هویتش قطع می‌شود."
"apiTokenDeleteWarning": "هر کلاینتی که از این توکن استفاده می‌کند بلافاصله احراز هویتش قطع می‌شود.",
"apiTokenCreatedTitle": "توکن ساخته شد",
"apiTokenCreatedNotice": "اکنون این توکن را کپی کنید. به‌دلیل امنیتی به‌صورت قابل‌خواندن ذخیره نمی‌شود و دوباره نمایش داده نخواهد شد."
},
"toasts": {
"modifySettings": "پارامترها تغییر کرده‌اند.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "misalnya central-panel-a",
"apiTokenNameRequired": "Nama wajib diisi",
"apiTokenEmpty": "Belum ada token — buat satu untuk mengautentikasi bot atau panel jarak jauh.",
"apiTokenDeleteWarning": "Setiap pemanggil yang menggunakan token ini akan berhenti terautentikasi segera."
"apiTokenDeleteWarning": "Setiap pemanggil yang menggunakan token ini akan berhenti terautentikasi segera.",
"apiTokenCreatedTitle": "Token dibuat",
"apiTokenCreatedNotice": "Salin token ini sekarang. Demi keamanan, token tidak disimpan dalam bentuk yang dapat dibaca dan tidak akan ditampilkan lagi."
},
"toasts": {
"modifySettings": "Parameter telah diubah.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "例: central-panel-a",
"apiTokenNameRequired": "名前は必須です",
"apiTokenEmpty": "トークンがまだありません — ボットやリモートパネルを認証するために作成してください。",
"apiTokenDeleteWarning": "このトークンを使用しているクライアントは直ちに認証できなくなります。"
"apiTokenDeleteWarning": "このトークンを使用しているクライアントは直ちに認証できなくなります。",
"apiTokenCreatedTitle": "トークンを作成しました",
"apiTokenCreatedNotice": "このトークンを今すぐコピーしてください。セキュリティ上、読み取り可能な形式では保存されず、再表示されません。"
},
"toasts": {
"modifySettings": "パラメーターが変更されました。",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "ex.: central-panel-a",
"apiTokenNameRequired": "O nome é obrigatório",
"apiTokenEmpty": "Nenhum token ainda — crie um para autenticar bots ou painéis remotos.",
"apiTokenDeleteWarning": "Qualquer cliente usando este token deixará de se autenticar imediatamente."
"apiTokenDeleteWarning": "Qualquer cliente usando este token deixará de se autenticar imediatamente.",
"apiTokenCreatedTitle": "Token criado",
"apiTokenCreatedNotice": "Copie este token agora. Por segurança, ele não é armazenado de forma legível e não será exibido novamente."
},
"toasts": {
"modifySettings": "Os parâmetros foram alterados.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "например, central-panel-a",
"apiTokenNameRequired": "Имя обязательно",
"apiTokenEmpty": "Токенов пока нет — создайте один для аутентификации ботов или удалённых панелей.",
"apiTokenDeleteWarning": "Любой клиент, использующий этот токен, немедленно потеряет аутентификацию."
"apiTokenDeleteWarning": "Любой клиент, использующий этот токен, немедленно потеряет аутентификацию.",
"apiTokenCreatedTitle": "Токен создан",
"apiTokenCreatedNotice": "Скопируйте этот токен сейчас. В целях безопасности он не хранится в читаемом виде и больше не будет показан."
},
"toasts": {
"modifySettings": "Настройки изменены",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "örn. central-panel-a",
"apiTokenNameRequired": "Ad zorunludur",
"apiTokenEmpty": "Henüz token yok — bot veya uzak panelleri doğrulamak için bir tane oluşturun.",
"apiTokenDeleteWarning": "Bu tokenı kullanan tüm istemciler anında kimlik doğrulamasını kaybeder."
"apiTokenDeleteWarning": "Bu tokenı kullanan tüm istemciler anında kimlik doğrulamasını kaybeder.",
"apiTokenCreatedTitle": "Belirteç oluşturuldu",
"apiTokenCreatedNotice": "Bu belirteci şimdi kopyalayın. Güvenlik nedeniyle okunabilir biçimde saklanmaz ve tekrar gösterilmez."
},
"toasts": {
"modifySettings": "Parametreler değiştirildi.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "наприклад, central-panel-a",
"apiTokenNameRequired": "Назва обов'язкова",
"apiTokenEmpty": "Поки немає токенів — створіть один для автентифікації ботів або віддалених панелей.",
"apiTokenDeleteWarning": "Будь-який клієнт, що використовує цей токен, негайно втратить автентифікацію."
"apiTokenDeleteWarning": "Будь-який клієнт, що використовує цей токен, негайно втратить автентифікацію.",
"apiTokenCreatedTitle": "Токен створено",
"apiTokenCreatedNotice": "Скопіюйте цей токен зараз. З міркувань безпеки він не зберігається у читабельному вигляді й більше не відображатиметься."
},
"toasts": {
"modifySettings": "Параметри було змінено.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "ví dụ: central-panel-a",
"apiTokenNameRequired": "Tên là bắt buộc",
"apiTokenEmpty": "Chưa có token nào — tạo một token để xác thực bot hoặc panel từ xa.",
"apiTokenDeleteWarning": "Mọi client đang dùng token này sẽ ngừng xác thực ngay lập tức."
"apiTokenDeleteWarning": "Mọi client đang dùng token này sẽ ngừng xác thực ngay lập tức.",
"apiTokenCreatedTitle": "Đã tạo token",
"apiTokenCreatedNotice": "Hãy sao chép token này ngay bây giờ. Vì lý do bảo mật, token không được lưu ở dạng đọc được và sẽ không hiển thị lại."
},
"toasts": {
"modifySettings": "Các tham số đã được thay đổi.",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "例如 central-panel-a",
"apiTokenNameRequired": "名称必填",
"apiTokenEmpty": "暂无令牌 — 创建一个用于认证机器人或远程面板。",
"apiTokenDeleteWarning": "使用此令牌的任何调用方将立即无法认证。"
"apiTokenDeleteWarning": "使用此令牌的任何调用方将立即无法认证。",
"apiTokenCreatedTitle": "令牌已创建",
"apiTokenCreatedNotice": "请立即复制此令牌。出于安全考虑,它不会以可读形式存储,也不会再次显示。"
},
"toasts": {
"modifySettings": "参数已更改。",
+3 -1
View File
@@ -1119,7 +1119,9 @@
"apiTokenNamePlaceholder": "例如 central-panel-a",
"apiTokenNameRequired": "名稱必填",
"apiTokenEmpty": "尚無令牌 — 建立一個以認證機器人或遠端面板。",
"apiTokenDeleteWarning": "使用此令牌的任何呼叫方將立即無法認證。"
"apiTokenDeleteWarning": "使用此令牌的任何呼叫方將立即無法認證。",
"apiTokenCreatedTitle": "權杖已建立",
"apiTokenCreatedNotice": "請立即複製此權杖。基於安全考量,它不會以可讀形式儲存,也不會再次顯示。"
},
"toasts": {
"modifySettings": "參數已更改。",