generated from azures04/Base-REST-API
Add auth, JWT tokens, and bootstrap startup
Introduce bootstrap entrypoint and prestartup to ping DB and generate/manage RSA keypair. Add security module for key IO. Implement JWT-based access and refresh tokens (tokensService) with refresh_tokens table and repo. Add auth, provider and user services, and refresh-token management. Update repositories (users, credentials, providers, servers) with SQL fixes and new helper methods. Move DDL execution to bootstrap (remove from server). Update package.json main and add dependencies (bcryptjs, jsonwebtoken). Update .env.example and .gitignore accordingly.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
const security = require("./security")
|
||||
const serversRepo = require("../repositories/serversRepo")
|
||||
const logger = require("./logger")
|
||||
const { pool } = require("./database")
|
||||
|
||||
const LOCAL_SERVER_ID = process.env.SERVER_UUID
|
||||
|
||||
async function setupKeys() {
|
||||
const existing = await serversRepo.findById(LOCAL_SERVER_ID)
|
||||
if (existing) {
|
||||
checkKeys(existing.publicKey)
|
||||
return existing
|
||||
}
|
||||
|
||||
const { privateKeyPem, publicKeyPem } = security.generateServerKeyPair()
|
||||
security.saveKeyPairToDisk({ privateKeyPem, publicKeyPem })
|
||||
|
||||
logger.log("Local server keypair generated and stored in data/secrets", ["KEYS", "yellow"])
|
||||
|
||||
return await serversRepo.createAndAssignIdManually(LOCAL_SERVER_ID, "internal", publicKeyPem)
|
||||
}
|
||||
|
||||
function checkKeys(publicKeyFromDb) {
|
||||
const { privateExists, publicExists } = security.keyFilesExist()
|
||||
|
||||
if (!privateExists) {
|
||||
throw new Error(
|
||||
"Private key file missing for local server, but server row exists in DB.",
|
||||
"Cannot regenerate without invalidating existing signatures/tokens."
|
||||
)
|
||||
}
|
||||
|
||||
if (!publicExists) {
|
||||
security.restorePublicKeyFile(publicKeyFromDb)
|
||||
logger.warn("Public key file was missing, restored from DB.", ["KEYS", "yellow"])
|
||||
}
|
||||
}
|
||||
|
||||
async function pingDb() {
|
||||
try {
|
||||
await pool.query("SELECT 1")
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
logger.error("Could'nt connect to db", ["PRESTARTUP", "cyan"])
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
pingDb,
|
||||
checkKeys,
|
||||
setupKeys,
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
const fs = require("node:fs")
|
||||
const path = require("node:path")
|
||||
const crypto = require("node:crypto")
|
||||
|
||||
const SECRETS_DIR = path.join(process.cwd(), "data", "secrets")
|
||||
const PRIVATE_KEY = path.join(SECRETS_DIR, "server.private.pem")
|
||||
const PUBLIC_KEY = path.join(SECRETS_DIR, "server.public.pem")
|
||||
|
||||
function generateServerKeyPair() {
|
||||
const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
|
||||
modulusLength: 2048,
|
||||
publicKeyEncoding: {
|
||||
type: "spki",
|
||||
format: "pem",
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: "pkcs8",
|
||||
format: "pem",
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
privateKeyPem: privateKey,
|
||||
publicKeyPem: publicKey,
|
||||
}
|
||||
}
|
||||
|
||||
function saveKeyPairToDisk({ publicKeyPem, privateKeyPem }) {
|
||||
fs.mkdirSync(SECRETS_DIR, { recursive: true })
|
||||
fs.writeFileSync(PRIVATE_KEY, privateKeyPem, { mode: 0o600 })
|
||||
fs.writeFileSync(PUBLIC_KEY, publicKeyPem, { mode: 0o644 })
|
||||
}
|
||||
|
||||
function keyFilesExist() {
|
||||
return {
|
||||
privateExists: fs.existsSync(PRIVATE_KEY),
|
||||
publicExists: fs.existsSync(PUBLIC_KEY),
|
||||
}
|
||||
}
|
||||
|
||||
function restorePublicKeyFile(publicKeyPem) {
|
||||
fs.mkdirSync(SECRETS_DIR, { recursive: true })
|
||||
fs.writeFileSync(PUBLIC_KEY, publicKeyPem, { mode: 0o644 })
|
||||
}
|
||||
|
||||
function readPrivateKey() {
|
||||
return fs.readFileSync(PRIVATE_KEY, "utf8")
|
||||
}
|
||||
|
||||
function readPublicKey() {
|
||||
return fs.readFileSync(PUBLIC_KEY, "utf8")
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateServerKeyPair,
|
||||
saveKeyPairToDisk,
|
||||
keyFilesExist,
|
||||
restorePublicKeyFile,
|
||||
readPrivateKey,
|
||||
readPublicKey
|
||||
}
|
||||
Reference in New Issue
Block a user