diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts index 9f45fa40..aae39b62 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts @@ -262,6 +262,7 @@ export function createScanLogic({ session: get(session), w3id: w3idResult, signature: signature, + appVersion: "0.4.0", }; console.log("🔐 Auth payload with signature:", { diff --git a/platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts b/platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts index a2463acc..34bf19e0 100644 --- a/platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts +++ b/platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts @@ -2,6 +2,10 @@ import { Request, Response } from "express"; import { v4 as uuidv4 } from "uuid"; import { EventEmitter } from "events"; import { auth } from "firebase-admin"; +import { isVersionValid } from "../utils/version"; + +const MIN_REQUIRED_VERSION = "0.4.0"; + export class AuthController { private eventEmitter: EventEmitter; @@ -51,13 +55,32 @@ export class AuthController { login = async (req: Request, res: Response) => { try { - const { ename, session } = req.body; + const { ename, session, appVersion } = req.body; console.log(req.body) if (!ename) { return res.status(400).json({ error: "ename is required" }); } + + if (!session) { + return res.status(400).json({ error: "session is required" }); + } + + // Check app version - missing version is treated as old version + if (!appVersion || !isVersionValid(appVersion, MIN_REQUIRED_VERSION)) { + const errorMessage = { + error: true, + message: `Your eID Wallet app version is outdated. Please update to version ${MIN_REQUIRED_VERSION} or later.`, + type: "version_mismatch" + }; + this.eventEmitter.emit(session, errorMessage); + return res.status(400).json({ + error: "App version too old", + message: errorMessage.message + }); + } + const token = await auth().createCustomToken(ename); console.log(token); diff --git a/platforms/blabsy-w3ds-auth-api/src/utils/version.ts b/platforms/blabsy-w3ds-auth-api/src/utils/version.ts new file mode 100644 index 00000000..256771a1 --- /dev/null +++ b/platforms/blabsy-w3ds-auth-api/src/utils/version.ts @@ -0,0 +1,32 @@ +/** + * Compares two semantic version strings + * @param version1 - First version string (e.g., "0.4.0") + * @param version2 - Second version string (e.g., "0.3.0") + * @returns -1 if version1 < version2, 0 if equal, 1 if version1 > version2 + */ +export function compareVersions(version1: string, version2: string): number { + const v1Parts = version1.split('.').map(Number); + const v2Parts = version2.split('.').map(Number); + + for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { + const v1Part = v1Parts[i] || 0; + const v2Part = v2Parts[i] || 0; + + if (v1Part < v2Part) return -1; + if (v1Part > v2Part) return 1; + } + + return 0; +} + +/** + * Checks if the app version meets the minimum required version + * @param appVersion - The version from the app (e.g., "0.4.0") + * @param minVersion - The minimum required version (e.g., "0.4.0") + * @returns true if appVersion >= minVersion, false otherwise + */ +export function isVersionValid(appVersion: string, minVersion: string): boolean { + return compareVersions(appVersion, minVersion) >= 0; +} + + diff --git a/platforms/blabsy/src/components/common/maintenance-banner.tsx b/platforms/blabsy/src/components/common/maintenance-banner.tsx index 7ad748e3..98e87c3d 100644 --- a/platforms/blabsy/src/components/common/maintenance-banner.tsx +++ b/platforms/blabsy/src/components/common/maintenance-banner.tsx @@ -18,7 +18,9 @@ export function MaintenanceBanner(): JSX.Element | null { const registryUrl = process.env.NEXT_PUBLIC_REGISTRY_URL || 'http://localhost:4321'; - const response = await axios.get(`${registryUrl}/motd`); + const response = await axios.get(`${registryUrl}/motd`, { + timeout: 5000 // 5 second timeout + }); setMotd(response.data); // Check if this message has been dismissed @@ -27,6 +29,19 @@ export function MaintenanceBanner(): JSX.Element | null { setIsDismissed(dismissed === response.data.message); } } catch (error) { + // Silently handle network errors - registry service may not be available + // Only log non-network errors for debugging + if (axios.isAxiosError(error)) { + if ( + error.code === 'ECONNABORTED' || + error.code === 'ERR_NETWORK' || + error.message === 'Network Error' + ) { + // Network error - registry service unavailable, silently fail + return; + } + } + // Log other errors (like 404, 500, etc.) for debugging console.error('Failed to fetch motd:', error); } }; diff --git a/platforms/blabsy/src/components/login/login-main.tsx b/platforms/blabsy/src/components/login/login-main.tsx index c1880cf7..55ec627e 100644 --- a/platforms/blabsy/src/components/login/login-main.tsx +++ b/platforms/blabsy/src/components/login/login-main.tsx @@ -9,6 +9,7 @@ import { isMobileDevice, getDeepLinkUrl } from '@lib/utils/mobile-detection'; export function LoginMain(): JSX.Element { const { signInWithCustomToken } = useAuth(); const [qr, setQr] = useState(); + const [errorMessage, setErrorMessage] = useState(null); function watchEventStream(id: string): void { const sseUrl = new URL( @@ -19,13 +20,37 @@ export function LoginMain(): JSX.Element { eventSource.onopen = (): void => { console.log('Successfully connected.'); + setErrorMessage(null); }; eventSource.onmessage = async (e): Promise => { - const data = JSON.parse(e.data as string) as { token: string }; - const { token } = data; - console.log(token); - await signInWithCustomToken(token); + const data = JSON.parse(e.data as string) as { + token?: string; + error?: boolean; + message?: string; + type?: string; + }; + + // Check for error messages (version mismatch) + if (data.error && data.type === 'version_mismatch') { + setErrorMessage( + data.message || + 'Your eID Wallet app version is outdated. Please update to continue.' + ); + eventSource.close(); + return; + } + + // Handle successful authentication + if (data.token) { + console.log(data.token); + await signInWithCustomToken(data.token); + } + }; + + eventSource.onerror = (): void => { + console.error('SSE connection error'); + eventSource.close(); }; } const getOfferData = async (): Promise => { @@ -89,6 +114,14 @@ export function LoginMain(): JSX.Element { Join Blabsy today.
+ {errorMessage && ( +
+

+ Authentication Error +

+

{errorMessage}

+
+ )} {isMobileDevice() ? (
diff --git a/platforms/dreamSync/client/src/components/auth/login-screen.tsx b/platforms/dreamSync/client/src/components/auth/login-screen.tsx index 91699a17..76038723 100644 --- a/platforms/dreamSync/client/src/components/auth/login-screen.tsx +++ b/platforms/dreamSync/client/src/components/auth/login-screen.tsx @@ -11,6 +11,7 @@ export function LoginScreen() { const [sessionId, setSessionId] = useState(""); const [isConnecting, setIsConnecting] = useState(false); const [isLoading, setIsLoading] = useState(true); + const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { const getAuthOffer = async () => { @@ -45,6 +46,15 @@ export function LoginScreen() { eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); + + // Check for error messages (version mismatch) + if (data.error && data.type === 'version_mismatch') { + setErrorMessage(data.message || 'Your eID Wallet app version is outdated. Please update to continue.'); + eventSource.close(); + return; + } + + // Handle successful authentication if (data.user && data.token) { setIsConnecting(true); // Store the token and user ID directly @@ -108,6 +118,13 @@ export function LoginScreen() {

+ {errorMessage && ( +
+

Authentication Error

+

{errorMessage}

+
+ )} + {qrCode && (
{isMobileDevice() ? ( diff --git a/platforms/dreamsync-api/src/controllers/AuthController.ts b/platforms/dreamsync-api/src/controllers/AuthController.ts index 05742e5b..b068d7a9 100644 --- a/platforms/dreamsync-api/src/controllers/AuthController.ts +++ b/platforms/dreamsync-api/src/controllers/AuthController.ts @@ -3,6 +3,9 @@ import { v4 as uuidv4 } from "uuid"; import { UserService } from "../services/UserService"; import { EventEmitter } from "events"; import { signToken } from "../utils/jwt"; +import { isVersionValid } from "../utils/version"; + +const MIN_REQUIRED_VERSION = "0.4.0"; export class AuthController { private userService: UserService; @@ -53,7 +56,7 @@ export class AuthController { login = async (req: Request, res: Response) => { try { - const { ename, session, w3id, signature } = req.body; + const { ename, session, w3id, signature, appVersion } = req.body; if (!ename) { return res.status(400).json({ error: "ename is required" }); @@ -63,6 +66,20 @@ export class AuthController { return res.status(400).json({ error: "session is required" }); } + // Check app version - missing version is treated as old version + if (!appVersion || !isVersionValid(appVersion, MIN_REQUIRED_VERSION)) { + const errorMessage = { + error: true, + message: `Your eID Wallet app version is outdated. Please update to version ${MIN_REQUIRED_VERSION} or later.`, + type: "version_mismatch" + }; + this.eventEmitter.emit(session, errorMessage); + return res.status(400).json({ + error: "App version too old", + message: errorMessage.message + }); + } + // Find user by ename (handles @ symbol variations) const user = await this.userService.findByEname(ename); diff --git a/platforms/dreamsync-api/src/utils/version.ts b/platforms/dreamsync-api/src/utils/version.ts new file mode 100644 index 00000000..256771a1 --- /dev/null +++ b/platforms/dreamsync-api/src/utils/version.ts @@ -0,0 +1,32 @@ +/** + * Compares two semantic version strings + * @param version1 - First version string (e.g., "0.4.0") + * @param version2 - Second version string (e.g., "0.3.0") + * @returns -1 if version1 < version2, 0 if equal, 1 if version1 > version2 + */ +export function compareVersions(version1: string, version2: string): number { + const v1Parts = version1.split('.').map(Number); + const v2Parts = version2.split('.').map(Number); + + for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { + const v1Part = v1Parts[i] || 0; + const v2Part = v2Parts[i] || 0; + + if (v1Part < v2Part) return -1; + if (v1Part > v2Part) return 1; + } + + return 0; +} + +/** + * Checks if the app version meets the minimum required version + * @param appVersion - The version from the app (e.g., "0.4.0") + * @param minVersion - The minimum required version (e.g., "0.4.0") + * @returns true if appVersion >= minVersion, false otherwise + */ +export function isVersionValid(appVersion: string, minVersion: string): boolean { + return compareVersions(appVersion, minVersion) >= 0; +} + + diff --git a/platforms/eVoting/package.json b/platforms/eVoting/package.json index dac7b5ff..d348e078 100644 --- a/platforms/eVoting/package.json +++ b/platforms/eVoting/package.json @@ -46,6 +46,7 @@ "lucide-react": "^0.453.0", "next": "15.4.2", "next-qrcode": "^2.5.1", + "next.js": "^1.0.3", "qrcode.react": "^4.2.0", "react": "19.1.0", "react-day-picker": "^9.11.1", diff --git a/platforms/eVoting/src/components/auth/login-screen.tsx b/platforms/eVoting/src/components/auth/login-screen.tsx index a49850fb..6c27558c 100644 --- a/platforms/eVoting/src/components/auth/login-screen.tsx +++ b/platforms/eVoting/src/components/auth/login-screen.tsx @@ -10,6 +10,7 @@ export function LoginScreen() { const [qrCode, setQrCode] = useState(""); const [sessionId, setSessionId] = useState(""); const [isConnecting, setIsConnecting] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { const getAuthOffer = async () => { @@ -35,6 +36,15 @@ export function LoginScreen() { eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); + + // Check for error messages (version mismatch) + if (data.error && data.type === 'version_mismatch') { + setErrorMessage(data.message || 'Your eID Wallet app version is outdated. Please update to continue.'); + eventSource.close(); + return; + } + + // Handle successful authentication if (data.user && data.token) { setIsConnecting(true); // Store the token and user ID directly @@ -71,6 +81,12 @@ export function LoginScreen() {
+ {errorMessage && ( +
+

Authentication Error

+

{errorMessage}

+
+ )} {qrCode ? (
{ try { - const { ename, session } = req.body; + const { ename, session, appVersion } = req.body; if (!ename) { return res.status(400).json({ error: "ename is required" }); } + if (!session) { + return res.status(400).json({ error: "session is required" }); + } + + // Check app version - missing version is treated as old version + if (!appVersion || !isVersionValid(appVersion, MIN_REQUIRED_VERSION)) { + const errorMessage = { + error: true, + message: `Your eID Wallet app version is outdated. Please update to version ${MIN_REQUIRED_VERSION} or later.`, + type: "version_mismatch" + }; + this.eventEmitter.emit(session, errorMessage); + return res.status(400).json({ + error: "App version too old", + message: errorMessage.message + }); + } + // Find user by ename (handles @ symbol variations) const user = await this.userService.findByEname(ename); diff --git a/platforms/evoting-api/src/utils/version.ts b/platforms/evoting-api/src/utils/version.ts new file mode 100644 index 00000000..256771a1 --- /dev/null +++ b/platforms/evoting-api/src/utils/version.ts @@ -0,0 +1,32 @@ +/** + * Compares two semantic version strings + * @param version1 - First version string (e.g., "0.4.0") + * @param version2 - Second version string (e.g., "0.3.0") + * @returns -1 if version1 < version2, 0 if equal, 1 if version1 > version2 + */ +export function compareVersions(version1: string, version2: string): number { + const v1Parts = version1.split('.').map(Number); + const v2Parts = version2.split('.').map(Number); + + for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { + const v1Part = v1Parts[i] || 0; + const v2Part = v2Parts[i] || 0; + + if (v1Part < v2Part) return -1; + if (v1Part > v2Part) return 1; + } + + return 0; +} + +/** + * Checks if the app version meets the minimum required version + * @param appVersion - The version from the app (e.g., "0.4.0") + * @param minVersion - The minimum required version (e.g., "0.4.0") + * @returns true if appVersion >= minVersion, false otherwise + */ +export function isVersionValid(appVersion: string, minVersion: string): boolean { + return compareVersions(appVersion, minVersion) >= 0; +} + + diff --git a/platforms/group-charter-manager-api/src/controllers/AuthController.ts b/platforms/group-charter-manager-api/src/controllers/AuthController.ts index fcb4a42c..17520ecb 100644 --- a/platforms/group-charter-manager-api/src/controllers/AuthController.ts +++ b/platforms/group-charter-manager-api/src/controllers/AuthController.ts @@ -2,6 +2,9 @@ import { Request, Response } from "express"; import { v4 as uuidv4 } from "uuid"; import { UserService } from "../services/UserService"; import { EventEmitter } from "events"; +import { isVersionValid } from "../utils/version"; + +const MIN_REQUIRED_VERSION = "0.4.0"; export class AuthController { private userService: UserService; @@ -54,12 +57,30 @@ export class AuthController { login = async (req: Request, res: Response) => { try { - const { ename, session } = req.body; + const { ename, session, appVersion } = req.body; if (!ename) { return res.status(400).json({ error: "ename is required" }); } + if (!session) { + return res.status(400).json({ error: "session is required" }); + } + + // Check app version - missing version is treated as old version + if (!appVersion || !isVersionValid(appVersion, MIN_REQUIRED_VERSION)) { + const errorMessage = { + error: true, + message: `Your eID Wallet app version is outdated. Please update to version ${MIN_REQUIRED_VERSION} or later.`, + type: "version_mismatch" + }; + this.eventEmitter.emit(session, errorMessage); + return res.status(400).json({ + error: "App version too old", + message: errorMessage.message + }); + } + const { user, token } = await this.userService.findUserByEname(ename); diff --git a/platforms/group-charter-manager-api/src/utils/version.ts b/platforms/group-charter-manager-api/src/utils/version.ts new file mode 100644 index 00000000..256771a1 --- /dev/null +++ b/platforms/group-charter-manager-api/src/utils/version.ts @@ -0,0 +1,32 @@ +/** + * Compares two semantic version strings + * @param version1 - First version string (e.g., "0.4.0") + * @param version2 - Second version string (e.g., "0.3.0") + * @returns -1 if version1 < version2, 0 if equal, 1 if version1 > version2 + */ +export function compareVersions(version1: string, version2: string): number { + const v1Parts = version1.split('.').map(Number); + const v2Parts = version2.split('.').map(Number); + + for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { + const v1Part = v1Parts[i] || 0; + const v2Part = v2Parts[i] || 0; + + if (v1Part < v2Part) return -1; + if (v1Part > v2Part) return 1; + } + + return 0; +} + +/** + * Checks if the app version meets the minimum required version + * @param appVersion - The version from the app (e.g., "0.4.0") + * @param minVersion - The minimum required version (e.g., "0.4.0") + * @returns true if appVersion >= minVersion, false otherwise + */ +export function isVersionValid(appVersion: string, minVersion: string): boolean { + return compareVersions(appVersion, minVersion) >= 0; +} + + diff --git a/platforms/group-charter-manager/src/components/auth/login-screen.tsx b/platforms/group-charter-manager/src/components/auth/login-screen.tsx index 844b1ce9..cf69e99d 100644 --- a/platforms/group-charter-manager/src/components/auth/login-screen.tsx +++ b/platforms/group-charter-manager/src/components/auth/login-screen.tsx @@ -12,6 +12,7 @@ export default function LoginScreen() { const [qrData, setQrData] = useState(""); const [isLoading, setIsLoading] = useState(true); const [isAuthenticating, setIsAuthenticating] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); const router = useRouter(); useEffect(() => { @@ -42,26 +43,37 @@ export default function LoginScreen() { eventSource.onopen = () => { console.log('Successfully connected to auth stream.'); + setErrorMessage(null); }; eventSource.onmessage = (e) => { const data = JSON.parse(e.data); console.log('Auth data received:', data); - const { user, token } = data; + // Check for error messages (version mismatch) + if (data.error && data.type === 'version_mismatch') { + setErrorMessage(data.message || 'Your eID Wallet app version is outdated. Please update to continue.'); + eventSource.close(); + return; + } - // Set authentication data - setAuthId(user.id); - setAuthToken(token); + // Handle successful authentication + if (data.user && data.token) { + const { user, token } = data; - // Close the event source - eventSource.close(); + // Set authentication data + setAuthId(user.id); + setAuthToken(token); - // Set authenticating state - setIsAuthenticating(true); + // Close the event source + eventSource.close(); - // Force a page refresh to trigger AuthProvider re-initialization - window.location.reload(); + // Set authenticating state + setIsAuthenticating(true); + + // Force a page refresh to trigger AuthProvider re-initialization + window.location.reload(); + } }; eventSource.onerror = (error) => { @@ -127,6 +139,13 @@ export default function LoginScreen() {

+ {errorMessage && ( +
+

Authentication Error

+

{errorMessage}

+
+ )} + {qrData && (
{isMobileDevice() ? ( diff --git a/platforms/pictique-api/src/controllers/AuthController.ts b/platforms/pictique-api/src/controllers/AuthController.ts index 2ca0ec97..8a0bafad 100644 --- a/platforms/pictique-api/src/controllers/AuthController.ts +++ b/platforms/pictique-api/src/controllers/AuthController.ts @@ -3,6 +3,10 @@ import { v4 as uuidv4 } from "uuid"; import { UserService } from "../services/UserService"; import { EventEmitter } from "events"; import { signToken } from "../utils/jwt"; +import { isVersionValid } from "../utils/version"; + +const MIN_REQUIRED_VERSION = "0.4.0"; + export class AuthController { private userService: UserService; private eventEmitter: EventEmitter; @@ -64,12 +68,30 @@ export class AuthController { login = async (req: Request, res: Response) => { try { - const { ename, session } = req.body; + const { ename, session, appVersion } = req.body; if (!ename) { return res.status(400).json({ error: "ename is required" }); } + if (!session) { + return res.status(400).json({ error: "session is required" }); + } + + // Check app version - missing version is treated as old version + if (!appVersion || !isVersionValid(appVersion, MIN_REQUIRED_VERSION)) { + const errorMessage = { + error: true, + message: `Your eID Wallet app version is outdated. Please update to version ${MIN_REQUIRED_VERSION} or later.`, + type: "version_mismatch" + }; + this.eventEmitter.emit(session, errorMessage); + return res.status(400).json({ + error: "App version too old", + message: errorMessage.message + }); + } + // Find user by ename (handles @ symbol variations) let user = await this.userService.findByEname(ename); diff --git a/platforms/pictique-api/src/utils/version.ts b/platforms/pictique-api/src/utils/version.ts new file mode 100644 index 00000000..256771a1 --- /dev/null +++ b/platforms/pictique-api/src/utils/version.ts @@ -0,0 +1,32 @@ +/** + * Compares two semantic version strings + * @param version1 - First version string (e.g., "0.4.0") + * @param version2 - Second version string (e.g., "0.3.0") + * @returns -1 if version1 < version2, 0 if equal, 1 if version1 > version2 + */ +export function compareVersions(version1: string, version2: string): number { + const v1Parts = version1.split('.').map(Number); + const v2Parts = version2.split('.').map(Number); + + for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { + const v1Part = v1Parts[i] || 0; + const v2Part = v2Parts[i] || 0; + + if (v1Part < v2Part) return -1; + if (v1Part > v2Part) return 1; + } + + return 0; +} + +/** + * Checks if the app version meets the minimum required version + * @param appVersion - The version from the app (e.g., "0.4.0") + * @param minVersion - The minimum required version (e.g., "0.4.0") + * @returns true if appVersion >= minVersion, false otherwise + */ +export function isVersionValid(appVersion: string, minVersion: string): boolean { + return compareVersions(appVersion, minVersion) >= 0; +} + + diff --git a/platforms/pictique/src/routes/(auth)/auth/+page.svelte b/platforms/pictique/src/routes/(auth)/auth/+page.svelte index 99c8e364..d02d11d6 100644 --- a/platforms/pictique/src/routes/(auth)/auth/+page.svelte +++ b/platforms/pictique/src/routes/(auth)/auth/+page.svelte @@ -13,33 +13,29 @@ import { onDestroy } from 'svelte'; import { qrcode } from 'svelte-qrcode-action'; - let qrData: string; - let isMobile = false; + let qrData = $state(''); + let isMobile = $state(false); + let errorMessage = $state(null); function checkMobile() { isMobile = window.innerWidth <= 640; // Tailwind's `sm` breakpoint } - let getAppStoreLink: () => string = () => ''; + function getAppStoreLink(): string { + const userAgent = + navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || ''; + if (/android/i.test(userAgent)) { + return 'https://play.google.com/store/apps/details?id=foundation.metastate.eid_wallet'; + } - onMount(async () => { - getAppStoreLink = () => { - const userAgent = - navigator.userAgent || - navigator.vendor || - (window as { opera?: string }).opera || - ''; - if (/android/i.test(userAgent)) { - return 'https://play.google.com/store/apps/details?id=foundation.metastate.eid_wallet'; - } - - if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) { - return 'https://apps.apple.com/in/app/eid-for-w3ds/id6747748667'; - } + if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) { + return 'https://apps.apple.com/in/app/eid-for-w3ds/id6747748667'; + } - return 'https://play.google.com/store/apps/details?id=foundation.metastate.eid_wallet'; - }; + return 'https://play.google.com/store/apps/details?id=foundation.metastate.eid_wallet'; + } + onMount(async () => { checkMobile(); window.addEventListener('resize', checkMobile); @@ -52,15 +48,34 @@ eventSource.onopen = () => { console.log('Successfully connected.'); + errorMessage = null; }; eventSource.onmessage = (e) => { const data = JSON.parse(e.data as string); - const { user } = data; - setAuthId(user.id); - const { token } = data; - setAuthToken(token); - goto('/home'); + + // 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'); + } + }; + + eventSource.onerror = () => { + console.error('SSE connection error'); + eventSource.close(); }; } @@ -88,6 +103,12 @@ Scan the QR code using your eID App to login {/if} + {#if errorMessage} +
+

Authentication Error

+

{errorMessage}

+
+ {/if} {#if qrData} {#if isMobileDevice()}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6525e97c..15acdb8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1683,6 +1683,9 @@ importers: next-qrcode: specifier: ^2.5.1 version: 2.5.1(react@18.3.1) + next.js: + specifier: ^1.0.3 + version: 1.0.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@18.3.1) @@ -11718,6 +11721,9 @@ packages: next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + next.js@1.0.3: + resolution: {integrity: sha512-nosnT7Jh4ML3kzwG9MPW+SfBKuHoZEMcCWw5kxwMYXoxnADGzIxWl+TdDMcEZP6Xl/t8m/5uyVlA9SBkH2oh/w==} + next@15.4.2: resolution: {integrity: sha512-oH1rmFso+84NIkocfuxaGKcXIjMUTmnzV2x0m8qsYtB4gD6iflLMESXt5XJ8cFgWMBei4v88rNr/j+peNg72XA==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} @@ -14998,7 +15004,7 @@ snapshots: '@babel/types': 7.28.4 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -15167,7 +15173,7 @@ snapshots: '@babel/parser': 7.28.4 '@babel/template': 7.27.2 '@babel/types': 7.28.4 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15948,7 +15954,7 @@ snapshots: '@eslint/eslintrc@1.4.1': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 @@ -16684,7 +16690,7 @@ snapshots: '@humanwhocodes/config-array@0.9.5': dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -19501,7 +19507,7 @@ snapshots: '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.1)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0)))(svelte@5.33.1)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0))': dependencies: '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.33.1)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0)) - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) svelte: 5.33.1 vite: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0) transitivePeerDependencies: @@ -19528,7 +19534,7 @@ snapshots: '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.1)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0))': dependencies: '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.1)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0)))(svelte@5.33.1)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0)) - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 @@ -21066,7 +21072,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 @@ -21397,7 +21403,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -22607,10 +22613,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.4.1: - dependencies: - ms: 2.1.3 - debug@4.4.1(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -23139,7 +23141,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.25.4): dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) esbuild: 0.25.4 transitivePeerDependencies: - supports-color @@ -23654,7 +23656,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) doctrine: 3.0.0 enquirer: 2.4.1 escape-string-regexp: 4.0.0 @@ -24778,7 +24780,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -24793,7 +24795,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -25130,7 +25132,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -27026,6 +27028,8 @@ snapshots: next-tick@1.1.0: {} + next.js@1.0.3: {} + next@15.4.2(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1): dependencies: '@next/env': 15.4.2 @@ -30475,7 +30479,7 @@ snapshots: vite-node@3.1.4(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0): dependencies: cac: 6.7.14 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(sass@1.89.1)(tsx@4.19.4)(yaml@2.8.0) @@ -30677,7 +30681,7 @@ snapshots: '@vitest/spy': 3.1.4 '@vitest/utils': 3.1.4 chai: 5.2.0 - debug: 4.4.1 + debug: 4.4.1(supports-color@5.5.0) expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3