Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 8x 8x 8x 8x 8x 1011x 1008x 1008x 1004x 1004x 1004x 1004x 16064x 1004x | const ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
// 8-char suffix from a 36-char alphabet was ~41 bits, weak for a permanent
// bearer token that grants board read-access AND auto-enrolls the caller as
// a co-editor. 16 chars gives ~82 bits, well above the 80-bit cryptographic
// minimum for a bearer secret.
const RANDOM_LEN = 16;
const MAX_PREFIX_LEN = 20;
const FALLBACK_PREFIX = "user";
// Accept both legacy 8-char suffix and new 16-char suffix so existing share
// URLs in the wild keep resolving. New generateShareToken() always emits
// 16 chars, so over time the legacy surface shrinks naturally as owners
// revoke and re-share. A future PR can tighten to {16} only.
export const SHARE_TOKEN_REGEX = /^[a-z0-9]{1,20}-[a-z0-9]{8}(?:[a-z0-9]{8})?$/;
export function sanitizeUserPrefix(raw: string | null | undefined): string {
if (!raw || typeof raw !== "string") return FALLBACK_PREFIX;
const cleaned = raw
.toLowerCase()
.replace(/[^a-z0-9]/g, "")
.slice(0, MAX_PREFIX_LEN);
return cleaned.length > 0 ? cleaned : FALLBACK_PREFIX;
}
export function generateShareToken(userName: string | null | undefined): string {
const prefix = sanitizeUserPrefix(userName);
const bytes = new Uint8Array(RANDOM_LEN);
crypto.getRandomValues(bytes);
let suffix = "";
for (const b of bytes) suffix += ALPHABET[b % ALPHABET.length];
return `${prefix}-${suffix}`;
}
|