Signature
function createHash(config?: HashConfig): HashInstanceCreates a password hashing instance powered by bcrypt. Uses bcryptjs (pure JavaScript, edge runtime compatible) under the hood.
import { createHash } from 'ideal-auth';
const hash = createHash();HashConfig
interface HashConfig {
rounds?: number;
}| Field | Type | Required | Default |
|---|---|---|---|
rounds |
number |
No | 12 |
The rounds parameter controls the bcrypt cost factor. Higher values are more secure but slower. Each increment roughly doubles the computation time.
const hash = createHash({ rounds: 14 });HashInstance
make
make(password: string): Promise<string>Hashes a password using bcrypt and returns the hash string. Throws if the password is empty.
Each call produces a different hash because bcrypt generates a random salt every time.
const hash = createHash();
const hashed = await hash.make('my-secure-password');
// "$2a$12$..."
// Each call produces a different result
const hashed2 = await hash.make('my-secure-password');
// "$2a$12$..." (different from hashed)try {
await hash.make('');
} catch (error) {
// Error: password must not be empty
}verify
verify(password: string, hash: string): Promise<boolean>Compares a plain-text password against a bcrypt hash. Returns true if they match, false otherwise.
const hash = createHash();
const hashed = await hash.make('my-secure-password');
await hash.verify('my-secure-password', hashed); // true
await hash.verify('wrong-password', hashed); // falsePrehash behavior
Passwords longer than 72 UTF-8 bytes are automatically SHA-256 prehashed before being passed to bcrypt. This is because bcrypt truncates input at 72 bytes — without prehashing, two passwords that share the same first 72 bytes would produce identical hashes.
The prehash is transparent. You do not need to do anything special; make() and verify() handle it automatically.
const hash = createHash();
// This 100-character password exceeds 72 UTF-8 bytes.
// It is automatically SHA-256 prehashed before bcrypt processes it.
const longPassword = 'a'.repeat(100);
const hashed = await hash.make(longPassword);
// Verification works identically — prehash is applied automatically
await hash.verify(longPassword, hashed); // trueUsage with createAuth
Pass the HashInstance to createAuth to enable the Laravel-style attempt() flow:
import { createAuth, createHash } from 'ideal-auth';
const hash = createHash();
const auth = createAuth({
secret: process.env.AUTH_SECRET!,
cookie: cookieBridge,
resolveUser: async (id) => db.user.findUnique({ where: { id } }),
resolveUserByCredentials: async (creds) =>
db.user.findUnique({ where: { email: creds.email } }),
hash,
});Registration example
Use make() to hash passwords before storing them in your database:
import { createHash } from 'ideal-auth';
const hash = createHash();
async function registerUser(email: string, password: string) {
const hashedPassword = await hash.make(password);
const user = await db.user.create({
data: {
email,
password: hashedPassword,
},
});
return user;
}Custom HashInstance
You don’t have to use createHash(). Any object that implements HashInstance works with createAuth. This lets you use your runtime’s native hashing or a different algorithm without pulling in bcryptjs.
import { prehash } from 'ideal-auth';
import type { HashInstance } from 'ideal-auth';
const hash: HashInstance = {
make: (password) => Bun.password.hash(prehash(password), {
algorithm: 'bcrypt',
cost: 12,
}),
verify: (password, hash) => Bun.password.verify(prehash(password), hash),
};Bun’s native bcrypt is significantly faster than bcryptjs (native implementation vs. pure JavaScript). prehash applies SHA-256 for passwords exceeding bcrypt’s 72-byte input limit, preventing silent truncation — the same protection that createHash() applies automatically.
import type { HashInstance } from 'ideal-auth';
const hash: HashInstance = {
make: (password) => Bun.password.hash(password, {
algorithm: 'argon2id',
memoryCost: 65536, // 64 MB
timeCost: 2,
}),
verify: (password, hash) => Bun.password.verify(password, hash),
};Argon2id is the current OWASP recommendation for password hashing. It is resistant to both GPU and side-channel attacks.
import argon2 from 'argon2';
import type { HashInstance } from 'ideal-auth';
const hash: HashInstance = {
make: (password) => argon2.hash(password),
verify: (password, hash) => argon2.verify(hash, password),
};Requires bun add argon2 (native Node.js addon).
Pass your custom instance to createAuth the same way:
const auth = createAuth({
secret: process.env.AUTH_SECRET!,
cookie: cookieBridge,
resolveUser: async (id) => db.user.findUnique({ where: { id } }),
resolveUserByCredentials: async (creds) =>
db.user.findUnique({ where: { email: creds.email } }),
hash, // your custom HashInstance
});Types
import type { HashConfig, HashInstance } from 'ideal-auth';