(text: string | null | undefined)
| 169 | * Encrypts OAuth token using AUTH_SECRET. Idempotent - returns token unchanged if already encrypted. |
| 170 | */ |
| 171 | export function encryptOAuthToken(text: string | null | undefined): string | undefined { |
| 172 | if (!text) { |
| 173 | return undefined; |
| 174 | } |
| 175 | |
| 176 | if (isOAuthTokenEncrypted(text)) { |
| 177 | return text; |
| 178 | } |
| 179 | |
| 180 | const iv = crypto.randomBytes(oauthIvLength); |
| 181 | const salt = crypto.randomBytes(oauthSaltLength); |
| 182 | const key = deriveOAuthKey(env.AUTH_SECRET, salt); |
| 183 | |
| 184 | const cipher = crypto.createCipheriv(oauthAlgorithm, key, iv); |
| 185 | const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]); |
| 186 | const tag = cipher.getAuthTag(); |
| 187 | |
| 188 | const tokenData: EncryptedOAuthToken = { |
| 189 | v: 1, |
| 190 | salt: salt.toString('hex'), |
| 191 | iv: iv.toString('hex'), |
| 192 | tag: tag.toString('hex'), |
| 193 | data: encrypted.toString('hex'), |
| 194 | }; |
| 195 | |
| 196 | return Buffer.from(JSON.stringify(tokenData)).toString('base64'); |
| 197 | } |
| 198 | |
| 199 | /** |
| 200 | * Decrypts OAuth token using AUTH_SECRET. Returns plaintext tokens unchanged during migration. |
no test coverage detected