- Add me.get procedure returning user profile with needsSetup flag - Add me.setupProfile procedure for initial profile setup after signup - Add nonEmptyString/optionalString schema helpers with tests - Use Web Crypto API (SubtleCrypto) for Cloudflare Workers compatibility - Use @formatjs/intl-durationformat for duration formatting - Remove node:crypto dependency from crypto utilities Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
49 lines
1.3 KiB
TypeScript
49 lines
1.3 KiB
TypeScript
/**
|
|
* Hash a token with SHA-256 for storage in database
|
|
* Never store raw tokens - always hash first
|
|
* Uses Web Crypto API for Cloudflare Workers compatibility
|
|
*/
|
|
export const hashToken = async (token: string): Promise<string> => {
|
|
const encoder = new TextEncoder();
|
|
const data = encoder.encode(token);
|
|
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
const hashArray = new Uint8Array(hashBuffer);
|
|
return Array.from(hashArray)
|
|
.map((b) => b.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
};
|
|
|
|
/**
|
|
* Generate a session token (UUID v4)
|
|
*/
|
|
export const generateSessionToken = (): string => {
|
|
return crypto.randomUUID();
|
|
};
|
|
|
|
/**
|
|
* Generate a device fingerprint (UUID v4)
|
|
*/
|
|
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("");
|
|
};
|
|
|
|
/**
|
|
* Generate expiration date
|
|
*/
|
|
export const generateExpiry = (seconds: number): Date => {
|
|
return new Date(Date.now() + seconds * 1000);
|
|
};
|