Emily Carter
@emily_carter • 1 months ago
Make a coding agent explain the current code, its planned edits, risks and open questions, then stop for your go-ahead before touching a single file.
taskcode{{task}}{{code}}task: Make password reset tokens expire after 30 minutes instead of 24 hours, and make them single-use.
code:
```ts
// src/auth/reset.ts
import crypto from "node:crypto"
import { db } from "../db"
import { sendEmail } from "../email"
const TOKEN_TTL_MS = 24 * 60 * 60 * 1000
export async function requestReset(email: string) {
const user = await db.user.findUnique({ where: { email } })
if (!user) return
const token = crypto.randomBytes(32).toString("hex")
await db.passwordReset.create({
data: { userId: user.id, token, expiresAt: new Date(Date.now() + TOKEN_TTL_MS) },
})
await sendEmail(user.email, "reset", { link: `https://app.example.com/reset?token=${token}` })
}
export async function resetPassword(token: string, newPassword: string) {
const row = await db.passwordReset.findUnique({ where: { token } })
if (!row || row.expiresAt < new Date()) throw new Error("Invalid or expired token")
await db.user.update({
where: { id: row.userId },
data: { passwordHash: await hash(newPassword) },
})
}
```reset.ts:6: tokens live for 24 hours (TOKEN_TTL_MS).reset.ts:8–16: requestReset creates a 64-hex-character random token, stores it in plain text with an expiry, and emails a link. Unknown emails return silently, which is good: it doesn't reveal which accounts exist.reset.ts:18–25: resetPassword looks up the token, rejects it if missing or expired, and updates the password hash. The token row is never deleted or marked used, so a link can be replayed until it expires.reset.ts:6: TOKEN_TTL_MS = 30 60 1000.resetPassword: consume the token atomically inside a transaction. deleteMany({ where: { id: row.id } }) returns a count. If it's 0, another request already used the token, so throw the same "Invalid or expired token" error. Then update the password. This needs no schema change.requestReset apart from the TTL.reset.test.ts doesn't exist, I'll add tests for expiry at 29 and 31 minutes, a second use failing, and a concurrent double-submit.hash is called on line 23 but isn't imported in this file. Either it's a global or this excerpt is incomplete.