Add generateSecureBase58Token to shared utils with login_ prefix

- Create packages/utils/src/generate-base58-token.ts with typed prefix support
- Function returns `${TPrefix}${string}` for type-safe prefixed tokens
- Add isBase58() validator and parseBase58Token() helper
- Add comprehensive tests (13 test cases)

- Update login request tokens to use "login_" prefix
- Fix login-password.ts to not replace token (cookie/DB mismatch bug)
- Migrate all token generation from generateSecureToken (hex) to
  generateSecureBase58Token (base58)
- Remove duplicate token generation from api-server/utils/crypto.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
RevIQ
2026-01-09 19:31:30 +08:00
parent 68fc67ba4a
commit ddd7c0c03b
10 changed files with 218 additions and 80 deletions

View File

@@ -5,7 +5,6 @@
import { ORPCError } from "@orpc/server";
import { COOKIE_NAMES, getCookie } from "../../utils/cookies.js";
import { generateSecureToken } from "../../utils/crypto.js";
import { sendLoginConfirmationEmail } from "../../utils/email.js";
import { verifyPassword } from "../../utils/password.js";
import { isDeviceTrusted } from "../../utils/session.js";
@@ -47,6 +46,7 @@ export const loginPassword = os.auth.loginPassword.handler(
"login_requests.id",
"login_requests.user_id",
"login_requests.email",
"login_requests.token",
"login_requests.device_fingerprint",
"login_requests.expires_at",
"login_requests.completed_at",
@@ -106,19 +106,9 @@ export const loginPassword = os.auth.loginPassword.handler(
.where("id", "=", result.id)
.execute();
} else {
// Device is untrusted - generate confirmation token and send email
const confirmationToken = generateSecureToken();
await context.db
.updateTable("login_requests")
.set({
token: confirmationToken,
})
.where("id", "=", result.id)
.execute();
// Send login confirmation email
await sendLoginConfirmationEmail(result.email, confirmationToken);
// Device is untrusted - send confirmation email with existing token
// The same base58 token is used for both cookie lookup and email confirmation
await sendLoginConfirmationEmail(result.email, result.token);
}
// Return void (success)