Skip to content

feat: add encrypted token storage and proper logout handling#492

Open
spc-28 wants to merge 3 commits intoCircuitVerse:masterfrom
spc-28:auth#488
Open

feat: add encrypted token storage and proper logout handling#492
spc-28 wants to merge 3 commits intoCircuitVerse:masterfrom
spc-28:auth#488

Conversation

@spc-28
Copy link
Copy Markdown

@spc-28 spc-28 commented Feb 14, 2026

Fixes #488

Describe the changes you have made in this PR:
-Add SecureStorageService using flutter_secure_storage for encrypted token and user data storage (iOS Keychain, Android KeyStore)
-Remove debug print statements that logged sensitive data
-Improve Google OAuth error handling with null checks
-Update LocalStorageService to use encrypted storage with caching

Summary by CodeRabbit

  • New Features

    • Encrypted secure storage service added and used for auth token/user; logout now calls backend and clears secure data.
  • Bug Fixes

    • Safer Google Sign‑In flow with additional null checks and clearer error states.
    • Dropdowns switched to initial-value usage (affects selection behavior across rebuilds).
  • Documentation

    • OAuth configuration header updated; README notes secure storage.
  • Chores

    • Android/Gradle/tooling and Dart SDK updates; Windows plugin registration added; localization config tweak; devtools metadata added.

@netlify
Copy link
Copy Markdown

netlify bot commented Feb 14, 2026

Deploy Preview for cv-mobile-app-web ready!

Name Link
🔨 Latest commit 6da9422
🔍 Latest deploy log https://app.netlify.com/projects/cv-mobile-app-web/deploys/69af25583818360008214644
😎 Deploy Preview https://deploy-preview-492--cv-mobile-app-web.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new SecureStorageService (singleton wrapper around FlutterSecureStorage) and integrates it into LocalStorageService with in-memory caching, initializeSecureData(), and clearSecureData(). Locator initialization now awaits secure data initialization. Introduces UsersApi.logout() and calls it from CVLandingViewModel before clearing secure data. Adds flutter_secure_storage and raises the Dart SDK lower bound. Updates Android toolchain and Gradle wrapper versions, adjusts README header and mobile service note, removes synthetic-package from l10n.yaml, registers quill_native_bridge_windows for Windows, changes several DropdownButtonFormField usages from value to initialValue, and hardens Google sign-in null checks.

Possibly related PRs

Suggested labels

potential-ai-slop

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several unrelated changes detected: Android/Gradle version updates, Kotlin plugin updates, devtools_options.yaml addition, DropdownButtonFormField value→initialValue changes in three views, and Windows plugin registrations are outside #488 security scope. Isolate security-focused changes to a separate PR; move dependency/tooling updates (Android, Gradle, Kotlin, Windows plugins) and UI field property changes to dedicated maintenance PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main changes: adding encrypted token storage (SecureStorageService) and proper logout handling (logout API call with secure data clearing).
Linked Issues check ✅ Passed All three objectives from issue #488 are addressed: (A) encrypted storage via SecureStorageService using flutter_secure_storage [#488]; (B) removed debug prints logging sensitive data [#488]; (C) backend logout revocation added via UsersApi.logout() [#488].

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@spc-28
Copy link
Copy Markdown
Author

spc-28 commented Feb 14, 2026

@hardik17771 @JatsuAkaYashvant please let me know, any further changes are required.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/viewmodels/cv_landing_viewmodel.dart (2)

42-55: ⚠️ Potential issue | 🔴 Critical

Network failure in logout() blocks the entire local cleanup — user can't log out offline.

If _usersApi.logout() throws (e.g., no network), the exception propagates and lines 45–54 never execute, leaving the user stuck in a logged-in state. Backend revocation should be best-effort; local cleanup must always proceed.

🐛 Proposed fix
  void onLogout() async {
-   await _usersApi.logout();
-
+   try {
+     await _usersApi.logout();
+   } catch (_) {
+     // Best-effort: proceed with local cleanup even if server revocation fails
+   }
+
    _storage.isLoggedIn = false;
    _storage.currentUser = null;
    _storage.token = null;
    await _storage.clearSecureData();

97-99: ⚠️ Potential issue | 🟠 Major

onLogout() is not awaited — logout may be incomplete when the snackbar shows.

onLogout() is declared as void (not Future<void>), so the call at line 98 is fire-and-forget. The selectedIndex = 0 and SnackBarUtils.showDark(...) on lines 99–103 execute immediately, before the async logout work finishes.

♻️ Suggested fix
- void onLogout() async {
+ Future<void> onLogout() async {
    if (_dialogResponse?.confirmed ?? false) {
-     onLogout();
+     await onLogout();
      selectedIndex = 0;
🤖 Fix all issues with AI agents
In `@android/settings.gradle`:
- Around line 21-22: Kotlin was bumped to 2.1.0; inspect MainActivity.kt and any
Kotlin-coded dependencies for 2.1.0 breaking changes: replace any usages of the
locale-sensitive toLowerCase()/toUpperCase() with lowercase()/uppercase(), fix
nullability annotations/usages to satisfy JSpecify stricter nullability (add
explicit ? or !! or safe calls as appropriate), and verify code relying on K2
smart-cast propagation (e.g., long-lived mutable vars or casts in functions) is
made explicit with local val copies or explicit casts; update any affected
functions, properties, or call sites in MainActivity.kt and Kotlin-based libs to
compile cleanly under Kotlin 2.1.0.

In `@lib/services/API/users_api.dart`:
- Around line 224-233: The logout() method currently swallows all exceptions in
its catch block; update the catch to capture the error (and optionally stack)
like catch (e, s) and record it instead of ignoring—use the project's logger (or
at minimum print) to log a clear message including the endpoint
(EnvironmentConfig.CV_API_BASE_URL + '/auth/logout'), the headers context, and
the exception and stack; keep the method non-throwing so UX isn't blocked but
ensure ApiUtils.delete failures are observable for debugging and monitoring.

In `@lib/services/secure_storage_service.dart`:
- Around line 6-22: The public default constructor of SecureStorageService
allows callers to instantiate it directly and bypass getInstance(), which can
leave _secureStorage null; make the class constructor private (e.g.,
SecureStorageService._()) so external code cannot call SecureStorageService()
and must use SecureStorageService.getInstance(), and verify that getInstance
initializes _secureStorage and returns the singleton _instance; update any
tests/usages to call getInstance() if necessary.
- Around line 52-54: The current deleteAll() implementation calls
_secureStorage!.deleteAll(), which will erase every key in FlutterSecureStorage;
change it to only remove keys owned by this service by iterating over the
service's known keys (or by reading all keys and filtering by your service
prefix) and calling _secureStorage!.delete(key: key) for each; update
SecureStorageService (or the class containing deleteAll) to store a list of
owned keys or a key prefix (e.g., a private List<String> _ownedKeys or String
_keyPrefix) and use that list/prefix in deleteAll() instead of deleteAll().

In `@windows/flutter/generated_plugin_registrant.cc`:
- Line 14: The generated registrant incorrectly includes a placeholder header
and registration for quill_native_bridge_windows (the include
<quill_native_bridge_windows/none.h> and the noneRegisterWithRegistrar call);
remove the bogus include and registration invocation from the
generated_plugin_registrant.cc so the plugin is not registered on the C++ side,
and instead ensure the package only exposes Dart-only registration
(QuillNativeBridgeWindows.registerWith) upstream; search for the symbols
"none.h" and "noneRegisterWithRegistrar" and delete those lines or update the
plugin's Windows metadata so the generated C++ does not emit the placeholder.
🧹 Nitpick comments (6)
android/app/build.gradle (1)

76-77: targetSdkVersion 33 is quite stale relative to compileSdk 36.

Google Play requires targetSdkVersion 34 for new app updates (since August 2024) and will require 35 soon. Consider bumping targetSdkVersion as well to avoid Play Store rejection.

lib/services/API/users_api.dart (1)

36-36: Parameter name mismatch between abstract class and implementation.

The abstract class declares bool subscribed (line 36) while the implementation uses bool isSubscribed (line 167). While Dart allows positional parameter names to differ, this inconsistency can confuse callers and future maintainers.

Align the abstract declaration
   Future<User>? updateProfile(
     String name,
     String? educationalInstitute,
     String? country,
-    bool subscribed,
+    bool isSubscribed,
     File? image,
     bool removePicture,
   );

Also applies to: 167-167

lib/services/secure_storage_service.dart (1)

36-42: Consider handling JSON decode errors for corrupted user data.

If the stored JSON is malformed (e.g., due to a schema change or corruption), json.decode or User.fromJson will throw an unhandled exception. A try-catch that deletes the corrupt entry and returns null would be more resilient.

Proposed improvement
   Future<User?> get currentUser async {
     final userJson = await _secureStorage!.read(key: _userKey);
     if (userJson == null || userJson == 'null') {
       return null;
     }
-    return User.fromJson(json.decode(userJson));
+    try {
+      return User.fromJson(json.decode(userJson));
+    } catch (_) {
+      await _secureStorage!.delete(key: _userKey);
+      return null;
+    }
   }
lib/services/local_storage_service.dart (2)

57-60: Fire-and-forget writes to secure storage lose errors and persistence guarantees.

The token and currentUser setters call async secure-storage methods without await, so write failures are silently swallowed and data may not persist if the app is killed immediately after. Since Dart setters can't be async, consider converting these to explicit async methods (e.g., Future<void> setToken(...)) so callers can await them.

♻️ Suggested approach
- String? get token => _cachedToken;
-
- set token(String? _token) {
-   _cachedToken = _token;
-   _secureStorage!.setToken(_token);
- }
+ String? get token => _cachedToken;
+
+ Future<void> setToken(String? _token) async {
+   _cachedToken = _token;
+   await _secureStorage!.setToken(_token);
+ }

Apply the same pattern to currentUser. All call-sites that currently use the setter syntax (_storage.token = value) would need to be updated to await _storage.setToken(value).


47-48: Instance fields for cache in a singleton with static factory — works but fragile.

_cachedToken and _cachedUser are instance fields, while _instance is static and reused. This works because only one instance exists, but if getInstance() were ever called after the singleton was created (returning the existing _instance), a new LocalStorageService() would not be constructed, so caches are safe. Just noting this coupling is implicit.

lib/viewmodels/cv_landing_viewmodel.dart (1)

50-52: authType check occurs after token is already nullified.

Line 47 sets _storage.token = null and line 48 clears secure data, but line 50 reads _storage.authType (stored in SharedPreferences, so still available). This happens to work because authType is in SharedPreferences, not secure storage. However, if authType is ever moved to secure storage, this will break. Consider reading authType before clearing state, or explicitly clearing it afterward.

Comment thread android/settings.gradle
Comment on lines +21 to +22
id "com.android.application" version "8.9.1" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find all Kotlin files in the repo
fd -e kt -e kts

Repository: CircuitVerse/mobile-app

Length of output: 138


🏁 Script executed:

# Check the full android/settings.gradle file for context
cat -n android/settings.gradle

Repository: CircuitVerse/mobile-app

Length of output: 967


🏁 Script executed:

# Search for Kotlin-related configurations in gradle files
rg -l "kotlin" --type gradle

Repository: CircuitVerse/mobile-app

Length of output: 115


🏁 Script executed:

# Look for any kotlinOptions or compiler settings
rg "kotlinOptions|compileOptions|jvmTarget" --type gradle -A 3

Repository: CircuitVerse/mobile-app

Length of output: 492


🏁 Script executed:

# Check build.gradle files for kotlin dependencies
rg "kotlin\(" --type gradle

Repository: CircuitVerse/mobile-app

Length of output: 49


🏁 Script executed:

# Check the android/app/build.gradle for more context on Kotlin configuration
cat -n android/app/build.gradle | head -100

Repository: CircuitVerse/mobile-app

Length of output: 3823


🌐 Web query:

Kotlin 2.1.0 K2 compiler default breaking changes migration guide

💡 Result:

Kotlin 2.1.0 + “K2 is the default”: what to migrate, what breaks, and how to de-risk

1) Reality check: K2 became the default before 2.1.0

Kotlin’s K2 compiler frontend is enabled by default starting with Kotlin 2.0.0, so moving to 2.1.0 mainly means you’re picking up additional K2 behavior changes + 2.1.x compatibility changes, not “turning K2 on for the first time.” [1]


2) The official migration docs you should follow (in this order)

  1. K2 compiler migration guide (core “what changed in the compiler frontend” + rollback options) [1]
  2. Compatibility guide for Kotlin 2.1.x (source/binary/behavioral breaks from 2.0 → 2.1) [2]
  3. What’s new in Kotlin 2.1.0 (high-level release notes + notable deprecations) [3]

3) The K2-related breaking/behavior changes that most often bite during 2.1.0 upgrades

A) K2 frontend semantic/typing differences (may change inference / behavior)

The 2.1.x compatibility guide calls out behavior changes in K2 smart-cast propagation (bidirectional propagation for inferred variables; explicitly typed variables stop propagating type info). This can change which branches compile or which overload is chosen. [2]

B) Java nullability becomes stricter (common “suddenly red” upgrade pain)

In 2.1.x, JSpecify nullability mismatches are treated as errors by default (“strict” mode) unless you override diagnostics with -Xnullability-annotations. [2]

C) Standard library deprecations that become errors in 2.1.0

A common one: locale-sensitive case conversion APIs (String.toLowerCase(), toUpperCase(), etc.) are error-level in 2.1.0; replace with lowercase()/uppercase() (optionally with an explicit locale). [2] (also noted in the 2.1.0 release notes) [3]


4) K2 migration-guide “compiler changed” items to scan for in your codebase

The K2 compiler migration guide highlights several categories that can break builds or change resolution, including:

  • Immediate initialization of open properties with backing fields
  • Deprecated synthetic setters on a projected receiver
  • Forbidden use of inaccessible generic types
  • More consistent resolution between Kotlin properties and Java fields with the same name
  • Improved null safety for Java primitive arrays
  • Stricter rules for abstract members in expect classes [1]

(If you hit odd compilation errors after upgrading, these bullets are usually where the explanation is.)


5) Practical migration steps (safe, incremental)

  • Upgrade Kotlin to 2.1.0 and run CI with warnings-as-errors off first (to see the delta). [3]
  • Verify compiler plugins are K2-compatible (K2 support is not universal across all plugins). The K2 migration guide lists supported plugins and notes that custom frontend plugins may need a rewrite. [1]
  • If you’re on Compose Multiplatform: note that from Compose Multiplatform 1.8.0, some klibs can be consumed only with Kotlin 2.1.0+ (so this upgrade may be mandatory depending on your dependency graph). [4]

6) Rollback / “stabilize first” options if you need time

The K2 migration guide documents how to roll back behavior by setting an older language version (for example, -language-version 1.9). This can be useful to unblock a release while you fix K2-related issues properly. [1]


Key sources

  • K2 compiler migration guide (official): [1]
  • Kotlin 2.1.x compatibility guide (official): [2]
  • Kotlin 2.1.0 release notes (official): [3]
  • Compose Multiplatform K2 migration note / Kotlin 2.1.0 requirement: [4]

🌐 Web query:

Kotlin 2.1.0 android plugin compatibility requirements

💡 Result:

Kotlin 2.1.0 + Android (Kotlin Gradle plugin 2.1.0) compatibility requirements

  • Minimum Android Gradle Plugin (AGP) for the Kotlin Gradle plugin: AGP 7.3.1+ (this is the Kotlin plugin’s own minimum supported AGP). [1]
  • Minimum Gradle version for Kotlin 2.1.0: Gradle 7.6.3+. [1]
  • Recommended / “fully compatible” Gradle range (per Kotlin 2.1.0 release notes): 7.6.3–8.6; 8.7–8.10 are supported with a noted exception (KMP withJava() deprecation warnings). [1]

Important Android build-tools constraint (D8/R8 via AGP)

Even though the Kotlin plugin supports AGP 7.3.1+, Google’s Android toolchain compatibility table lists Kotlin 2.1 as requiring at least:

  • AGP 8.6+ (bundles matching D8/R8), and
  • R8 8.6.17+. [2]

Practical takeaway: use AGP 8.6+ (and its matching Gradle version) if you want to stay within the officially required Android toolchain versions for Kotlin 2.1; otherwise, you may be in a “Kotlin plugin supports it, but Android shrinker/tooling may not” zone. [1][2]

Sources: Kotlin 2.1.0 release notes / Gradle & AGP minimums [1]; Android Developers Kotlin↔AGP/R8 requirements table [2].


Review Kotlin 2.1.0 compatibility—verify codebase handles breaking changes.

Kotlin 2.1.0 introduces breaking changes beyond K2 (which became default in 2.0.0): JSpecify nullability is stricter by default, locale-sensitive case conversion APIs (toLowerCase(), toUpperCase()) are now errors (use lowercase()/uppercase()), and K2 smart-cast propagation behavior has changed. Your Android Gradle Plugin 8.9.1 is compatible with Kotlin 2.1.0, but verify the single Kotlin file (MainActivity.kt) and any Kotlin-based dependencies handle these 2.1.0 changes.

🤖 Prompt for AI Agents
In `@android/settings.gradle` around lines 21 - 22, Kotlin was bumped to 2.1.0;
inspect MainActivity.kt and any Kotlin-coded dependencies for 2.1.0 breaking
changes: replace any usages of the locale-sensitive toLowerCase()/toUpperCase()
with lowercase()/uppercase(), fix nullability annotations/usages to satisfy
JSpecify stricter nullability (add explicit ? or !! or safe calls as
appropriate), and verify code relying on K2 smart-cast propagation (e.g.,
long-lived mutable vars or casts in functions) is made explicit with local val
copies or explicit casts; update any affected functions, properties, or call
sites in MainActivity.kt and Kotlin-based libs to compile cleanly under Kotlin
2.1.0.

Comment thread lib/services/secure_storage_service.dart
Comment thread lib/services/secure_storage_service.dart
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <quill_native_bridge_windows/none.h>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the file in question
head -40 windows/flutter/generated_plugin_registrant.cc

Repository: CircuitVerse/mobile-app

Length of output: 1845


🏁 Script executed:

# Search for quill_native_bridge_windows references in the codebase
rg "quill_native_bridge_windows" --type yaml --type lock --type txt -i

Repository: CircuitVerse/mobile-app

Length of output: 163


🏁 Script executed:

# Check for pubspec files that might reference this plugin
fd "pubspec.yaml" | head -20

Repository: CircuitVerse/mobile-app

Length of output: 79


🏁 Script executed:

# Look for the plugin registration metadata
find . -name "*.yaml" -o -name "*.yml" | xargs grep -l "quill_native_bridge_windows" 2>/dev/null | head -10

Repository: CircuitVerse/mobile-app

Length of output: 49


🌐 Web query:

quill_native_bridge_windows flutter plugin windows registration

💡 Result:

quill_native_bridge_windows uses Dart-side registration (it doesn’t require a Windows C++ RegisterWithRegistrar step).

  • If you depend on quill_native_bridge (the app-facing federated plugin), the Windows implementation is auto-included on Windows builds; you normally don’t add or register quill_native_bridge_windows yourself. (pub.dev)
  • The Windows “registration” is just a static Dart method that sets the platform-interface singleton:
    QuillNativeBridgeWindows.registerWith()QuillNativeBridgePlatform.instance = QuillNativeBridgeWindows(); (pub.dev)

When would you manually register?

Only if you’re doing something non-standard (custom plugin registrant / unusual bootstrap). Then call it early (before using QuillNativeBridge), e.g.:

import 'package:quill_native_bridge_windows/quill_native_bridge_windows.dart';

void main() {
  QuillNativeBridgeWindows.registerWith();
  // runApp(...)
}

Citations:


Suspicious none.h header and noneRegisterWithRegistrar — upstream plugin misconfiguration.

The quill_native_bridge_windows plugin appears in the C++ registrant as #include <quill_native_bridge_windows/none.h> (line 14) and noneRegisterWithRegistrar(...) (line 30-31). This is incorrect: quill_native_bridge_windows uses Dart-side registration only (QuillNativeBridgeWindows.registerWith()) and should not require C++ registration. The "none.h" header and "none" plugin name are placeholder values indicating a misconfiguration in the plugin's Windows registration metadata. This is an upstream issue in the quill_native_bridge_windows package; verify this doesn't cause build or runtime issues on Windows.

🤖 Prompt for AI Agents
In `@windows/flutter/generated_plugin_registrant.cc` at line 14, The generated
registrant incorrectly includes a placeholder header and registration for
quill_native_bridge_windows (the include <quill_native_bridge_windows/none.h>
and the noneRegisterWithRegistrar call); remove the bogus include and
registration invocation from the generated_plugin_registrant.cc so the plugin is
not registered on the C++ side, and instead ensure the package only exposes
Dart-only registration (QuillNativeBridgeWindows.registerWith) upstream; search
for the symbols "none.h" and "noneRegisterWithRegistrar" and delete those lines
or update the plugin's Windows metadata so the generated C++ does not emit the
placeholder.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@lib/services/API/users_api.dart`:
- Around line 224-226: The logout() override is currently empty; implement it to
call the backend logout endpoint (POST/DELETE to /auth/logout per API spec) to
revoke the server-side token before clearing local state. In the UsersApi
class/function logout(), perform the network request using the existing HTTP
client (reuse the same client used by other methods like
login()/refreshToken()), handle non-2xx responses by logging or throwing an
error, and only then clear local credentials/session cache so the token is
revoked server-side as required by the PR.

In `@lib/services/local_storage_service.dart`:
- Around line 47-55: The synchronous getters token and currentUser can return
null before initializeSecureData() populates _cachedToken/_cachedUser; add an
initialization guard (e.g., a private bool _initialized set true at the end of
initializeSecureData()) and assert or throw a clear StateError when token or
currentUser is accessed while !_initialized (or use assert(_initialized) in
debug builds) so callers get a loud failure during development; update
getToken() to still populate lazily but keep the synchronous getters protected
using the _initialized flag and reference _cachedToken, _cachedUser,
initializeSecureData(), token, currentUser, and getToken() in the change.
- Around line 57-60: The token setter currently updates _cachedToken and calls
_secureStorage!.setToken(_token) fire-and-forget; if the async write fails the
error is lost and in-memory state is inconsistent—update the setter for token
(and mirror the same change for the currentUser setter) to attach a completion
handler to the Future returned by _secureStorage.setToken (e.g., using
.catchError or .then(..., onError: ...)) and log or surface failures via the
existing logger so persistence errors are observable; ensure you handle nullable
_secureStorage safely and include context in the log message (e.g., "Failed to
persist token" / "Failed to persist currentUser") and do the same for the
currentUser setter.

In `@lib/services/secure_storage_service.dart`:
- Around line 36-42: The currentUser getter can throw during json.decode or
User.fromJson causing startup crashes; wrap the deserialization in a try-catch
inside the currentUser getter, and on exception delete the corrupt entry via
_secureStorage!.delete(key: _userKey) and return null so initializeSecureData
can continue; optionally log the error (e.g., using your logger) for
diagnostics.
🧹 Nitpick comments (1)
lib/services/secure_storage_service.dart (1)

13-15: Consider specifying AndroidOptions for completeness.

You've configured IOSOptions for Keychain accessibility but haven't set AndroidOptions. While the defaults use EncryptedSharedPreferences, explicitly passing aOptions: AndroidOptions(encryptedSharedPreferences: true) documents the intent and guards against future library default changes.

Comment thread lib/services/API/users_api.dart
Comment thread lib/services/local_storage_service.dart Outdated
Comment thread lib/services/local_storage_service.dart
Comment thread lib/services/secure_storage_service.dart
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@pubspec.yaml`:
- Line 55: Update the pubspec to flutter_secure_storage: ^10.0.0 and ensure the
package constraints in the environment section require Dart ≥3.3 (e.g., sdk:
">=3.3.0 <4.0.0") and your project uses Flutter ≥3.19; verify Android
configuration by checking android/app/build.gradle for minSdkVersion and follow
the flutter_secure_storage changelog instructions for any Jetpack Security /
cipher or migration setup; audit usages of shared_preferences (the symbol
shared_preferences) and remove or restrict it to non-sensitive data, or replace
sensitive storage with flutter_secure_storage and run the provided migration
tooling for Android if you keep existing encryptedSharedPreferences data.
- Line 18: Check whether any package in pubspec.yaml actually requires Dart
>=3.9.0 by running `dart pub deps --json` or inspecting dependency
pages/CHANGELOGs (notably flutter_secure_storage requires >=3.3), then if no
dependency needs 3.9.0, change the SDK constraint in pubspec.yaml from ">=3.9.0
<4.0.0" to the lowest required version (e.g., ">=3.3.0 <4.0.0") to maximize
compatibility; if a specific dependency does require 3.9.0, document that
dependency name and version in the pubspec.yaml comment and keep the bumped
constraint.

Comment thread pubspec.yaml

environment:
sdk: ">=3.7.2 <4.0.0"
sdk: ">=3.9.0 <4.0.0"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Dart SDK 3.9.0 release date stable

💡 Result:

Dart SDK 3.9.0 was released to the stable channel on August 13, 2025. [1] [2]


🌐 Web query:

flutter_secure_storage 10.0.0 version pub.dev

💡 Result:

flutter_secure_storage v10.0.0 is published on pub.dev (publisher: steenbakker.dev) and is the current stable 10.x release. It requires Dart SDK >= 3.3 (and the v10 beta notes indicate Flutter >= 3.19). [1][2]

Add it to pubspec.yaml:

dependencies:
  flutter_secure_storage: ^10.0.0

Key v10 highlights (from the changelog): major security/platform modernization, with a largely rewritten Android implementation due to Jetpack Security deprecation, new/updated default ciphers (RSA-OAEP + AES-GCM), improved biometrics support, and migration tools (with some breaking changes around encryptedSharedPreferences behavior/options). [3]


🏁 Script executed:

# Check the pubspec.yaml file to understand the actual changes
find . -name "pubspec.yaml" -type f | head -5

Repository: CircuitVerse/mobile-app

Length of output: 81


🏁 Script executed:

cat -n ./pubspec.yaml

Repository: CircuitVerse/mobile-app

Length of output: 3812


Verify that the Dart SDK 3.9.0 bump is actually required by dependencies.

Dart 3.9.0 is a confirmed stable release (August 13, 2025). However, flutter_secure_storage v10.0.0 only requires Dart >= 3.3, not 3.9.0. Verify whether other dependencies in this project mandate the 3.9.0 minimum. If not, prefer keeping the minimum SDK constraint as low as possible for broader compatibility with contributor environments and CI pipelines.

🤖 Prompt for AI Agents
In `@pubspec.yaml` at line 18, Check whether any package in pubspec.yaml actually
requires Dart >=3.9.0 by running `dart pub deps --json` or inspecting dependency
pages/CHANGELOGs (notably flutter_secure_storage requires >=3.3), then if no
dependency needs 3.9.0, change the SDK constraint in pubspec.yaml from ">=3.9.0
<4.0.0" to the lowest required version (e.g., ">=3.3.0 <4.0.0") to maximize
compatibility; if a specific dependency does require 3.9.0, document that
dependency name and version in the pubspec.yaml comment and keep the bumped
constraint.

Comment thread pubspec.yaml
scroll_to_index: ^3.0.1
share_plus: ^11.1.0
shared_preferences: ^2.5.4
flutter_secure_storage: ^10.0.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

flutter_secure_storage 10.0.0 pub.dev

💡 Result:

flutter_secure_storage v10.0.0 on pub.dev is the latest stable major release (published ~2 months ago) and requires Dart SDK ≥ 3.3 (Flutter ≥ 3.19). [1][2]

What changed in 10.0.0 (high level):

  • Major Android rewrite due to Jetpack Security deprecation: new secure cipher implementations, improved biometrics support, updated default ciphers, and migration tooling (including migration away from deprecated encryptedSharedPreferences). [3]
  • Platform modernization updates across supported platforms. [3]

Add to pubspec.yaml:

dependencies:
  flutter_secure_storage: ^10.0.0

[1]

Sources: pub.dev package page [1], versions [2], changelog [3].


flutter_secure_storage v10.0.0 is published and compatible.

Version 10.0.0 is the latest stable release and requires Dart SDK ≥ 3.3 (Flutter ≥ 3.19). The version includes a major Android rewrite due to Jetpack Security deprecation with improved cipher implementations and migration tooling to move away from deprecated encryptedSharedPreferences.

Ensure your project targets Dart 3.3+ and Flutter 3.19+. Check the changelog for any specific Android configuration requirements for your minSdkVersion.

Regarding shared_preferences (line 54): the migration away from encryptedSharedPreferences reinforces using secure storage for sensitive data. Verify whether shared_preferences is still needed for non-sensitive data or can be removed.

🤖 Prompt for AI Agents
In `@pubspec.yaml` at line 55, Update the pubspec to flutter_secure_storage:
^10.0.0 and ensure the package constraints in the environment section require
Dart ≥3.3 (e.g., sdk: ">=3.3.0 <4.0.0") and your project uses Flutter ≥3.19;
verify Android configuration by checking android/app/build.gradle for
minSdkVersion and follow the flutter_secure_storage changelog instructions for
any Jetpack Security / cipher or migration setup; audit usages of
shared_preferences (the symbol shared_preferences) and remove or restrict it to
non-sensitive data, or replace sensitive storage with flutter_secure_storage and
run the provided migration tooling for Android if you keep existing
encryptedSharedPreferences data.

@JatsuAkaYashvant
Copy link
Copy Markdown
Member

@spc-28 Please address the coderabbit's comment

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 30d4cb0f-e78d-4bec-a8b0-ee3ff19ceb21

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3e72b and 47212c4.

📒 Files selected for processing (3)
  • lib/services/API/users_api.dart
  • lib/services/local_storage_service.dart
  • lib/services/secure_storage_service.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/services/API/users_api.dart

Comment thread lib/services/local_storage_service.dart
Comment thread lib/services/secure_storage_service.dart
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
lib/services/local_storage_service.dart (1)

134-138: Consider setting isLoggedIn = false in clearSecureData() for defensive consistency.

While the current logout flow correctly sets isLoggedIn = false before calling clearSecureData(), the method would be more self-contained and resilient to misuse if it also cleared this flag. The initializeSecureData() method similarly sets isLoggedIn = false as a defensive measure when detecting inconsistent state (line 129), establishing a precedent for defensive state management in this class.

♻️ Proposed fix
  Future<void> clearSecureData() async {
    _cachedToken = null;
    _cachedUser = null;
    await _secureStorage!.deleteAll();
+   isLoggedIn = false;
  }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ac8e8af-abea-4ad5-958a-45e34af21fac

📥 Commits

Reviewing files that changed from the base of the PR and between 47212c4 and 6da9422.

📒 Files selected for processing (3)
  • lib/services/API/users_api.dart
  • lib/services/local_storage_service.dart
  • lib/services/secure_storage_service.dart
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/services/secure_storage_service.dart
  • lib/services/API/users_api.dart

@spc-28
Copy link
Copy Markdown
Author

spc-28 commented Mar 9, 2026

@JatsuAkaYashvant I have solved the issues addressed by coderabbit's review, the remaining reviews are version related

Please let me know if anymore changes needed

Sign up for free to 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.

Authentication related security vulnerabilities

2 participants