Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. // Auth utilities — password hashing (scrypt), tokens, and TOTP MFA.
  2. // Uses only Node's built-in crypto, no external auth deps.
  3. const crypto = require('crypto');
  4. // ---- Passwords (scrypt) ----
  5. function hashPassword(password, salt = crypto.randomBytes(16).toString('hex')) {
  6. const hash = crypto.scryptSync(password, salt, 64).toString('hex');
  7. return { hash, salt };
  8. }
  9. function verifyPassword(password, salt, expectedHash) {
  10. const hash = crypto.scryptSync(password, salt, 64).toString('hex');
  11. return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(expectedHash));
  12. }
  13. // ---- Random tokens ----
  14. const token = (bytes = 24) => crypto.randomBytes(bytes).toString('hex');
  15. const id = () => crypto.randomBytes(8).toString('hex');
  16. const numericCode = (digits = 6) =>
  17. String(crypto.randomInt(0, 10 ** digits)).padStart(digits, '0');
  18. // ---- TOTP (RFC 6238), SHA-1, 30s, 6 digits ----
  19. const B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
  20. function base32Encode(buf) {
  21. let bits = 0, value = 0, out = '';
  22. for (const byte of buf) {
  23. value = (value << 8) | byte; bits += 8;
  24. while (bits >= 5) { out += B32[(value >>> (bits - 5)) & 31]; bits -= 5; }
  25. }
  26. if (bits > 0) out += B32[(value << (5 - bits)) & 31];
  27. return out;
  28. }
  29. function base32Decode(str) {
  30. const clean = str.replace(/=+$/, '').toUpperCase();
  31. let bits = 0, value = 0; const out = [];
  32. for (const ch of clean) {
  33. const idx = B32.indexOf(ch);
  34. if (idx === -1) continue;
  35. value = (value << 5) | idx; bits += 5;
  36. if (bits >= 8) { out.push((value >>> (bits - 8)) & 0xff); bits -= 8; }
  37. }
  38. return Buffer.from(out);
  39. }
  40. function newMfaSecret() {
  41. return base32Encode(crypto.randomBytes(20));
  42. }
  43. function totp(secret, timeStep = Math.floor(Date.now() / 30000)) {
  44. const key = base32Decode(secret);
  45. const buf = Buffer.alloc(8);
  46. buf.writeBigUInt64BE(BigInt(timeStep));
  47. const hmac = crypto.createHmac('sha1', key).update(buf).digest();
  48. const offset = hmac[hmac.length - 1] & 0xf;
  49. const code =
  50. ((hmac[offset] & 0x7f) << 24) |
  51. ((hmac[offset + 1] & 0xff) << 16) |
  52. ((hmac[offset + 2] & 0xff) << 8) |
  53. (hmac[offset + 3] & 0xff);
  54. return String(code % 1_000_000).padStart(6, '0');
  55. }
  56. function verifyTotp(secret, code, window = 1) {
  57. if (!/^\d{6}$/.test(String(code || ''))) return false;
  58. const step = Math.floor(Date.now() / 30000);
  59. for (let i = -window; i <= window; i++) {
  60. if (totp(secret, step + i) === String(code)) return true;
  61. }
  62. return false;
  63. }
  64. function otpauthUrl(secret, email, issuer = 'RemoteAccess') {
  65. return `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(email)}` +
  66. `?secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=SHA1&digits=6&period=30`;
  67. }
  68. module.exports = {
  69. hashPassword, verifyPassword, token, id, numericCode,
  70. newMfaSecret, totp, verifyTotp, otpauthUrl,
  71. };