Files
modstagram-back/utils/secretCipher.js
payacom 3f416a7a60 full
2026-08-31 20:45:41 +03:30

80 lines
2.1 KiB
JavaScript

const crypto = require('crypto')
const ALGORITHM = 'aes-256-gcm'
const IV_LENGTH = 12
const SALT = 'modstagram-instagram-salt'
let cachedKey = null
function getKey () {
if (cachedKey) return cachedKey
const secret = process.env.INSTAGRAM_TOKEN_ENC_KEY || process.env.APP_SECRET
if (!secret) {
throw new Error(
'[secretCipher] Neither INSTAGRAM_TOKEN_ENC_KEY nor APP_SECRET is configured'
)
}
if (!process.env.INSTAGRAM_TOKEN_ENC_KEY) {
console.warn(
'[secretCipher] INSTAGRAM_TOKEN_ENC_KEY is not set; falling back to APP_SECRET. Set a dedicated key in production.'
)
}
cachedKey = crypto.scryptSync(secret, SALT, 32)
return cachedKey
}
/**
* Encrypt a plaintext secret (e.g. Instagram access token) for at-rest storage.
* Returns `${iv}:${authTag}:${ciphertext}` (all hex), or null for empty input.
*/
function encryptSecret (plaintext) {
if (!plaintext) return null
const key = getKey()
const iv = crypto.randomBytes(IV_LENGTH)
const cipher = crypto.createCipheriv(ALGORITHM, key, iv)
const encrypted = Buffer.concat([
cipher.update(String(plaintext), 'utf8'),
cipher.final()
])
const authTag = cipher.getAuthTag()
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted.toString('hex')}`
}
/**
* Decrypt a value produced by encryptSecret. Returns null on missing/invalid input
* instead of throwing, so a corrupt/legacy value never crashes a caller.
*/
function decryptSecret (encoded) {
if (!encoded || typeof encoded !== 'string') return null
const parts = encoded.split(':')
if (parts.length !== 3) return null
try {
const [ivHex, authTagHex, dataHex] = parts
const key = getKey()
const decipher = crypto.createDecipheriv(ALGORITHM, key, Buffer.from(ivHex, 'hex'))
decipher.setAuthTag(Buffer.from(authTagHex, 'hex'))
const decrypted = Buffer.concat([
decipher.update(Buffer.from(dataHex, 'hex')),
decipher.final()
])
return decrypted.toString('utf8')
} catch (err) {
console.error('[secretCipher] decrypt failed:', err.message)
return null
}
}
module.exports = {
encryptSecret,
decryptSecret
}