Use implement(contract).$context<APIContext>() for proper type safety in all procedure handlers. Create authMiddleware and loginRequestMiddleware using os.middleware() and apply with .use() on routes requiring auth. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
59 lines
1.7 KiB
TypeScript
59 lines
1.7 KiB
TypeScript
/**
|
|
* Forgot password handler
|
|
* Public procedure (no authentication required)
|
|
*
|
|
* Anti-enumeration: Always returns success even if user doesn't exist
|
|
* This prevents attackers from determining which emails are registered
|
|
*/
|
|
|
|
import { TOKEN_DURATIONS } from "../../utils/cookies.js";
|
|
import { generateExpiry, generateSecureToken } from "../../utils/crypto.js";
|
|
import { sendPasswordResetEmail } from "../../utils/email.js";
|
|
import { os } from "../base.js";
|
|
|
|
export const forgotPassword = os.auth.forgotPassword.handler(
|
|
async ({ input, context }) => {
|
|
const { email } = input;
|
|
|
|
// Normalize email to lowercase
|
|
const normalizedEmail = email.toLowerCase();
|
|
|
|
// Look up user by email
|
|
const user = await context.db
|
|
.selectFrom("users")
|
|
.select(["id", "email"])
|
|
.where("email", "=", normalizedEmail)
|
|
.executeTakeFirst();
|
|
|
|
// If user exists, create password reset token and send email
|
|
if (user) {
|
|
// Delete any existing password reset tokens for this user (security measure)
|
|
await context.db
|
|
.deleteFrom("password_resets")
|
|
.where("user_id", "=", user.id)
|
|
.execute();
|
|
|
|
// Generate secure token (64 hex chars)
|
|
const token = generateSecureToken();
|
|
|
|
// Create password reset record with 1 hour expiry
|
|
const expiresAt = generateExpiry(TOKEN_DURATIONS.PASSWORD_RESET);
|
|
|
|
await context.db
|
|
.insertInto("password_resets")
|
|
.values({
|
|
user_id: user.id,
|
|
token,
|
|
expires_at: expiresAt,
|
|
})
|
|
.execute();
|
|
|
|
// Send password reset email (stubbed)
|
|
await sendPasswordResetEmail(user.email, token);
|
|
}
|
|
|
|
// Always return success (anti-enumeration)
|
|
// Don't reveal whether the email exists or not
|
|
},
|
|
);
|