feat(auth): génération automatique du JWT secret au démarrage
- secret.ts: génère un secret aléatoire 96 chars hex si absent - secret.ts: persiste dans data/secret.key (mode 600) - secret.ts: priorité env JWT_SECRET > secret.key > génération - auth: import JWT_SECRET depuis secret.ts au lieu de process.env - docker-compose: JWT_SECRET optionnel (géré automatiquement) - readme: mise à jour — JWT_SECRET n'est plus obligatoire - feat: add favicon.ico
This commit is contained in:
1 parent
867186db98
commit
e7afe138e1
3 files changed
+48
-4
No files matched your search
+3
-3
@@ -3,9 +3,9 @@ import path from 'path'
|
||||
import bcrypt from 'bcrypt'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import type { Request, Response, NextFunction } from 'express'
|
||||
import { JWT_SECRET } from './secret.js'
|
||||
|
||||
const DATA_PATH = path.join(process.cwd(), 'data', 'auth.json')
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'fankarr-secret-change-me'
|
||||
const SALT_ROUNDS = 10
|
||||
|
||||
interface AuthData {
|
||||
@@ -63,7 +63,7 @@ export function authSetup(req: Request, res: Response): void {
|
||||
const passwordHash = bcrypt.hashSync(password, SALT_ROUNDS)
|
||||
writeAuth({ username, passwordHash })
|
||||
|
||||
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '30d' })
|
||||
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '1d' })
|
||||
res.cookie('fankarr_token', token, { httpOnly: true, sameSite: 'lax' })
|
||||
res.json({ success: true })
|
||||
}
|
||||
@@ -87,7 +87,7 @@ export function authLogin(req: Request, res: Response): void {
|
||||
return
|
||||
}
|
||||
|
||||
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '30d' })
|
||||
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '1d' })
|
||||
res.cookie('fankarr_token', token, { httpOnly: true, sameSite: 'lax' })
|
||||
res.json({ success: true })
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* secret.ts
|
||||
* =========
|
||||
* Génère et persiste le JWT secret dans data/secret.key
|
||||
* Si JWT_SECRET est défini en variable d'environnement, il est utilisé tel quel.
|
||||
* Sinon, on lit data/secret.key — s'il n'existe pas, on en génère un et on le sauvegarde.
|
||||
*/
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import crypto from 'crypto'
|
||||
|
||||
const SECRET_PATH = path.join(process.cwd(), 'data', 'secret.key')
|
||||
|
||||
function generateSecret(): string {
|
||||
return crypto.randomBytes(48).toString('hex')
|
||||
}
|
||||
|
||||
function loadOrCreateSecret(): string {
|
||||
// 1. Variable d'environnement — priorité absolue
|
||||
if (process.env.JWT_SECRET && process.env.JWT_SECRET !== 'fankarr-secret-change-me') {
|
||||
return process.env.JWT_SECRET
|
||||
}
|
||||
|
||||
// 2. Fichier existant
|
||||
if (fs.existsSync(SECRET_PATH)) {
|
||||
const secret = fs.readFileSync(SECRET_PATH, 'utf-8').trim()
|
||||
if (secret.length > 0) return secret
|
||||
}
|
||||
|
||||
// 3. Génération + sauvegarde
|
||||
const secret = generateSecret()
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(SECRET_PATH), { recursive: true })
|
||||
fs.writeFileSync(SECRET_PATH, secret, { encoding: 'utf-8', mode: 0o600 })
|
||||
console.log('[auth] JWT secret généré et sauvegardé dans data/secret.key')
|
||||
} catch (err) {
|
||||
console.error('[auth] Impossible de sauvegarder le secret:', err)
|
||||
}
|
||||
|
||||
return secret
|
||||
}
|
||||
|
||||
export const JWT_SECRET: string = loadOrCreateSecret()
|
||||
@@ -273,7 +273,7 @@ async function download(key: string, torrent_url: string | null, magnet: string
|
||||
downloading.value = key
|
||||
const result = await store.download(torrent_url, magnet)
|
||||
downloading.value = null
|
||||
if (result.success) showToast('success', 'Ajouté à qBittorrent !')
|
||||
if (result.success) showToast('success', 'Téléchargement lancé')
|
||||
else showToast('error', result.error ?? 'Erreur inconnue')
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user