chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore: make applications reject older wallets - #424

Merged
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform
Nov 13, 2025
Merged

chore: make applications reject older wallets#424
coodos merged 1 commit into
mainfrom
feat/prevent-older-versions-from-using-the-platform

Conversation

@coodos

@coodoscoodos commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Description of change

Issue Number

Type of change

  • Breaking (any change that would cause existing functionality to not work as expected)
  • New (a change which implements a new feature)
  • Update (a change which updates existing functionality)
  • Fix (a change which fixes an issue)
  • Docs (changes to the documentation)
  • Chore (refactoring, build scripts or anything else that isn't user-facing)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

Release Notes

  • New Features
    • App version validation now enforced during authentication; users on outdated app versions will receive clear error messages
    • Enhanced error handling and user-friendly messaging for authentication failures
  • Bug Fixes
    • Improved timeout and error handling for message fetching in maintenance notifications
    • Enhanced logging for network and HTTP-related errors

@coderabbitai

coderabbitaiBot commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements app version gating for client authentication across multiple platforms. Client apps now send appVersion "0.4.0" in auth payloads; servers validate against MIN_REQUIRED_VERSION and reject older clients with a version_mismatch error. Client UIs handle version errors by displaying user-facing messages and halting login flow. Version utility modules provide semantic version comparison logic.

Changes

Cohort / File(s)Summary
Version Utilities
platforms/blabsy-w3ds-auth-api/src/utils/version.ts, platforms/dreamsync-api/src/utils/version.ts, platforms/evoting-api/src/utils/version.ts, platforms/group-charter-manager-api/src/utils/version.ts, platforms/pictique-api/src/utils/version.ts
Added semantic version comparison utilities: compareVersions() splits versions by dots, compares numeric segments, and returns -1/0/1; isVersionValid() checks if appVersion >= minVersion.
Auth Controllers - Version Validation
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts, platforms/dreamsync-api/src/controllers/AuthController.ts, platforms/evoting-api/src/controllers/AuthController.ts, platforms/group-charter-manager-api/src/controllers/AuthController.ts, platforms/pictique-api/src/controllers/AuthController.ts
Added version validation in login flow: imports isVersionValid, defines MIN_REQUIRED_VERSION = "0.4.0", validates appVersion from request body, and emits version_mismatch error (400 response) if validation fails; proceeds with existing auth flow on valid version.
Client Auth Payload
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts
Added appVersion: "0.4.0" to authentication payload in createScanLogic flow.
Login UI - SSE Error Handling
platforms/blabsy/src/components/login/login-main.tsx, platforms/dreamSync/client/src/components/auth/login-screen.tsx, platforms/eVoting/src/components/auth/login-screen.tsx, platforms/group-charter-manager/src/components/auth/login-screen.tsx, platforms/pictique/src/routes/(auth)/auth/+page.svelte
Added errorMessage state to track authentication errors; SSE message parsing detects version_mismatch errors, displays user-friendly error banner, closes stream, and halts processing; on SSE open event clears error message; maintains existing successful authentication flow.
Maintenance Banner - Network Error Handling
platforms/blabsy/src/components/common/maintenance-banner.tsx
Introduced 5-second timeout on MOTD fetch request; added catch block distinguishing network errors (ECONNABORTED, ERR_NETWORK) handled silently, while non-network errors (HTTP errors) are logged for debugging.
Package.json Dependency
platforms/eVoting/package.json
Added next.js dependency version ^1.0.3.

Sequence Diagram

sequenceDiagram
actor User
participant Client as Client App<br/>(v0.4.0)
participant Server as Auth Server
participant UI as UI Component
User->>Client: Initiate QR Scan
Client->>Server: POST /login with<br/>appVersion: "0.4.0"
alt Version Valid
Server->>Server: isVersionValid("0.4.0")<br/>returns true
Server->>Client: Send token via SSE
Client->>UI: Receive token event
UI->>UI: Sign in & Navigate
else Version Invalid
Server->>Server: isVersionValid(appVersion)<br/>returns false
Server->>Client: Send version_mismatch error
Client->>UI: Receive error event
UI->>UI: Set errorMessage
UI->>User: Display Error Banner
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • Version utility implementations: Review consistency of semantic version comparison logic across 5 nearly-identical modules to catch edge cases (e.g., missing segments, malformed input)
  • Auth controller changes: Verify version validation placement, error emission patterns, and flow control (early returns) are consistent and correct across 5 platform-specific implementations
  • UI error handling: Confirm SSE message parsing, error state management, and UI rendering are consistent; verify stream closure and early exit mechanisms prevent race conditions
  • Dependency addition: Verify next.js version constraint and compatibility
  • Cross-platform consistency: Ensure MIN_REQUIRED_VERSION value and error messages are synchronized across all platforms; check for any platform-specific branching or exceptions

Possibly related PRs

  • Fix/eid onboarding #415: Modifies the same createScanLogic implementation in infrastructure/eid-wallet; both PRs alter the scan authentication flow.
  • Fix/author details #229: Modifies SSE auth flow in pictique login component; related through version_mismatch error handling changes.
  • Feat/social media platforms #195: Alters eid-wallet QR-scan authentication flow and POST payload structure; directly related to appVersion additions.

Suggested reviewers

  • sosweetham
  • grv-saini-20
  • JulienAuvo

Poem

🐰 Hop and authenticate, version gates align,
Across the platforms, all clients now define,
Their versions declared, no older ones pass,
A semantic hop forward, no need to backslash! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Description check⚠️ WarningThe PR description is entirely empty of substantive information; only the template structure is present with no values filled in for Issue Number, Type of change, How the change has been tested, or any checklist items completed.Fill in the description with: Issue Number (if applicable), select the Type of change, explain how changes were tested, and verify the checklist items before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 55.56% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'chore: make applications reject older wallets' accurately summarizes the main purpose of the changeset, which implements version validation across multiple applications and platforms.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prevent-older-versions-from-using-the-platform

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coodos
coodos merged commit 6148a99 into mainNov 13, 2025
3 of 4 checks passed
@coodos
coodos deleted the feat/prevent-older-versions-from-using-the-platform branch November 13, 2025 15:54

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (6)
platforms/group-charter-manager-api/src/utils/version.ts (1)

1-32: Same issues as dreamsync-api version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts. Please refer to the comments on that file regarding:

  • Missing input validation for malformed version strings
  • NaN handling in compareVersions
  • Code duplication across services
platforms/group-charter-manager-api/src/controllers/AuthController.ts (2)

5-7: Version validation imports and configuration follow the same pattern.

The implementation is consistent with other AuthControllers. See comments on platforms/dreamsync-api/src/controllers/AuthController.ts regarding:

  • Externalizing MIN_REQUIRED_VERSION to environment configuration
  • Extracting validation logic to shared middleware

70-82: Version validation logic is correct and consistent.

The validation implementation follows the same well-structured pattern as other AuthControllers. See previous comments for suggestions about extracting this to shared middleware.

platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)

5-7: Version validation follows established pattern.

The implementation is consistent with other AuthControllers. Refer to comments on platforms/dreamsync-api/src/controllers/AuthController.ts for suggestions about:

  • Externalizing MIN_REQUIRED_VERSION
  • Extracting to shared middleware

Also applies to: 70-82

platforms/evoting-api/src/controllers/AuthController.ts (1)

6-8: Version validation implementation is consistent and correct.

This follows the same well-structured pattern as the other AuthControllers. All previous comments regarding externalization of MIN_REQUIRED_VERSION and extracting to shared middleware apply here as well.

Also applies to: 69-81

platforms/evoting-api/src/utils/version.ts (1)

1-32: Identical implementation to other version utilities.

This file is identical to platforms/dreamsync-api/src/utils/version.ts and platforms/group-charter-manager-api/src/utils/version.ts. All previous comments about input validation, NaN handling, and code duplication apply here.

🧹 Nitpick comments (8)
infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1)

265-265: Consider extracting version to a constant.

The hardcoded version string "0.4.0" could become a maintenance burden if it needs to be updated across multiple files. Consider extracting it to a constant or reading it from package.json.

// At the top of the file or in a separate config fileconstAPP_VERSION="0.4.0";// Then use it in the payloadconstauthPayload={ename: vault.ename,session: get(session),w3id: w3idResult,signature: signature,appVersion: APP_VERSION,};

Alternatively, if the wallet has a package.json, consider reading the version dynamically to keep it in sync with the actual app version.

platforms/eVoting/src/components/auth/login-screen.tsx (1)

40-45: LGTM - Consider adding onopen handler.

The version mismatch handling is correct. Optionally, consider adding an onopen handler to clear any previous error messages when the connection successfully opens, as done in the blabsy implementation:

eventSource.onopen=()=>{setErrorMessage(null);};
platforms/pictique-api/src/controllers/AuthController.ts (1)

81-93: LGTM - Good fail-fast version validation.

The version check correctly:

  • Validates before expensive operations (user lookup)
  • Treats missing appVersion as outdated (secure default)
  • Provides clear error messages to both SSE listeners and the HTTP response
  • Uses the shared utility function for consistency

Consider extracting MIN_REQUIRED_VERSION to an environment variable or config file for easier updates across deployments:

constMIN_REQUIRED_VERSION=process.env.MIN_WALLET_VERSION||"0.4.0";
platforms/dreamsync-api/src/controllers/AuthController.ts (2)

6-8: Consider externalizing MIN_REQUIRED_VERSION to environment configuration.

While hardcoding the version is acceptable, moving it to an environment variable or configuration file would allow updating the minimum version requirement without code changes or redeployment.

-const MIN_REQUIRED_VERSION = "0.4.0";+const MIN_REQUIRED_VERSION = process.env.MIN_APP_VERSION || "0.4.0";

69-81: Consider extracting version validation to shared middleware.

This version validation logic is duplicated across multiple AuthControllers. Consider extracting it to a reusable middleware function to maintain consistency and reduce duplication.

Example middleware:

// utils/versionMiddleware.tsexportfunctionvalidateAppVersion(appVersion: string|undefined,minVersion: string,session: string,eventEmitter: EventEmitter): {valid: boolean;response?: any}{if(!appVersion||!isVersionValid(appVersion,minVersion)){consterrorMessage={error: true,message: `Your eID Wallet app version is outdated. Please update to version ${minVersion} or later.`,type: "version_mismatch"};eventEmitter.emit(session,errorMessage);return{valid: false,response: {error: "App version too old",message: errorMessage.message}};}return{valid: true};}
platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

106-111: Error UI is clear and appropriately styled.

The error display is well-positioned and clearly communicates the authentication failure to users. Consider adding a dismiss button or refresh action if users need to retry without a full page refresh.

platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)

7-20: Recommended: Add input validation for better error handling.

Consider validating the version string format before processing to provide clearer error messages:

 export function compareVersions(version1: string, version2: string): number {
+ const versionPattern = /^\d+(\.\d+)*$/;+ if (!versionPattern.test(version1) || !versionPattern.test(version2)) {+ throw new Error('Invalid version format: expected format like "1.2.3"');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
-- // Validate that all parts are valid numbers- if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {- throw new Error('Invalid version format: versions must contain only numeric segments');- }
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;
}

This provides more specific error messages and validates format upfront, making the NaN check unnecessary.


7-20: Optional: Consider full semantic versioning support for future needs.

The current implementation handles only numeric segments (major.minor.patch). If you later need to support pre-release versions (e.g., "1.0.0-alpha") or build metadata (e.g., "1.0.0+20130313144700"), consider using the semver npm package for full SemVer 2.0 compliance. For now, the simple implementation is adequate for the app version gating use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1caaf and 097fb76.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts (1 hunks)
  • platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1 hunks)
  • platforms/blabsy/src/components/common/maintenance-banner.tsx (2 hunks)
  • platforms/blabsy/src/components/login/login-main.tsx (3 hunks)
  • platforms/dreamSync/client/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/dreamsync-api/src/controllers/AuthController.ts (3 hunks)
  • platforms/dreamsync-api/src/utils/version.ts (1 hunks)
  • platforms/eVoting/package.json (1 hunks)
  • platforms/eVoting/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/evoting-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/evoting-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/utils/version.ts (1 hunks)
  • platforms/group-charter-manager/src/components/auth/login-screen.tsx (3 hunks)
  • platforms/pictique-api/src/controllers/AuthController.ts (2 hunks)
  • platforms/pictique-api/src/utils/version.ts (1 hunks)
  • platforms/pictique/src/routes/(auth)/auth/+page.svelte (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
platforms/evoting-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (4)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts (1)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)
platforms/eVoting/src/lib/authUtils.ts (2)
  • setAuthId (18-20)
  • setAuthToken (5-7)
platforms/pictique-api/src/controllers/AuthController.ts (1)
platforms/pictique-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/controllers/AuthController.ts (1)
platforms/group-charter-manager-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/evoting-api/src/controllers/AuthController.ts (1)
platforms/evoting-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/controllers/AuthController.ts (1)
platforms/dreamsync-api/src/utils/version.ts (1)
  • isVersionValid (28-30)
platforms/group-charter-manager-api/src/utils/version.ts (4)
platforms/blabsy-w3ds-auth-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/dreamsync-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/evoting-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
platforms/pictique-api/src/utils/version.ts (2)
  • compareVersions (7-20)
  • isVersionValid (28-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (11)
platforms/blabsy/src/components/common/maintenance-banner.tsx (3)

1-88: This file doesn't relate to the PR objective.

The PR is titled "make applications reject older wallets" and the summary describes app version gating for client authentication. However, this file only implements timeout and error handling improvements for the maintenance banner MOTD fetch, with no version checking or wallet rejection logic.


21-23: Good addition of timeout.

The 5-second timeout is appropriate for a non-critical MOTD fetch and prevents the request from hanging indefinitely if the registry service is unresponsive.


31-46: Error handling logic is sound.

The enhanced error handling correctly distinguishes between network failures (which are silently handled since the registry may be unavailable) and other errors like HTTP status errors, which are logged for debugging. The string comparison error.message === 'Network Error' is a common pattern for backward compatibility with different Axios versions.

platforms/dreamSync/client/src/components/auth/login-screen.tsx (2)

50-55: LGTM - Clean version mismatch handling.

The version error handling correctly:

  • Checks for the specific error type
  • Sets a user-friendly message with a fallback
  • Closes the EventSource to prevent further processing
  • Returns early to avoid executing the success path

121-126: LGTM - Clear error UI feedback.

The error banner provides clear visual feedback to users when authentication fails due to version mismatch, with appropriate styling and structure.

platforms/blabsy/src/components/login/login-main.tsx (2)

21-24: Good practice - Clear error on reconnection.

Clearing the error message when the SSE connection opens successfully ensures users see current status and don't get confused by stale error messages.


34-42: LGTM - Consistent version mismatch handling.

The error handling follows the same pattern as other login components in the PR, ensuring a consistent user experience across platforms.

platforms/group-charter-manager/src/components/auth/login-screen.tsx (1)

53-76: LGTM - Comprehensive error and success handling.

The authentication flow correctly:

  • Checks for version mismatch errors first with early return
  • Only processes successful authentication when both user and token exist
  • Clears error message on connection open
  • Properly closes EventSource before page reload

The implementation is consistent with the version gating pattern across the codebase.

platforms/dreamsync-api/src/utils/version.ts (1)

28-30: Function logic is correct.

The implementation correctly delegates to compareVersions. However, it will inherit any input validation issues from that function.

platforms/dreamsync-api/src/controllers/AuthController.ts (1)

69-81: Version validation logic is well-implemented.

The validation correctly treats missing appVersion as invalid, provides user-friendly error messages, and notifies both SSE clients and the HTTP response. The type: "version_mismatch" field enables proper client-side error handling.

platforms/pictique/src/routes/(auth)/auth/+page.svelte (1)

57-64: Version mismatch error handling is well-implemented.

The code properly detects version mismatch errors, sets user-facing error messages with a fallback, closes the SSE connection, and halts further processing. This aligns well with the server-side version validation.

const { ename, session } = req.body;
const { ename, session, appVersion } = req.body;

console.log(req.body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug logging statement should be removed before merging to production. Logging the entire request body can expose sensitive data (ename, session tokens, etc.) in production logs.

- console.log(req.body)-
if (!ename) {
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(req.body)
if(!ename){
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/controllers/AuthController.ts around line
60, remove the debug console.log(req.body) statement; if you need request
inspection keep it out of production by replacing it with a proper logger call
that only logs non-sensitive fields or use logger.debug behind an environment
check (e.g., log only when NODE_ENV !== 'production' or redact tokens/PII) so no
full request bodies are written to production logs.

Comment on lines +7 to +30
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Consolidate duplicated version utilities into a shared module.

This exact implementation is duplicated across 5 platform APIs (dreamsync-api, evoting-api, group-charter-manager-api, pictique-api, and blabsy-w3ds-auth-api). This violates DRY principles and creates maintenance burden—any bug fix or enhancement must be applied to all 5 copies.

Consider extracting these utilities into a shared package (e.g., @project/version-utils or a shared/utils module) that all platform APIs can import.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Handle invalid version strings to prevent NaN comparison bugs.

The function uses .map(Number) without validating that version strings contain only numeric segments. If a version contains non-numeric parts (e.g., "1.2.a", "abc"), Number() returns NaN, and all comparisons with NaN evaluate to false. This causes the function to incorrectly return 0 (versions equal), which could allow malformed version strings to bypass validation in the authentication flow.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Validate that all parts are valid numbers+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Invalid version format: versions must contain only numeric segments');+ }
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;
}
🤖 Prompt for AI Agents
In platforms/blabsy-w3ds-auth-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation maps segments to Number without validating, which
yields NaN for non-numeric segments and makes comparisons always false; update
the function to first trim the input, split on '.', validate each segment with a
strict numeric check (e.g., /^\d+$/) and reject invalid version strings by
throwing a descriptive error (or returning a distinct error code) before
performing numeric comparisons, then parse segments with parseInt and proceed
with the existing numeric comparison logic.

Comment on lines +1 to +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;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated version utilities to a shared package.

This exact implementation is duplicated across at least 5 services in the codebase. In a monorepo, shared utilities should be extracted to a common package (e.g., packages/shared-utils or packages/version-utils) and imported by each service.

Consider creating a shared package structure:

// packages/shared-utils/src/version.tsexportfunctioncompareVersions(version1: string,version2: string): number{// implementation}exportfunctionisVersionValid(appVersion: string,minVersion: string): boolean{// implementation}

Then import in each service:

import{isVersionValid}from'@metastate/shared-utils/version';
🤖 Prompt for AI Agents
platforms/dreamsync-api/src/utils/version.ts lines 1-32: this version utility is
duplicated across services and should be extracted to a shared package; create a
new workspace package (e.g., packages/shared-utils or packages/version-utils),
move the compareVersions and isVersionValid implementations into that package
and export them, update the monorepo package.json/workspace config to include
the new package, replace the local functions here with an import from the new
package (e.g., import { isVersionValid } from '@your-org/shared-utils/version'),
remove the duplicate copies from other services or update their imports to the
shared package, run installs/build and update any tests or type imports to
ensure the new package compiles and all services reference the single shared
implementation.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add input validation and handle malformed version strings.

The function doesn't validate inputs or handle non-numeric segments. When split('.').map(Number) encounters non-numeric segments (e.g., "1.a.0"), it produces NaN. Since NaN < x and NaN > x both return false, the function incorrectly returns 0 (equal) for malformed versions.

Apply this diff to add validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be null or empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric segments');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
🤖 Prompt for AI Agents
In platforms/dreamsync-api/src/utils/version.ts around lines 7 to 20, the
compareVersions function currently maps segments with Number and fails silently
on non-numeric parts (yielding NaN and incorrect equality). Change the
implementation to validate inputs: ensure both inputs are non-empty strings,
split on '.', parse each segment with parseInt(segment, 10), and if any parsed
segment is NaN or not a valid integer (or contains non-digit characters after
trimming) throw a clear Error (or return a distinct value) indicating a
malformed version string; then compare numeric segments with missing parts
treated as 0 as before. Ensure you add unit tests or callers handle the thrown
error.

"lucide-react": "^0.453.0",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"next.js": "^1.0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove incorrect dependency - wrong package name.

The dependency "next.js" is NOT the official Next.js package. The official package is "next" (which is already present on line 47). The "next.js" package on npm is an outdated, unofficial package that was last published 10 years ago.

Apply this diff to remove the incorrect dependency:

 "next": "15.4.2",
"next-qrcode": "^2.5.1",
- "next.js": "^1.0.3",
"qrcode.react": "^4.2.0",
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"next.js": "^1.0.3",
"next": "15.4.2",
"next-qrcode": "^2.5.1",
"qrcode.react": "^4.2.0",
🤖 Prompt for AI Agents
In platforms/eVoting/package.json around line 49, remove the incorrect
dependency "next.js": "^1.0.3" because the official Next.js package is "next"
(already present on line 47); update package.json by deleting the "next.js"
entry from the dependencies block and run a quick npm/yarn install to ensure
lockfile and node_modules are consistent.

Comment on lines +7 to +20
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation and consider edge cases.

The compareVersions function lacks input validation and has limited semantic versioning support:

  1. No validation: Invalid inputs like empty strings, non-numeric segments, or malformed versions will cause issues. split('.').map(Number) will produce NaN for non-numeric parts.
  2. Limited semver: Doesn't handle pre-release versions (1.0.0-alpha) or build metadata (1.0.0+build123).
  3. Code duplication: This exact implementation is duplicated across multiple platform APIs (blabsy, dreamsync, evoting, group-charter-manager, pictique). Consider extracting to a shared utility package.

Apply this diff to add basic validation:

 export function compareVersions(version1: string, version2: string): number {
+ if (!version1 || !version2) {+ throw new Error('Version strings cannot be empty');+ }+
const v1Parts = version1.split('.').map(Number);
const v2Parts = version2.split('.').map(Number);
++ // Check for invalid version parts (NaN)+ if (v1Parts.some(isNaN) || v2Parts.some(isNaN)) {+ throw new Error('Version strings must contain only numeric parts separated by dots');+ }
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {

For the code duplication, consider creating a shared @metastate/version-utils package that all platforms can import.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportfunctioncompareVersions(version1: string,version2: string): number{
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
exportfunctioncompareVersions(version1: string,version2: string): number{
if(!version1||!version2){
thrownewError('Version strings cannot be empty');
}
constv1Parts=version1.split('.').map(Number);
constv2Parts=version2.split('.').map(Number);
// Check for invalid version parts (NaN)
if(v1Parts.some(isNaN)||v2Parts.some(isNaN)){
thrownewError('Version strings must contain only numeric parts separated by dots');
}
for(leti=0;i<Math.max(v1Parts.length,v2Parts.length);i++){
constv1Part=v1Parts[i]||0;
constv2Part=v2Parts[i]||0;
if(v1Part<v2Part)return-1;
if(v1Part>v2Part)return1;
}
return0;
}
🤖 Prompt for AI Agents
In platforms/pictique-api/src/utils/version.ts around lines 7 to 20, the
compareVersions implementation lacks input validation, mishandles non-numeric
segments, and doesn't account for basic semantic-version pre-release/build
rules; update it to first validate both inputs (non-empty strings), trim them,
and split off build metadata (+...) to ignore it, then split a core numeric
version and optional pre-release (by '-') and ensure all numeric core segments
parse to finite numbers (throw or return an error on invalid input), compare
core numeric segments as before, and if cores are equal handle pre-release
precedence (a version without a pre-release has higher precedence than one with;
otherwise compare prerelease identifiers by numeric where possible, or
lexicographically), and finally factor this logic into a shared package (e.g.,
@metastate/version-utils) to remove duplication across platforms so each API
imports the common utility.

Comment on lines +24 to +36
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';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Potential duplication with mobile-detection utilities.

The platform detection logic here may duplicate functionality already available in the imported mobile-detection utils (line 11). The store URLs are also hardcoded but should use the imported environment variables (PUBLIC_APP_STORE_EID_WALLET, PUBLIC_PLAY_STORE_EID_WALLET from lines 4-6).

 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';- }-- 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';+ const userAgent =+ navigator.userAgent || navigator.vendor || (window as { opera?: string }).opera || '';+ if (/android/i.test(userAgent)) {+ return PUBLIC_PLAY_STORE_EID_WALLET;+ }+ if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {+ return PUBLIC_APP_STORE_EID_WALLET;+ }+ return PUBLIC_PLAY_STORE_EID_WALLET;
}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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';
}
function getAppStoreLink(): string {
const userAgent =
navigator.userAgent || navigator.vendor || (window as { opera?:string }).opera || '';
if (/android/i.test(userAgent)) {
return PUBLIC_PLAY_STORE_EID_WALLET;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !('MSStream' in window)) {
return PUBLIC_APP_STORE_EID_WALLET;
}
return PUBLIC_PLAY_STORE_EID_WALLET;
}

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@coodos@sosweetham