feat(auth): password hashing service
This commit is contained in:
18
package-lock.json
generated
18
package-lock.json
generated
@@ -2005,6 +2005,13 @@
|
||||
"@babel/types": "^7.28.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bcryptjs": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz",
|
||||
"integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/better-sqlite3": {
|
||||
"version": "7.6.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
|
||||
@@ -2559,6 +2566,15 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz",
|
||||
"integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"bin": {
|
||||
"bcrypt": "bin/bcrypt"
|
||||
}
|
||||
},
|
||||
"node_modules/better-sqlite3": {
|
||||
"version": "11.10.0",
|
||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
|
||||
@@ -7838,6 +7854,7 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@flashcard/shared": "*",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"better-sqlite3": "^11.0.0",
|
||||
"drizzle-orm": "^0.33.0",
|
||||
"express": "^4.19.0",
|
||||
@@ -7846,6 +7863,7 @@
|
||||
"zod": "^3.23.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/better-sqlite3": "^7.6.0",
|
||||
"@types/express": "^4.17.0",
|
||||
"@types/multer": "^1.4.0",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@flashcard/shared": "*",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"better-sqlite3": "^11.0.0",
|
||||
"drizzle-orm": "^0.33.0",
|
||||
"express": "^4.19.0",
|
||||
@@ -25,6 +26,7 @@
|
||||
"zod": "^3.23.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/better-sqlite3": "^7.6.0",
|
||||
"@types/express": "^4.17.0",
|
||||
"@types/multer": "^1.4.0",
|
||||
|
||||
19
packages/backend/src/services/auth/passwords.test.ts
Normal file
19
packages/backend/src/services/auth/passwords.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { hashPassword, verifyPassword } from './passwords.js';
|
||||
|
||||
describe('passwords', () => {
|
||||
it('hashes a password and verifies it', async () => {
|
||||
const hash = await hashPassword('correcthorse');
|
||||
expect(hash).toMatch(/^\$2[aby]\$/);
|
||||
expect(await verifyPassword('correcthorse', hash)).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects a wrong password', async () => {
|
||||
const hash = await hashPassword('correcthorse');
|
||||
expect(await verifyPassword('wrong', hash)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false on malformed hash', async () => {
|
||||
expect(await verifyPassword('x', 'not-a-bcrypt-hash')).toBe(false);
|
||||
});
|
||||
});
|
||||
15
packages/backend/src/services/auth/passwords.ts
Normal file
15
packages/backend/src/services/auth/passwords.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
const COST = 12;
|
||||
|
||||
export async function hashPassword(plain: string): Promise<string> {
|
||||
return bcrypt.hash(plain, COST);
|
||||
}
|
||||
|
||||
export async function verifyPassword(plain: string, hash: string): Promise<boolean> {
|
||||
try {
|
||||
return await bcrypt.compare(plain, hash);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user