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:
@@ -1,5 +1,8 @@
|
||||
import { base58 } from "@scure/base";
|
||||
|
||||
// Re-export generateSecureBase58Token from shared utils
|
||||
export { generateSecureBase58Token } from "@reviq/utils";
|
||||
|
||||
/**
|
||||
* Token prefix for all RevIQ API tokens
|
||||
*/
|
||||
@@ -62,58 +65,6 @@ export const generateDeviceFingerprint = (): string => {
|
||||
return crypto.randomUUID();
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a secure random token for email verification, password reset, etc.
|
||||
* Uses 32 bytes (256 bits) of entropy
|
||||
* Uses Web Crypto API for Cloudflare Workers compatibility
|
||||
*/
|
||||
export const generateSecureToken = (): string => {
|
||||
const bytes = new Uint8Array(32);
|
||||
crypto.getRandomValues(bytes);
|
||||
return Array.from(bytes)
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Base58 alphabet (Bitcoin-style, no 0, O, I, l)
|
||||
*/
|
||||
const BASE58_ALPHABET =
|
||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
/**
|
||||
* Generate a cryptographically secure base58 token
|
||||
* Uses 24 bytes (192 bits) of entropy, producing ~33 character output
|
||||
*/
|
||||
export const generateBase58Token = (byteLength = 24): string => {
|
||||
const bytes = new Uint8Array(byteLength);
|
||||
crypto.getRandomValues(bytes);
|
||||
|
||||
// Convert bytes to base58
|
||||
let result = "";
|
||||
let num = BigInt(0);
|
||||
for (const byte of bytes) {
|
||||
num = num * 256n + BigInt(byte);
|
||||
}
|
||||
|
||||
while (num > 0n) {
|
||||
const remainder = Number(num % 58n);
|
||||
result = BASE58_ALPHABET.charAt(remainder) + result;
|
||||
num /= 58n;
|
||||
}
|
||||
|
||||
// Handle leading zeros
|
||||
for (const byte of bytes) {
|
||||
if (byte === 0) {
|
||||
result = BASE58_ALPHABET.charAt(0) + result;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate expiration date
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user