Implement auth procedures with code review fixes
Add complete auth backend (Workstream D): - Auth middleware for session/API key authentication - Signup with password or passkey (WebAuthn) - Login flow with device trust and email confirmation - Password reset and email verification - Session management and logout Utilities created: - cookies.ts: Cookie helpers and configuration - crypto.ts: Token generation and hashing - password.ts: zxcvbn validation, argon2id hashing - geo.ts: IP/location extraction from headers - email.ts: Stubbed email sending - session.ts: Session creation and device trust Code review improvements applied: - Use ORPCError instead of Error in procedures - Add ast-grep rule to enforce ORPCError usage - Remove error info leakage (generic messages) - Optimize N+1 query with JOIN in login-password - Extract signupWithPassword/signupWithPasskey for testability - Add 15-minute WebAuthn challenge expiry check - Strengthen CookieOptions type definitions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
67
apps/api-server/src/utils/password.ts
Normal file
67
apps/api-server/src/utils/password.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import zxcvbn from "zxcvbn";
|
||||
|
||||
export interface PasswordValidationResult {
|
||||
valid: boolean;
|
||||
feedback: string[];
|
||||
score: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate password strength using zxcvbn
|
||||
* @param password - The password to validate
|
||||
* @param userInputs - User-specific inputs to penalize (email, display name)
|
||||
* @returns Validation result with feedback if invalid
|
||||
*/
|
||||
export const validatePassword = (
|
||||
password: string,
|
||||
userInputs: string[] = [],
|
||||
): PasswordValidationResult => {
|
||||
const result = zxcvbn(password, userInputs);
|
||||
|
||||
if (result.score < 3) {
|
||||
const feedback =
|
||||
result.feedback.suggestions.length > 0
|
||||
? result.feedback.suggestions
|
||||
: [
|
||||
"Password is too weak. Try a longer phrase or add numbers and symbols.",
|
||||
];
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
feedback,
|
||||
score: result.score,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
feedback: [],
|
||||
score: result.score,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Hash a password using Bun's built-in argon2id
|
||||
* @param password - The plaintext password to hash
|
||||
* @returns The hashed password
|
||||
*/
|
||||
export const hashPassword = async (password: string): Promise<string> => {
|
||||
return Bun.password.hash(password, {
|
||||
algorithm: "argon2id",
|
||||
memoryCost: 65536, // 64 MiB
|
||||
timeCost: 3,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify a password against a stored hash
|
||||
* @param password - The plaintext password to verify
|
||||
* @param hash - The stored password hash
|
||||
* @returns True if the password matches the hash
|
||||
*/
|
||||
export const verifyPassword = async (
|
||||
password: string,
|
||||
hash: string,
|
||||
): Promise<boolean> => {
|
||||
return Bun.password.verify(password, hash);
|
||||
};
|
||||
Reference in New Issue
Block a user