### Manually Compile node-argon2 from Source Source: https://github.com/ranisalt/node-argon2/blob/master/README.md Install the argon2 library while ignoring its install script, then manually rebuild using node-gyp. This method provides granular control over the compilation process. ```bash $ npm install argon2 --ignore-scripts $ npx node-gyp rebuild -C ./node_modules/argon2 ``` -------------------------------- ### Install node-argon2 with Specific GCC Version Source: https://github.com/ranisalt/node-argon2/blob/master/README.md Install the argon2 library, specifying the GCC binary to use during the build process. This is useful if you have multiple GCC versions installed or need to ensure a specific compiler is used. ```bash $ CXX=g++-12 npm install argon2 ``` -------------------------------- ### Basic Hashing and Verification Example Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/index.md Demonstrates the basic usage of hashing a password and then verifying it using the node-argon2 library. ```javascript const argon2 = require('argon2'); // Hash a password const password = 'user_password'; const hash = await argon2.hash(password); // Later: verify the password if (await argon2.verify(hash, password)) { console.log('Password matches'); } ``` -------------------------------- ### Install GCC on OSX using Homebrew Source: https://github.com/ranisalt/node-argon2/blob/master/README.md Use Homebrew to install GCC version 5 or higher on macOS. This is a prerequisite for compiling native Node.js modules. ```bash $ brew install gcc ``` -------------------------------- ### Custom Security Parameters Example Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/index.md Illustrates how to hash a password with custom, high-security parameters for time cost, memory cost, parallelism, and algorithm type. ```javascript const argon2 = require('argon2'); // High-security configuration const hash = await argon2.hash(password, { timeCost: 4, memoryCost: 1 << 17, // 128 MiB parallelism: 8, type: argon2.argon2i, }); ``` -------------------------------- ### Usage Example for Argon2 Version Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/types.md Shows how to specify the Argon2 version when hashing and how to check if a hash needs to be rehashed to a newer version. ```javascript const argon2 = require('argon2'); // Use current version (default) const hash1 = await argon2.hash('password'); // Uses v=19 // Create hash with older version for compatibility const hash2 = await argon2.hash('password', { version: 0x10 }); // Check if hash needs version upgrade if (argon2.needsRehash(hash2, { version: 0x13 })) { console.log('Should upgrade hash to newer version'); } ``` -------------------------------- ### Install node-gyp Globally Source: https://github.com/ranisalt/node-argon2/blob/master/README.md Install node-gyp globally to manage native Node.js module compilation. This tool is essential for building modules that require native code. ```bash $ npm install -g node-gyp ``` -------------------------------- ### Force Build node-argon2 from Source Source: https://github.com/ranisalt/node-argon2/blob/master/README.md Install the argon2 library by forcing it to compile from source, bypassing any prebuilt binaries. Use this option if prebuilt binaries cause issues or are not available for your platform. ```bash $ npm install argon2 --build-from-source ``` -------------------------------- ### Verify Password (CommonJS) Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/module-exports.md Example of verifying a password against a generated hash. ```javascript const valid = await argon2.verify(hash, 'password'); ``` -------------------------------- ### Configure Argon2 hash options Source: https://github.com/ranisalt/node-argon2/wiki/Options Example of using the Argon2d variant with custom memory and length settings. ```javascript const hash = await argon2.hash(password, { type: argon2.argon2d, memoryCost: 2 ** 16, hashLength: 50, }); ``` -------------------------------- ### Version-specific checking with extracted parameters Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/needsRehash.md This example demonstrates how to perform a version-specific check by first extracting current hash parameters and then creating updated parameters that only modify the version. This allows for targeted upgrades, such as moving to a newer Argon2 version while keeping other parameters the same. ```javascript // Check only version, ignoring other parameters const hash = '$argon2id$v=16$m=65536,p=4,t=3$...'; // Extract the existing parameters from the hash const currentParams = extractParamsFromHash(hash); // helper function // Only update version const updatedParams = { ...currentParams, version: 0x13 }; if (argon2.needsRehash(hash, updatedParams)) { console.log('Need to rehash for version upgrade'); } ``` -------------------------------- ### Argon2 Hash String Example Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/types.md This is an example of a fully formed Argon2 hash string in the PHC format. It includes the algorithm type, version, memory cost, parallelism, time cost, salt, and the resulting hash. ```plaintext $argon2id$v=19$m=65536,p=4,t=3$c2FsdHNhbHRzYWx0c2FsdA$rBWULD5jOGpQy32rLvGcmvQMVqIVNAmrCtekWvUA8bw ``` -------------------------------- ### Hash Password (CommonJS) Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/module-exports.md Example of hashing a password using the imported argon2 module. ```javascript const hash = await argon2.hash('password'); ``` -------------------------------- ### Usage Example for Argon2Type Constants Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/types.md Demonstrates how to use the Argon2Type constants (argon2i, argon2d, argon2id) or their numeric values directly when hashing. ```javascript const argon2 = require('argon2'); // Using type constants directly const hash1 = await argon2.hash('password', { type: argon2.argon2i }); const hash2 = await argon2.hash('password', { type: argon2.argon2d }); const hash3 = await argon2.hash('password', { type: argon2.argon2id }); // Or using numeric values directly const hash4 = await argon2.hash('password', { type: 2 }); // Same as argon2id ``` -------------------------------- ### TypeScript usage for Argon2 hashing Source: https://github.com/ranisalt/node-argon2/blob/master/README.md Example demonstrating how to import and use the argon2 library within a TypeScript project. Type declarations are included with the module. ```typescript import * as argon2 from "argon2"; const hash = await argon2.hash(..); ``` -------------------------------- ### Include associated data Source: https://github.com/ranisalt/node-argon2/wiki/Options Example of adding non-secret binary data to the hash parameters. ```javascript > await argon2.hash('password', {associatedData: Buffer.from('associated data')}) '$argon2i$v=19$m=4096,t=3,p=1,data=YXNzb2NpYXRlZCBkYXRh$3bxuEoDAcj2SCRtaYbfRlg$lXodFk65Jmld5unb/2SPlNXSA4Xo4YqrWRJWJIgtVX4' ``` -------------------------------- ### Progressive password update on login Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/needsRehash.md This example demonstrates how to integrate `needsRehash` into a login flow. If a password matches and its hash is outdated, the hash is recomputed with stronger parameters and updated in the database, enhancing security without immediate user intervention. ```javascript const user = await database.getUserByEmail(email); const passwordMatches = await argon2.verify(user.passwordHash, password); if (passwordMatches) { // Check if we should upgrade the hash if (argon2.needsRehash(user.passwordHash)) { // Rehash with new parameters const newHash = await argon2.hash(password); await database.updatePasswordHash(user.id, newHash); console.log('Password hash upgraded'); } // Grant access return { success: true }; } ``` -------------------------------- ### Generate raw hash output Source: https://github.com/ranisalt/node-argon2/wiki/Options Example of returning the raw hash buffer instead of the encoded string. Note that this prevents the use of the verify function. ```javascript > await argon2.hash('password', {raw: true}) ``` -------------------------------- ### Check hash against a specific Argon2 version Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/needsRehash.md This example shows how to check if a hash uses an older Argon2 version than the specified one. It's useful for ensuring compatibility and security by migrating to newer versions. ```javascript // A hash created with an older Argon2 version const legacyHash = '$argon2id$v=16$m=65536,p=4,t=3$...'; // Check if version should be updated if (argon2.needsRehash(legacyHash, { version: 0x13 })) { console.log('Hash uses old Argon2 version 16, should upgrade to version 19'); } ``` -------------------------------- ### Password Update Detection Example Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/index.md Shows how to use the needsRehash function to detect if a stored password hash needs to be updated to current security standards. ```javascript const argon2 = require('argon2'); const storedHash = await database.getPasswordHash(userId); // Check if hash needs updating to current standards if (argon2.needsRehash(storedHash)) { // User needs to re-authenticate and re-hash their password console.log('Hash is outdated, request password reset'); } ``` -------------------------------- ### Consistent Hashing with Fixed Salt Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/configuration.md Demonstrates how to generate identical hashes for the same password and salt by pre-generating and providing a salt. Verifies that two hashes with the same salt and password are equal. ```javascript const crypto = require('node:crypto'); const argon2 = require('argon2'); const salt = crypto.randomBytes(16); // All hashes with this salt and password are identical const hash1 = await argon2.hash('password', { salt }); const hash2 = await argon2.hash('password', { salt }); console.log(hash1 === hash2); // true (same salt + password = same hash) ``` -------------------------------- ### Use a secret pepper Source: https://github.com/ranisalt/node-argon2/wiki/Options Example of providing a Buffer as a secret to the hashing process. ```javascript > await argon2.hash('password', {secret: Buffer.from('mysecret')}) '$argon2id$v=19$m=65536,t=3,p=4$HqToZhtkAjxwqpiY4H1eDQ$GhyTvrSAB2bxQ8RkD8w9DLrHzyEa3pf0bu+Hm158fhY' ``` -------------------------------- ### Hashing with a Specific Salt Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/hash.md Demonstrates how to provide a custom salt for the hashing process. Using a specific salt ensures that the same password will always produce the same hash. ```javascript const salt = Buffer.from('0123456789abcdef'); const hash = await argon2.hash('password', { salt }); // Returns same hash every time for this password and salt ``` -------------------------------- ### Specify hash variant Source: https://github.com/ranisalt/node-argon2/wiki/Options Example of explicitly setting the hash type to argon2id. ```javascript > await argon2.hash('password', {type: argon2.argon2id}) '$argon2id$v=19$m=4096,t=3,p=1$dSzY4kzLGcr/+/I0YvF5mQ$jzKftP3Px0+4X1oYvSLQv8OKkM728OZjPNdgRbIUr2s' ``` -------------------------------- ### Login Flow with Verification Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/verify.md Illustrates a typical login scenario where user credentials are verified against a stored hash. Handles potential verification errors and provides appropriate responses. ```javascript const user = await database.getUserByEmail('user@example.com'); try { const isValid = await argon2.verify(user.passwordHash, submittedPassword); if (isValid) { // Grant access res.json({ success: true, token: generateToken(user) }); } else { // Reject login res.status(401).json({ error: 'Invalid password' }); } } catch (error) { console.error('Verification failed:', error); res.status(500).json({ error: 'Internal error' }); } ``` -------------------------------- ### TypeScript Type Definitions Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/module-exports.md Example of TypeScript type definitions for the core Argon2 functions and constants. ```typescript export function hash(password: Buffer | string, options?: HashOptions & { raw?: boolean }): Promise; export function verify(digest: string, password: Buffer | string, options?: VerifyOptions): Promise; export function needsRehash(digest: string, options?: object): boolean; export const argon2d: 0; export const argon2i: 1; export const argon2id: 2; export interface HashOptions { /* ... */ } export interface VerifyOptions { /* ... */ } ``` -------------------------------- ### Testing Pattern: Integration Testing with Database Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Illustrates how to perform integration tests that involve hashing/verifying passwords and interacting with a mock or test database. This ensures the authentication flow works correctly within the application's persistence layer. ```javascript const argon2 = require('argon2'); // Mock database module for testing const mockDatabase = { users: {}, async addUser(username, passwordHash) { this.users[username] = { username, password_hash: passwordHash }; }, async getUserHash(username) { const user = this.users[username]; return user ? user.password_hash : null; } }; // Mock authentication service using the mock database const authService = { async register(username, password) { const hash = await argon2.hash(password); await mockDatabase.addUser(username, hash); return true; }, async login(username, password) { const storedHash = await mockDatabase.getUserHash(username); if (!storedHash) return false; try { return await argon2.verify(storedHash, password); } catch (err) { return false; } } }; // --- Integration Test Suite (Conceptual) --- // describe('Authentication Service Integration Tests', () => { // beforeEach(async () => { // // Reset mock database before each test // mockDatabase.users = {}; // }); // test('should register a user and allow login', async () => { // const username = 'integration_user'; // const password = 'secure_password'; // // Register the user // const registered = await authService.register(username, password); // expect(registered).toBe(true); // // Verify user was added to DB (optional check) // const hashInDb = await mockDatabase.getUserHash(username); // expect(hashInDb).toBeDefined(); // expect(hashInDb).toMatch(/^\$argon2[idb]/); // // Log in the user // const loggedIn = await authService.login(username, password); // expect(loggedIn).toBe(true); // }); // test('should fail login with incorrect password', async () => { // const username = 'integration_user'; // const password = 'secure_password'; // await authService.register(username, password); // const loggedIn = await authService.login(username, 'wrong_password'); // expect(loggedIn).toBe(false); // }); // test('should fail login for non-existent user', async () => { // const loggedIn = await authService.login('non_existent_user', 'any_password'); // expect(loggedIn).toBe(false); // }); // }); // Example Usage (simulating test execution): (async () => { console.log('Running simulated integration tests...'); await authService.register('testuser', 'testpass'); let loginSuccess = await authService.login('testuser', 'testpass'); console.log(`Login success (correct pass): ${loginSuccess}`); loginSuccess = await authService.login('testuser', 'wrongpass'); console.log(`Login success (wrong pass): ${loginSuccess}`); })(); ``` -------------------------------- ### Check if Rehash Needed (CommonJS) Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/module-exports.md Example of checking if a hash needs to be rehashed with updated parameters. ```javascript const outdated = argon2.needsRehash(hash); ``` -------------------------------- ### Hash with Specific Type (CommonJS) Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/module-exports.md Example of hashing a password and specifying the Argon2 type using a constant. ```javascript const h = await argon2.hash('password', { type: argon2.argon2i }); ``` -------------------------------- ### Hashing with a Secret Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/hash.md Illustrates how to compute a hash using both a password and a secret key. The secret is combined with the password during the hashing process. ```javascript const secret = Buffer.from('my_secret_key'); const hash = await argon2.hash('password', { secret }); // Hash is computed with both password and secret ``` -------------------------------- ### Verification with Secret Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/verify.md Shows how to verify a password when a secret was used during the hashing process. The same secret must be provided to both `hash` and `verify` functions. ```javascript const secret = Buffer.from('my_secret_key'); const hash = await argon2.hash('password', { secret }); // Must provide the same secret to verify const isMatch = await argon2.verify(hash, 'password', { secret }); console.log(isMatch); // true ``` -------------------------------- ### Basic Verification without Secret Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/configuration.md Demonstrates the fundamental process of verifying a password against a previously generated hash using the `verify` function. Assumes no secret key was used during hashing. ```javascript const argon2 = require('argon2'); const hash = await argon2.hash('password'); const matches = await argon2.verify(hash, 'password'); ``` -------------------------------- ### Basic Hashing with Defaults Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/hash.md Demonstrates the simplest way to hash a password using Argon2 with its default settings. The output is a PHC-encoded string. ```javascript const argon2 = require('argon2'); const password = 'my_password'; const hash = await argon2.hash(password); // Returns PHC-encoded string: // $argon2id$v=19$m=65536,p=4,t=3$$ ``` -------------------------------- ### Hash with argon2id using Direct Import Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/constants.md Demonstrates how to hash a password using the argon2id type by directly importing the argon2 library. ```javascript const argon2 = require('argon2'); const hash = await argon2.hash('password', { type: argon2.argon2id, }); ``` -------------------------------- ### Database Integration: Verifying Passwords Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Illustrates the process of retrieving a user's password hash from a database and verifying it against the provided plain text password during login. This is a standard authentication flow. ```javascript const argon2 = require('argon2'); // Assume 'db' is your database connection object // const db = require('./database'); // Mock database function to retrieve user hash async function getUserHashFromDB(username) { // Replace with actual database query const mockUsers = { 'bob': '$argon2id$v=19$m=65536,t=3,p=4$SGVsbG9Xb3JsZA$V2VyeVNlY3VyZQ==', 'charlie': '$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQxMjM0NQ$QW5vdGhlclNlY3VyZUhhc2g=' }; return mockUsers[username] || null; } async function loginUser(username, password) { const storedHash = await getUserHashFromDB(username); if (!storedHash) { console.log('Login failed: User not found.'); return false; } try { const match = await argon2.verify(storedHash, password); if (match) { console.log(`Login successful for ${username}!`); return true; } else { console.log('Login failed: Incorrect password.'); return false; } } catch (err) { console.error('Login verification error:', err); return false; } } // Example Usage: (async () => { await loginUser('bob', 'password'); // Should succeed if mock hash matches await loginUser('bob', 'wrongpassword'); // Should fail await loginUser('dave', 'anypassword'); // Should fail (user not found) })(); ``` -------------------------------- ### Hash with Secret Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/configuration.md Securely hash a password using a secret key for added protection. Ensure the secret is managed securely, for example, via environment variables. ```javascript const secret = process.env.ARGON2_SECRET || 'fallback_secret'; await argon2.hash(password, { secret: Buffer.from(secret) }); ``` -------------------------------- ### Manually Rebuild node-argon2 Binaries Source: https://github.com/ranisalt/node-argon2/blob/master/README.md Manually rebuild the binaries for the argon2 module using @mapbox/node-pre-gyp. This command is useful for troubleshooting or when prebuilt binaries are not available or compatible. ```bash $ npx @mapbox/node-pre-gyp rebuild -C ./node_modules/argon2 ``` -------------------------------- ### Async Batch Password Rehashing with Argon2 Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/usage-patterns.md Perform batch operations to rehash all user passwords in the database as a background job. This example logs users that require manual review as their plain password is not available. ```javascript const argon2 = require('argon2'); const db = require('./database'); async function upgradeAllPasswordsInBatch(batchSize = 100) { const total = await db.users.count(); let processed = 0; for (let offset = 0; offset < total; offset += batchSize) { const users = await db.users.findMany(batchSize, offset); // Process in parallel (up to 4 hashes at a time) const promises = users.map(async (user) => { if (!argon2.needsRehash(user.passwordHash)) { return; // Already up to date } // This is a database-only operation, no plain password available // Log for manual review or skip console.log(`User ${user.id} needs upgrade (manual review required)`); }); await Promise.all(promises); processed += users.length; console.log(`Progress: ${processed}/${total}`); } } ``` -------------------------------- ### Custom Hashing Parameters Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Illustrates how to specify custom parameters (memory, iterations, parallelism) when hashing. Adjust these based on your server's capabilities and security requirements. ```javascript const argon2 = require('argon2'); async function hashWithCustomParams(plainPassword) { const options = { type: argon2.argon2id, // Use Argon2id variant memoryCost: 65536, // 64MB memory timeCost: 3, // 3 iterations parallelism: 4, // 4 threads salt: Buffer.from('random_salt_string') // Optional: provide your own salt }; try { const hash = await argon2.hash(plainPassword, options); return hash; } catch (err) { console.error(err); } } // Example Usage: (async () => { const password = 'anothersecret'; const customHashed = await hashWithCustomParams(password); console.log('Custom Hashed:', customHashed); })(); ``` -------------------------------- ### Fast Testing with Minimal Parameters Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/usage-patterns.md Employ minimal Argon2 parameters for unit tests to speed up execution. This snippet defines fast hashing options and demonstrates hashing and verifying a password using these test-specific settings. ```javascript const argon2 = require('argon2'); const TEST_HASH_OPTIONS = { timeCost: 1, memoryCost: 8, parallelism: 1, }; // Use in tests to speed up execution async function testHash(password) { return argon2.hash(password, TEST_HASH_OPTIONS); } describe('Auth', () => { it('hashes and verifies password', async () => { const hash = await testHash('password'); const valid = await argon2.verify(hash, 'password'); assert(valid === true); }); }); ``` -------------------------------- ### Retrieving Raw Hash Buffer Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/hash.md Illustrates how to obtain the raw hash bytes as a Buffer instead of a PHC-encoded string by setting the `raw` option to `true`. ```javascript const buffer = await argon2.hash('password', { raw: true }); // Returns Buffer with raw hash bytes, not encoded console.log(buffer); // ``` -------------------------------- ### Verification with Secret Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/configuration.md Illustrates password verification when a secret key was used during the hashing process. Shows successful verification with the correct secret and failure with an incorrect or missing secret. ```javascript const argon2 = require('argon2'); const secret = Buffer.from('my_app_secret'); // Hash with secret const hash = await argon2.hash('password', { secret }); // Verify with matching secret const matches = await argon2.verify(hash, 'password', { secret }); console.log(matches); // true // Verify with wrong/missing secret fails const wrongSecret = Buffer.from('wrong_secret'); const noMatch = await argon2.verify(hash, 'password', { secret: wrongSecret }); console.log(noMatch); // false ``` -------------------------------- ### Node-Argon2 Hash Function Flow Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/architecture.md Outlines the step-by-step process of the hash() function, from input to output. ```text hash(password, options) ↓ Merge with defaults ↓ Validate parameters (throw RangeError if invalid) ↓ Generate random salt (if not provided) ↓ Call bindingsHash (native C++ function) ↓ Receive raw hash buffer from native layer ↓ If raw: return Buffer Else: serialize with @phc/format → return string ``` -------------------------------- ### Migration Pattern: Phased Rollout Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Describes a phased approach to migrating users to Argon2, where both old and new hashing methods are supported during the transition. This allows users to log in with either hash type until all accounts are migrated. ```javascript const argon2 = require('argon2'); // Mock function to get user data, potentially with both old and new hash formats async function getUserForPhasedLogin(username) { // Replace with actual DB query const mockUsers = { 'user_old': { id: 10, username: 'user_old', password_hash: '$2b$10$...' }, // Example old bcrypt hash 'user_new': { id: 11, username: 'user_new', password_hash: await argon2.hash('argon2_pass') } // Argon2 hash }; return mockUsers[username] || null; } async function loginWithPhasedMigration(username, password) { const user = await getUserForPhasedLogin(username); if (!user) { console.log('Login failed: User not found.'); return false; } let match = false; try { // First, try verifying with Argon2 (the new standard) if (user.password_hash.startsWith('$argon2')) { match = await argon2.verify(user.password_hash, password); if (match) { console.log('Login successful (Argon2).'); // Optional: Re-hash with current defaults if needed if (argon2.needsRehash(user.password_hash)) { console.log('Re-hashing user password...'); // const newHash = await argon2.hash(password); // await updateUserHash(user.id, newHash); } return true; } } // If Argon2 verification failed or wasn't applicable, try the old method (e.g., bcrypt) // This part requires integrating the old hashing library (e.g., 'bcrypt') // const bcrypt = require('bcrypt'); // if (user.password_hash.startsWith('$2b$')) { // Check if it looks like bcrypt // match = await bcrypt.compare(password, user.password_hash); // if (match) { // console.log('Login successful (Old Hash). Initiating re-hash...'); // // Immediately re-hash with Argon2 and update the user record // const newHash = await argon2.hash(password); // await updateUserHash(user.id, newHash); // Assume updateUserHash exists // return true; // } // } // If neither matched console.log('Login failed: Incorrect password.'); return false; } catch (err) { console.error('Login verification error:', err); return false; } } // Example Usage: // Note: This example requires a bcrypt implementation to fully test the old hash path. // The Argon2 path can be tested directly. (async () => { // Assuming 'user_new' exists and 'argon2_pass' is the correct password await loginWithPhasedMigration('user_new', 'argon2_pass'); await loginWithPhasedMigration('user_new', 'wrong_pass'); })(); ``` -------------------------------- ### Migration Pattern: Initial Hash Storage Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Details the first step in migrating users to Argon2: hashing existing passwords (if stored in plain text or a weaker format) using Argon2 and storing the new hashes. This assumes you have a way to access old passwords. ```javascript const argon2 = require('argon2'); // Mock function to get users with plain text passwords from an old system async function getOldUsers() { // Replace with actual data retrieval logic return [ { id: 1, username: 'migrator1', old_password: 'password1' }, { id: 2, username: 'migrator2', old_password: 'password2' } ]; } // Mock function to update user's password hash in the new system async function updatePasswordHash(userId, newHash) { console.log(`Updating user ${userId} with hash: ${newHash}`); // Replace with actual database update logic } async function migratePasswordsToArgon2() { const users = await getOldUsers(); for (const user of users) { try { const argon2Hash = await argon2.hash(user.old_password); await updatePasswordHash(user.id, argon2Hash); console.log(`Successfully migrated password for user ${user.username}`); } catch (err) { console.error(`Failed to migrate password for user ${user.username}:`, err); } } } // Example Usage: (async () => { await migratePasswordsToArgon2(); })(); ``` -------------------------------- ### Register New User Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/index.md Hashes a user's password and creates a new user record. This is used when a new user signs up. ```javascript async function registerUser(email, password) { const hash = await argon2.hash(password); return await db.createUser({ email, passwordHash: hash }); } ``` -------------------------------- ### Hashing with Custom Parameters Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/hash.md Shows how to hash a password with specific Argon2 parameters, including time cost, memory cost, parallelism, and type. ```javascript const hash = await argon2.hash('password', { timeCost: 4, memoryCost: 1 << 17, // 131072 KiB parallelism: 8, type: argon2.argon2i, }); ``` -------------------------------- ### Error Handling for Invalid Parameters Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/hash.md Demonstrates how to catch `RangeError` exceptions that occur when configuration parameters exceed their allowed limits. ```javascript try { // This exceeds the maximum allowed value await argon2.hash('password', { timeCost: Number.MAX_SAFE_INTEGER }); } catch (error) { if (error instanceof RangeError) { console.error('Configuration error:', error.message); } } ``` -------------------------------- ### Context-Aware Hashing with Associated Data Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/configuration.md Illustrates context-aware hashing using the `associatedData` option. Different associated data (e.g., email context vs. app context) will produce different hashes even for the same password. ```javascript const argon2 = require('argon2'); // Different contexts produce different hashes for same password const emailContext = Buffer.from('user@example.com'); const appContext = Buffer.from('myapp-v1'); const emailHash = await argon2.hash('password', { associatedData: emailContext }); const appHash = await argon2.hash('password', { associatedData: appContext }); // emailHash !== appHash even with same password ``` -------------------------------- ### Node-Argon2 Layer Structure Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/architecture.md Illustrates the layered architecture of node-argon2, from the application layer down to the C/C++ core. ```text ┌─────────────────────────────────────────────────────────────┐ │ Application Layer (user code) │ │ import argon2 = require('argon2'); │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ JavaScript API Layer (argon2.cjs) │ │ • hash(password, options) │ │ • verify(digest, password, options) │ │ • needsRehash(digest, options) │ │ • Type constants (argon2d, argon2i, argon2id) │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ Encoding Layer (@phc/format) │ │ • serialize(hash, salt, params) → PHC string │ │ • deserialize(PHC string) → hash, salt, params │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ Native Binding Layer (argon2.cpp) │ │ • Native async worker for hash computation │ │ • Promise-based interface to C++ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ C/C++ Layer (argon2/src/) │ │ • argon2.c — Core Argon2 algorithm │ │ • core.c — Hash context and computation │ │ • encoding.c — PHC string encoding │ │ • blake2/ — BLAKE2 hash function │ │ • ref.c, opt.c — Reference and optimized implementations │ │ • thread.c — Multi-threading support │ └─────────────────────────────────────────────────────────────┘ ``` -------------------------------- ### Testing Pattern: Unit Testing Hash/Verify Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Provides a basic structure for unit testing the `hash` and `verify` functions using a testing framework like Jest. It covers testing with default parameters and verifying correct and incorrect password attempts. ```javascript const argon2 = require('argon2'); // Assume these functions are in a file named 'authUtils.js' // const { hashPassword, verifyPassword } = require('./authUtils'); // Mock functions for demonstration within this snippet async function hashPassword(plainPassword) { return await argon2.hash(plainPassword); } async function verifyPassword(hash, plainPassword) { try { return await argon2.verify(hash, plainPassword); } catch (err) { return false; // Treat verification errors as false } } // --- Jest Test Suite --- // describe('Password Hashing and Verification', () => { // test('should hash a password and verify it correctly', async () => { // const password = 'testpassword123'; // const hash = await hashPassword(password); // // Check if hash is generated and looks like an Argon2 hash // expect(typeof hash).toBe('string'); // expect(hash).toMatch(/^\$argon2[idb]/); // Starts with $argon2id, $argon2i, or $argon2d // // Verify the correct password // const isValid = await verifyPassword(hash, password); // expect(isValid).toBe(true); // }); // test('should return false for an incorrect password', async () => { // const password = 'testpassword123'; // const wrongPassword = 'wrongpassword'; // const hash = await hashPassword(password); // const isValid = await verifyPassword(hash, wrongPassword); // expect(isValid).toBe(false); // }); // test('should handle invalid hash during verification', async () => { // const invalidHash = 'not-a-real-hash'; // const password = 'anypassword'; // const isValid = await verifyPassword(invalidHash, password); // expect(isValid).toBe(false); // }); // test('should allow specifying custom hashing parameters', async () => { // const password = 'customparams'; // const options = { // memoryCost: 1024, // Lower cost for faster tests // timeCost: 1, // parallelism: 1 // }; // const hash = await argon2.hash(password, options); // const isValid = await argon2.verify(hash, password); // expect(isValid).toBe(true); // }); // }); // Example Usage (simulating test execution): (async () => { console.log('Running simulated tests...'); // Simulate the first test case const password = 'testpassword123'; const hash = await hashPassword(password); const isValid = await verifyPassword(hash, password); console.log(`Test 1: Correct password verification - ${isValid}`); // Simulate the second test case const isValidWrong = await verifyPassword(hash, 'wrongpassword'); console.log(`Test 2: Incorrect password verification - ${isValidWrong}`); // Simulate the third test case const isValidInvalid = await verifyPassword('not-a-real-hash', password); console.log(`Test 3: Invalid hash verification - ${isValidInvalid}`); })(); ``` -------------------------------- ### Constrained Environments Performance Tuning Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/configuration.md Configure Argon2 for constrained environments, providing minimum viable security. Expected duration is 20-40ms, but this is not recommended for production password hashing. ```javascript // Minimum viable security { timeCost: 2, memoryCost: 1 << 15, // 32 MiB parallelism: 1, } // Expected duration: 20-40ms // WARNING: Not recommended for production password hashing ``` -------------------------------- ### Basic Authentication with Argon2 Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/usage-patterns.md Hash a password during user registration and verify it during login. This pattern uses default Argon2 parameters. ```javascript const argon2 = require('argon2'); const db = require('./database'); // User registration async function register(email, plainPassword) { // Validate inputs if (!email || !plainPassword) throw new Error('Missing fields'); // Hash password with defaults const hash = await argon2.hash(plainPassword); // Store in database const user = await db.users.create({ email, passwordHash: hash, }); return user; } // User login async function login(email, plainPassword) { // Retrieve user from database const user = await db.users.findByEmail(email); if (!user) return false; // User not found // Verify password const isValid = await argon2.verify(user.passwordHash, plainPassword); return isValid; } ``` -------------------------------- ### Handling Passwords with Null Bytes Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/verify.md Illustrates the correct way to verify passwords that contain null bytes. The full password, including the null byte, must be passed to the `verify` function. ```javascript const hash = await argon2.hash('pass\0word'); // The full password including the null byte must be provided if (await argon2.verify(hash, 'pass\0word')) { console.log('Verified with null byte in password'); } ``` -------------------------------- ### File Structure of node-argon2 Documentation Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/INDEX.md This snippet shows the directory structure of the node-argon2 documentation, outlining the purpose of each file and subdirectory. It helps in understanding where to find specific types of information. ```text output/ ├── README.md (Overview & navigation) ├── INDEX.md (This file) ├── module-exports.md (What gets exported) ├── types.md (Type definitions) ├── configuration.md (All options) ├── errors.md (Error reference) ├── architecture.md (Internal design) ├── usage-patterns.md (Real examples) └── api-reference/ ├── index.md (API overview) ├── hash.md (hash function) ├── verify.md (verify function) ├── needsRehash.md (needsRehash function) └── constants.md (Type constants) ``` -------------------------------- ### Compare hash lengths Source: https://github.com/ranisalt/node-argon2/wiki/Options Demonstrates how the hashLength option affects the resulting digest length. ```javascript > await argon2.hash('password') '$argon2i$v=19$m=4096,t=3,p=1$SX5sc9gOkbvc4wum7EDYRg$3ZlnlCa8+Si4tqbHAnRqMFvWu3QfH4zysPGX7buE0mI' > await argon2.hash('password', {hashLength: 40}) '$argon2i$v=19$m=4096,t=3,p=1$71SW0DoQwt6oBJza41+J5A$7mf4z59jvSsL3vl31B/ejqHTTNh6Xg9dW7waXzwV/DV05e6JTiuTlg' ``` -------------------------------- ### Basic Password Verification Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/api-reference/verify.md Demonstrates how to verify a password against a stored Argon2 hash. Ensure the hash is in PHC string format. The function returns false for mismatches, not an error. ```javascript const argon2 = require('argon2'); const hash = '$argon2id$v=19$m=65536,p=4,t=3$c2FsdHNhbHRzYWx0c2FsdHNhbHQ$rBWULD5jOGpQy32rLvGcmvQMVqIVNAmrCtekWvUA8bw'; // Correct password if (await argon2.verify(hash, 'password')) { console.log('Password is correct'); } else { console.log('Password is incorrect'); } ``` -------------------------------- ### Advanced Pattern: Using Context for Verification Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Demonstrates how to pass additional context (like a user ID or session token) during the `verify` operation. While Argon2 itself doesn't use this context, it's useful for application logic to correlate verification attempts with specific user sessions or requests. ```javascript const argon2 = require('argon2'); async function verifyWithContext(hash, plainPassword, context) { try { // Argon2.verify does not directly use the 'context' parameter. // It's passed here to be potentially used by surrounding application logic // before or after the verification call. const match = await argon2.verify(hash, plainPassword); if (match) { console.log(`Verification successful for context: ${JSON.stringify(context)}`); // Perform actions based on context, e.g., log login event // logLoginEvent(context.userId, context.ipAddress); } else { console.log(`Verification failed for context: ${JSON.stringify(context)}`); } return match; } catch (err) { console.error('Verification error with context:', err); return false; } } // Example Usage: (async () => { const password = 'context_password'; const hash = await argon2.hash(password); const context1 = { userId: 123, ipAddress: '192.168.1.100' }; await verifyWithContext(hash, password, context1); const context2 = { userId: 456, ipAddress: '10.0.0.5' }; await verifyWithContext(hash, 'wrong_password', context2); })(); ``` -------------------------------- ### Test Fixtures for Argon2 Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/usage-patterns.md Use deterministic hashes with known salts for testing authentication logic. This allows for consistent verification of correct and incorrect passwords against pre-computed hashes. ```javascript const argon2 = require('argon2'); // Pre-computed test hashes with known salt const TEST_FIXTURES = { password: '$argon2id$v=19$m=65536,p=4,t=3$c2FsdHNhbHRzYWx0c2FsdA$rBWULD5jOGpQy32rLvGcmvQMVqIVNAmrCtekWvUA8bw', wrongPassword: '$argon2id$v=19$m=65536,p=4,t=3$c2FsdHNhbHRzYWx0c2FsdA$different_hash_value', }; describe('Authentication', () => { it('verifies correct password', async () => { const valid = await argon2.verify(TEST_FIXTURES.password, 'password'); assert(valid === true); }); it('rejects wrong password', async () => { const valid = await argon2.verify(TEST_FIXTURES.password, 'wrong'); assert(valid === false); }); }); ``` -------------------------------- ### Using Different Argon2 Variants Source: https://github.com/ranisalt/node-argon2/blob/master/_autodocs/MANIFEST.md Shows how to select specific Argon2 variants (Argon2d, Argon2i, Argon2id) during the hashing process. Argon2id is generally recommended for most applications due to its resistance to side-channel attacks. ```javascript const argon2 = require('argon2'); async function hashWithVariant(plainPassword, variant) { const options = { type: variant, // Other options like memoryCost, timeCost, parallelism can be set here }; try { const hash = await argon2.hash(plainPassword, options); return hash; } catch (err) { console.error(err); } } // Example Usage: (async () => { const password = 'password123'; const hashD = await hashWithVariant(password, argon2.argon2d); console.log('Argon2d Hash:', hashD); const hashI = await hashWithVariant(password, argon2.argon2i); console.log('Argon2i Hash:', hashI); const hashId = await hashWithVariant(password, argon2.argon2id); console.log('Argon2id Hash:', hashId); })(); ```