@@ -9,12 +9,46 @@ export function expiresAt(expiresIn: number) {
99 return timeNow + expiresIn
1010}
1111
12+ // Counter for SSR-safe UUID generation
13+ let ssrUuidCounter = 0
14+
15+ /**
16+ * Generates a UUID v4 string.
17+ *
18+ * This function is SSR-aware to handle Next.js 16 pre-rendering constraints:
19+ * - In browsers: Uses crypto.randomUUID() or crypto.getRandomValues() for cryptographic randomness
20+ * - During SSR: Uses a deterministic fallback (timestamp + counter)
21+ *
22+ * Note: The SSR fallback is safe because:
23+ * 1. UUIDs from this function are only used for internal subscription IDs, not security-critical operations
24+ * 2. During SSR/pre-rendering, auth callbacks don't actually fire
25+ * 3. Once in the browser, proper cryptographic APIs are always used
26+ */
1227export function uuid ( ) {
13- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' . replace ( / [ x y ] / g, function ( c ) {
14- const r = ( Math . random ( ) * 16 ) | 0 ,
15- v = c == 'x' ? r : ( r & 0x3 ) | 0x8
16- return v . toString ( 16 )
17- } )
28+ // Modern browsers and Node.js 19+ - use native crypto.randomUUID()
29+ if ( typeof crypto !== 'undefined' && typeof crypto . randomUUID === 'function' ) {
30+ return crypto . randomUUID ( )
31+ }
32+
33+ // Browsers with crypto.getRandomValues() support
34+ if ( typeof crypto !== 'undefined' && typeof crypto . getRandomValues === 'function' ) {
35+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' . replace ( / [ x y ] / g, function ( c ) {
36+ const array = new Uint8Array ( 1 )
37+ crypto . getRandomValues ( array )
38+ const r = array [ 0 ] % 16
39+ const v = c == 'x' ? r : ( r & 0x3 ) | 0x8
40+ return v . toString ( 16 )
41+ } )
42+ }
43+
44+ // SSR/pre-render fallback - deterministic but unique within session
45+ // This only generates subscription IDs during pre-render; real UUIDs use crypto in browser
46+ const timestamp = Date . now ( )
47+ const counter = ssrUuidCounter ++
48+ const random1 = Math . floor ( timestamp / 1000000 ) % 10000
49+ const random2 = ( timestamp % 1000000 ) % 10000
50+
51+ return `ssr-${ timestamp . toString ( 16 ) } -${ counter . toString ( 16 ) } -${ random1 . toString ( 16 ) } -${ random2 . toString ( 16 ) } `
1852}
1953
2054export const isBrowser = ( ) => typeof window !== 'undefined' && typeof document !== 'undefined'
0 commit comments