{
  "slug": "password-hashing",
  "runtimes": {
    "node": {
      "frameworks": {
        "express": {
          "prompt": "Select password hashing strategy:",
          "variants": {
            "argon2": {
              "label": "Argon2 (recommended)",
              "dependencies": {
                "runtime": [
                  "argon2"
                ],
                "dev": []
              },
              "env": [],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helpers.ts",
                      "content": "import argon2 from \"argon2\";\n\nexport async function hashPassword(password: string): Promise<string> {\n  return argon2.hash(password);\n}\n\nexport async function verifyPassword(\n  password: string,\n  hash: string\n): Promise<boolean> {\n  return argon2.verify(hash, password);\n}\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/modules/auth/auth.helpers.ts",
                      "content": "import argon2 from \"argon2\";\n\nexport async function hashPassword(password: string): Promise<string> {\n  return argon2.hash(password);\n}\n\nexport async function verifyPassword(\n  password: string,\n  hash: string\n): Promise<boolean> {\n  return argon2.verify(hash, password);\n}\n"
                    }
                  ]
                }
              }
            },
            "scrypt": {
              "label": "Scrypt (Node.js crypto-based)",
              "dependencies": {
                "runtime": [],
                "dev": []
              },
              "env": [],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helpers.ts",
                      "content": "import crypto from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nconst scryptAsync = promisify(\n  crypto.scrypt as (\n    password: crypto.BinaryLike,\n    salt: crypto.BinaryLike,\n    keylen: number,\n    options: crypto.ScryptOptions,\n    callback: (err: Error | null, derivedKey: Buffer) => void\n  ) => void\n);\n\nconst DEFAULTS = {\n  keyLength: 64,\n  saltLength: 16,\n  N: 16384,\n  r: 8,\n  p: 1\n};\n\nexport async function hashPasswordScrypt(\n  password: string,\n  options = DEFAULTS\n): Promise<string> {\n  const salt = crypto.randomBytes(options.saltLength);\n\n  const derivedKey = await scryptAsync(password, salt, options.keyLength, {\n    N: options.N,\n    r: options.r,\n    p: options.p\n  });\n\n  return [\n    \"scrypt\",\n    options.N,\n    options.r,\n    options.p,\n    salt.toString(\"hex\"),\n    derivedKey.toString(\"hex\")\n  ].join(\"$\");\n}\n\nexport async function verifyPasswordScrypt(\n  password: string,\n  storedHash: string\n): Promise<boolean> {\n  const [algo, N, r, p, saltHex, hashHex] = storedHash.split(\"$\");\n\n  if (algo !== \"scrypt\") {\n    throw new Error(\"Invalid scrypt hash format\");\n  }\n\n  const salt = Buffer.from(saltHex, \"hex\");\n  const hash = Buffer.from(hashHex, \"hex\");\n\n  const derivedKey = await scryptAsync(password, salt, hash.length, {\n    N: Number(N),\n    r: Number(r),\n    p: Number(p)\n  });\n\n  return crypto.timingSafeEqual(hash, derivedKey);\n}\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/modules/auth/auth.helpers.ts",
                      "content": "import crypto from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nconst scryptAsync = promisify(\n  crypto.scrypt as (\n    password: crypto.BinaryLike,\n    salt: crypto.BinaryLike,\n    keylen: number,\n    options: crypto.ScryptOptions,\n    callback: (err: Error | null, derivedKey: Buffer) => void\n  ) => void\n);\n\nconst DEFAULTS = {\n  keyLength: 64,\n  saltLength: 16,\n  N: 16384,\n  r: 8,\n  p: 1\n};\n\nexport async function hashPasswordScrypt(\n  password: string,\n  options = DEFAULTS\n): Promise<string> {\n  const salt = crypto.randomBytes(options.saltLength);\n\n  const derivedKey = await scryptAsync(password, salt, options.keyLength, {\n    N: options.N,\n    r: options.r,\n    p: options.p\n  });\n\n  return [\n    \"scrypt\",\n    options.N,\n    options.r,\n    options.p,\n    salt.toString(\"hex\"),\n    derivedKey.toString(\"hex\")\n  ].join(\"$\");\n}\n\nexport async function verifyPasswordScrypt(\n  password: string,\n  storedHash: string\n): Promise<boolean> {\n  const [algo, N, r, p, saltHex, hashHex] = storedHash.split(\"$\");\n\n  if (algo !== \"scrypt\") {\n    throw new Error(\"Invalid scrypt hash format\");\n  }\n\n  const salt = Buffer.from(saltHex, \"hex\");\n  const hash = Buffer.from(hashHex, \"hex\");\n\n  const derivedKey = await scryptAsync(password, salt, hash.length, {\n    N: Number(N),\n    r: Number(r),\n    p: Number(p)\n  });\n\n  return crypto.timingSafeEqual(hash, derivedKey);\n}\n"
                    }
                  ]
                }
              }
            },
            "bcryptjs": {
              "label": "BcryptJS (JavaScript bcrypt implementation)",
              "dependencies": {
                "runtime": [
                  "bcryptjs"
                ],
                "dev": []
              },
              "env": [],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helpers.ts",
                      "content": "import bcrypt from \"bcryptjs\";\n\nexport async function hashPassword(\n  password: string,\n  rounds = 12\n): Promise<string> {\n  return bcrypt.hash(password, rounds);\n}\n\nexport async function verifyPassword(\n  password: string,\n  hash: string\n): Promise<boolean> {\n  return bcrypt.compare(password, hash);\n}\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/modules/auth/auth.helpers.ts",
                      "content": "import bcrypt from \"bcryptjs\";\n\nexport async function hashPassword(\n  password: string,\n  rounds = 12\n): Promise<string> {\n  return bcrypt.hash(password, rounds);\n}\n\nexport async function verifyPassword(\n  password: string,\n  hash: string\n): Promise<boolean> {\n  return bcrypt.compare(password, hash);\n}\n"
                    }
                  ]
                }
              }
            },
            "pbkdf2": {
              "label": "PBKDF2 (Node.js crypto-based)",
              "dependencies": {
                "runtime": [],
                "dev": []
              },
              "env": [],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helpers.ts",
                      "content": "import crypto from \"crypto\";\n\nconst DEFAULTS = {\n  iterations: 310000,\n  keyLength: 64,\n  saltLength: 16,\n  digest: \"sha512\"\n};\n\nexport function hashPasswordPbkdf2(\n  password: string,\n  options = DEFAULTS\n): string {\n  const salt = crypto.randomBytes(options.saltLength);\n\n  const derivedKey = crypto.pbkdf2Sync(\n    password,\n    salt,\n    options.iterations,\n    options.keyLength,\n    options.digest\n  );\n\n  return [\n    \"pbkdf2\",\n    options.iterations,\n    options.digest,\n    salt.toString(\"hex\"),\n    derivedKey.toString(\"hex\")\n  ].join(\"$\");\n}\n\nexport function verifyPasswordPbkdf2(\n  password: string,\n  storedHash: string\n): boolean {\n  const [algo, iterations, digest, saltHex, hashHex] = storedHash.split(\"$\");\n\n  if (algo !== \"pbkdf2\") {\n    throw new Error(\"Invalid PBKDF2 hash format\");\n  }\n\n  const salt = Buffer.from(saltHex, \"hex\");\n  const hash = Buffer.from(hashHex, \"hex\");\n\n  const derivedKey = crypto.pbkdf2Sync(\n    password,\n    salt,\n    Number(iterations),\n    hash.length,\n    digest\n  );\n\n  return crypto.timingSafeEqual(hash, derivedKey);\n}\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/modules/auth/auth.helpers.ts",
                      "content": "import crypto from \"crypto\";\n\nconst DEFAULTS = {\n  iterations: 310000,\n  keyLength: 64,\n  saltLength: 16,\n  digest: \"sha512\"\n};\n\nexport function hashPasswordPbkdf2(\n  password: string,\n  options = DEFAULTS\n): string {\n  const salt = crypto.randomBytes(options.saltLength);\n\n  const derivedKey = crypto.pbkdf2Sync(\n    password,\n    salt,\n    options.iterations,\n    options.keyLength,\n    options.digest\n  );\n\n  return [\n    \"pbkdf2\",\n    options.iterations,\n    options.digest,\n    salt.toString(\"hex\"),\n    derivedKey.toString(\"hex\")\n  ].join(\"$\");\n}\n\nexport function verifyPasswordPbkdf2(\n  password: string,\n  storedHash: string\n): boolean {\n  const [algo, iterations, digest, saltHex, hashHex] = storedHash.split(\"$\");\n\n  if (algo !== \"pbkdf2\") {\n    throw new Error(\"Invalid PBKDF2 hash format\");\n  }\n\n  const salt = Buffer.from(saltHex, \"hex\");\n  const hash = Buffer.from(hashHex, \"hex\");\n\n  const derivedKey = crypto.pbkdf2Sync(\n    password,\n    salt,\n    Number(iterations),\n    hash.length,\n    digest\n  );\n\n  return crypto.timingSafeEqual(hash, derivedKey);\n}\n"
                    }
                  ]
                }
              }
            }
          }
        },
        "nextjs": {
          "prompt": "Select password hashing strategy:",
          "variants": {
            "argon2": {
              "label": "Argon2 (recommended)",
              "dependencies": {
                "runtime": [
                  "argon2"
                ],
                "dev": []
              },
              "env": [],
              "architectures": {
                "file-api": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helper.ts",
                      "content": "import argon2 from \"argon2\";\n\nexport async function hashPassword(password: string): Promise<string> {\n  return argon2.hash(password);\n}\n\nexport async function verifyPassword(\n  password: string,\n  hash: string\n): Promise<boolean> {\n  return argon2.verify(hash, password);\n}\n"
                    }
                  ]
                }
              }
            },
            "scrypt": {
              "label": "Scrypt (Node.js crypto-based)",
              "dependencies": {
                "runtime": [],
                "dev": []
              },
              "env": [],
              "architectures": {
                "file-api": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helper.ts",
                      "content": "import crypto from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nconst scryptAsync = promisify(\n  crypto.scrypt as (\n    password: crypto.BinaryLike,\n    salt: crypto.BinaryLike,\n    keylen: number,\n    options: crypto.ScryptOptions,\n    callback: (err: Error | null, derivedKey: Buffer) => void\n  ) => void\n);\n\nconst DEFAULTS = {\n  keyLength: 64,\n  saltLength: 16,\n  N: 16384,\n  r: 8,\n  p: 1\n};\n\nexport async function hashPasswordScrypt(\n  password: string,\n  options = DEFAULTS\n): Promise<string> {\n  const salt = crypto.randomBytes(options.saltLength);\n\n  const derivedKey = await scryptAsync(password, salt, options.keyLength, {\n    N: options.N,\n    r: options.r,\n    p: options.p\n  });\n\n  return [\n    \"scrypt\",\n    options.N,\n    options.r,\n    options.p,\n    salt.toString(\"hex\"),\n    derivedKey.toString(\"hex\")\n  ].join(\"$\");\n}\n\nexport async function verifyPasswordScrypt(\n  password: string,\n  storedHash: string\n): Promise<boolean> {\n  const [algo, N, r, p, saltHex, hashHex] = storedHash.split(\"$\");\n\n  if (algo !== \"scrypt\") {\n    throw new Error(\"Invalid scrypt hash format\");\n  }\n\n  const salt = Buffer.from(saltHex, \"hex\");\n  const hash = Buffer.from(hashHex, \"hex\");\n\n  const derivedKey = await scryptAsync(password, salt, hash.length, {\n    N: Number(N),\n    r: Number(r),\n    p: Number(p)\n  });\n\n  return crypto.timingSafeEqual(hash, derivedKey);\n}\n"
                    }
                  ]
                }
              }
            },
            "bcryptjs": {
              "label": "BcryptJS (JavaScript bcrypt implementation)",
              "dependencies": {
                "runtime": [
                  "bcryptjs"
                ],
                "dev": []
              },
              "env": [],
              "architectures": {
                "file-api": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helper.ts",
                      "content": "import bcrypt from \"bcryptjs\";\n\nexport async function hashPassword(\n  password: string,\n  rounds = 12\n): Promise<string> {\n  return bcrypt.hash(password, rounds);\n}\n\nexport async function verifyPassword(\n  password: string,\n  hash: string\n): Promise<boolean> {\n  return bcrypt.compare(password, hash);\n}\n"
                    }
                  ]
                }
              }
            },
            "pbkdf2": {
              "label": "PBKDF2 (Node.js crypto-based)",
              "dependencies": {
                "runtime": [],
                "dev": []
              },
              "env": [],
              "architectures": {
                "file-api": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/helpers/auth.helper.ts",
                      "content": "import crypto from \"crypto\";\n\nconst DEFAULTS = {\n  iterations: 310000,\n  keyLength: 64,\n  saltLength: 16,\n  digest: \"sha512\"\n};\n\nexport function hashPasswordPbkdf2(\n  password: string,\n  options = DEFAULTS\n): string {\n  const salt = crypto.randomBytes(options.saltLength);\n\n  const derivedKey = crypto.pbkdf2Sync(\n    password,\n    salt,\n    options.iterations,\n    options.keyLength,\n    options.digest\n  );\n\n  return [\n    \"pbkdf2\",\n    options.iterations,\n    options.digest,\n    salt.toString(\"hex\"),\n    derivedKey.toString(\"hex\")\n  ].join(\"$\");\n}\n\nexport function verifyPasswordPbkdf2(\n  password: string,\n  storedHash: string\n): boolean {\n  const [algo, iterations, digest, saltHex, hashHex] = storedHash.split(\"$\");\n\n  if (algo !== \"pbkdf2\") {\n    throw new Error(\"Invalid PBKDF2 hash format\");\n  }\n\n  const salt = Buffer.from(saltHex, \"hex\");\n  const hash = Buffer.from(hashHex, \"hex\");\n\n  const derivedKey = crypto.pbkdf2Sync(\n    password,\n    salt,\n    Number(iterations),\n    hash.length,\n    digest\n  );\n\n  return crypto.timingSafeEqual(hash, derivedKey);\n}\n"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
