Skip to content

Commit 183645d

Browse files
committed
♻️ Add a non-crypto fallback for crypto.randomUUID() (#2446)
1 parent 771f597 commit 183645d

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

src/lib/utils/auth_forms.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export const isDevelopmentMode = (): boolean => {
151151
* Contains constraints and shape definitions used across different form strategies
152152
*/
153153
const createBaseAuthForm = () => ({
154-
id: 'error-fallback-form-' + crypto.randomUUID(), // Generate unique form ID for fallback
154+
id: getBaseAuthFormId(),
155155
data: { username: '', password: '' },
156156
constraints: {
157157
username: { minlength: 3, maxlength: 24, required: true, pattern: '[\\w]*' },
@@ -167,3 +167,25 @@ const createBaseAuthForm = () => ({
167167
password: { type: 'string' },
168168
},
169169
});
170+
171+
/**
172+
* Generates a unique identifier for authentication form elements.
173+
*
174+
* Uses Web Crypto API's randomUUID() when available, falling back to a
175+
* timestamp-based random string for environments where crypto is unavailable.
176+
*
177+
* @returns A unique string identifier prefixed with 'error-fallback-form-'
178+
*
179+
* @example
180+
* ```typescript
181+
* const formId = getBaseAuthFormId();
182+
* // Returns: "error-fallback-form-550e8400-e29b-41d4-a716-446655440000"
183+
* // or: "error-fallback-form-1703875200000-abc123def"
184+
* ```
185+
*/
186+
const getBaseAuthFormId = () => {
187+
return (
188+
'error-fallback-form-' +
189+
(globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`)
190+
); // Fallback when Web Crypto is unavailable
191+
};

src/test/lib/utils/auth_forms.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,19 @@ describe('auth_forms', () => {
189189
expect(crypto.randomUUID).toHaveBeenCalled();
190190
});
191191

192+
test('expect to use fallback ID generation when crypto.randomUUID is unavailable', async () => {
193+
// Mock crypto.randomUUID to be undefined
194+
vi.stubGlobal('crypto', {
195+
randomUUID: undefined,
196+
});
197+
198+
vi.mocked(superValidate).mockRejectedValueOnce(new Error('Primary strategy failed'));
199+
200+
const result = await createAuthFormWithFallback();
201+
202+
expect(result.form.id).toMatch(/^error-fallback-form-\d+-[a-z0-9]+$/);
203+
});
204+
192205
test('expect to use fallback strategy when primary strategy fails', async () => {
193206
// Mock superValidate to fail
194207
vi.mocked(superValidate).mockRejectedValueOnce(new Error('SuperValidate failed'));

0 commit comments

Comments
 (0)