-
Notifications
You must be signed in to change notification settings - Fork 5
fix: auth refresh #697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: auth refresh #697
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,8 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let qrData = $state<string>(''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let isMobile = $state(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let errorMessage = $state<string | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let eventSource: EventSource | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let refreshTimer: ReturnType<typeof setTimeout> | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function checkMobile() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isMobile = window.innerWidth <= 640; // Tailwind's `sm` breakpoint | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -69,6 +71,71 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function watchEventStream(id: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sseUrl = new URL(`/api/auth/sessions/${id}`, PUBLIC_PICTIQUE_BASE_URL).toString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const newEventSource = new EventSource(sseUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newEventSource.onopen = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('Successfully connected.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorMessage = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newEventSource.onmessage = (e) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const data = JSON.parse(e.data as string); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for error messages (version mismatch) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.error && data.type === 'version_mismatch') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorMessage = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data.message || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Your eID Wallet app version is outdated. Please update to continue.'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newEventSource.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+83
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for JSON parsing.
🛠️ Suggested fix newEventSource.onmessage = (e) => {
- const data = JSON.parse(e.data as string);
+ let data;
+ try {
+ data = JSON.parse(e.data as string);
+ } catch (parseError) {
+ console.error('Failed to parse SSE message:', parseError);
+ return;
+ }
// Check for error messages (version mismatch)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle successful authentication | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.user && data.token) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { user } = data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setAuthId(user.id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { token } = data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setAuthToken(token); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goto('/home'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newEventSource.onerror = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('SSE connection error'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newEventSource.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return newEventSource; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function fetchOfferAndSetupSSE() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Clean up existing SSE connection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (eventSource) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Clean up existing refresh timer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (refreshTimer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearTimeout(refreshTimer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refreshTimer = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { data } = await apiClient.get('/api/auth/offer'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| qrData = data.uri; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource = watchEventStream(new URL(qrData).searchParams.get('session') as string); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+125
to
+128
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling and validate session parameter. Two concerns:
🛠️ Suggested fix async function fetchOfferAndSetupSSE() {
// Clean up existing SSE connection
if (eventSource) {
eventSource.close();
eventSource = null;
}
// Clean up existing refresh timer
if (refreshTimer) {
clearTimeout(refreshTimer);
refreshTimer = null;
}
- const { data } = await apiClient.get('/api/auth/offer');
- qrData = data.uri;
-
- eventSource = watchEventStream(new URL(qrData).searchParams.get('session') as string);
+ try {
+ const { data } = await apiClient.get('/api/auth/offer');
+ qrData = data.uri;
+
+ const session = new URL(qrData).searchParams.get('session');
+ if (!session) {
+ console.error('Session parameter missing from offer URI');
+ return;
+ }
+ eventSource = watchEventStream(session);
+ } catch (error) {
+ console.error('Failed to fetch offer:', error);
+ return;
+ }
// Set up auto-refresh after 60 seconds🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set up auto-refresh after 60 seconds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refreshTimer = setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('Refreshing QR code after 60 seconds'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fetchOfferAndSetupSSE().catch((error) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('Error refreshing QR code:', error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, 60000); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onMount(async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkMobile(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.addEventListener('resize', checkMobile); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -90,51 +157,17 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If no query params, proceed with normal flow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { data } = await apiClient.get('/api/auth/offer'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| qrData = data.uri; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function watchEventStream(id: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sseUrl = new URL(`/api/auth/sessions/${id}`, PUBLIC_PICTIQUE_BASE_URL).toString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const eventSource = new EventSource(sseUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource.onopen = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('Successfully connected.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorMessage = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource.onmessage = (e) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const data = JSON.parse(e.data as string); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for error messages (version mismatch) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.error && data.type === 'version_mismatch') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorMessage = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data.message || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Your eID Wallet app version is outdated. Please update to continue.'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle successful authentication | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (data.user && data.token) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { user } = data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setAuthId(user.id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { token } = data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setAuthToken(token); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goto('/home'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fetchOfferAndSetupSSE(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource.onerror = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('SSE connection error'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onDestroy(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.removeEventListener('resize', checkMobile); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (eventSource) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eventSource.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (refreshTimer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clearTimeout(refreshTimer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| watchEventStream(new URL(qrData).searchParams.get('session') as string); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onDestroy(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.removeEventListener('resize', checkMobile); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </script> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -211,8 +244,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span class="mb-1 block font-bold text-gray-600" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| >The {isMobileDevice() ? 'button' : 'code'} is valid for 60 seconds</span | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span class="block font-light text-gray-600">Please refresh the page if it expires</span | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span class="block font-light text-gray-600">It will refresh automatically</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p class="w-full rounded-md bg-white/60 p-4 text-center text-xs leading-4 text-black/40"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling and validate session parameter.
Two concerns consistent with the Svelte implementation:
axios.getcall isn't wrapped in try/catch - if it fails,setQrwon't be called andeventSourceRef.currentremains with the old (closed) value.as stringassertion on line 76 is unsafe -searchParams.get('session')can returnnull.🛠️ Suggested fix
const getOfferData = useCallback(async (): Promise<void> => { // Clean up existing SSE connection if (eventSourceRef.current) { eventSourceRef.current.close(); } // Clean up existing refresh timer if (refreshTimerRef.current) { clearTimeout(refreshTimerRef.current); } - const { data } = await axios.get<{ uri: string }>( - new URL( - '/api/auth/offer', - process.env.NEXT_PUBLIC_BASE_URL - ).toString() - ); - setQr(data.uri); - eventSourceRef.current = watchEventStream( - new URL(data.uri).searchParams.get('session') as string - ); + try { + const { data } = await axios.get<{ uri: string }>( + new URL( + '/api/auth/offer', + process.env.NEXT_PUBLIC_BASE_URL + ).toString() + ); + setQr(data.uri); + + const session = new URL(data.uri).searchParams.get('session'); + if (!session) { + console.error('Session parameter missing from offer URI'); + return; + } + eventSourceRef.current = watchEventStream(session); + } catch (error) { + console.error('Failed to fetch offer:', error); + return; + } // Set up auto-refresh after 60 seconds📝 Committable suggestion
🤖 Prompt for AI Agents