Skip to content

Improve guest auth and reset flow#3477

Merged
Baalmart merged 2 commits into
stagingfrom
fix/guest-reset-flow
May 15, 2026
Merged

Improve guest auth and reset flow#3477
Baalmart merged 2 commits into
stagingfrom
fix/guest-reset-flow

Conversation

@Mozart299

@Mozart299 Mozart299 commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved session expiration handling and prevented duplicate user load requests
    • Enhanced form validation in password reset flow
  • Improvements

    • Updated reset code format from 5 to 6 digits
    • Strengthened password requirements: minimum 10 characters with uppercase, lowercase, numbers, and special characters
    • Refined authentication state navigation

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Mozart299 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 8 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 15d9108d-7f72-4cf2-981d-ebaf131dda1c

📥 Commits

Reviewing files that changed from the base of the PR and between 23f2217 and fffca16.

📒 Files selected for processing (2)
  • src/mobile/lib/src/app/auth/bloc/auth_bloc.dart
  • src/mobile/lib/src/app/dashboard/pages/dashboard_page.dart
📝 Walkthrough

Walkthrough

This PR refactors the mobile app's authentication session handling and password reset flow. Password reset validation shifts from 5-digit email-dependent pins to 6-digit codes validated independently. Auth bloc now emits GuestUser consistently on startup failures and session expiration. App navigation is simplified to route all auth states through NavPage, and user loading is guarded by a flag to prevent duplicate requests. Test doubles and cleanup are updated throughout.

Changes

Auth Flow and Password Reset Refactor

Layer / File(s) Summary
Password Reset Code Validation Contract and Implementation
src/mobile/lib/src/app/auth/repository/auth_repository.dart, src/mobile/lib/src/app/auth/repository/auth_repository_impl.dart
Interface and implementation for password reset validation are simplified: validatePinFormat(pin, email)validateResetCodeFormat(pin), validation logic now accepts exactly 6-digit numeric codes.
Password Reset Code Entry UI Update
src/mobile/lib/src/app/auth/pages/password_reset/reset_link_sent.dart
Reset code entry UI parameterizes the code length to 6 digits via resetCodeLength constant, updates PIN field width calculations and continue button validation, and adjusts error messaging.
Password Reset Form Validation and Submission
src/mobile/lib/src/app/auth/pages/password_reset/password_reset.dart
Form state uses late controller initialization, password requirements enforce 10+ characters with uppercase, lowercase, numeric, and special-character presence, submission validates form state before dispatch, and success listener clears controllers before navigation.
Password Reset Bloc Event Handlers
src/mobile/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart
Event handlers are refactored: request handler maintains loading/success flow, update handler reformats error messages, verify handler adds early exit for missing session email and calls new validateResetCodeFormat with updated success messaging, error helper reformats condition blocks.
Auth Bloc Initialization and Session Expiration
src/mobile/lib/src/app/auth/bloc/auth_bloc.dart
Initial state changes from AuthInitial to GuestUser, app startup wraps token refresh in try/catch to emit GuestUser on failure, session expiration handler emits GuestUser after clearing auth data in both paths.
App Navigation and User Load Guarding
src/mobile/lib/main.dart
WelcomeScreen import is removed, new _hasRequestedUserLoad flag guards LoadUser dispatch to occur once per auth session, token expiry check on resume is refactored, BlocConsumer listener manages user load flag and emits snackbars on expiration, all terminal auth states route through NavPage.
Dashboard Conditional User Load
src/mobile/lib/src/app/dashboard/pages/dashboard_page.dart
Dashboard initialization conditionally dispatches LoadUser only when AuthBloc is AuthLoaded, country lookup and active country assignment logic are reformatted for readability.
Widget Test Doubles and Setup Refactoring
src/mobile/test/widget_test.dart
SocialAuthRepository import and TestSocialAuthRepository class are added, TestAuthRepository replaces verifyResetPin with validateResetCodeFormat (6-digit validation), adds verifyEmailCode and deleteUserAccount, test setup uses debugPrint for errors and Hive cleanup in tearDown.
Test Cleanup: Mock Removal and Assertion Refactoring
src/mobile/test/app/shared/services/cache_manager_test.dart, src/mobile/test/app/dashboard/widgets/nearby_view_test.dart
Mock setup is simplified by removing unnecessary Mockito/Hive/Connectivity mocks, JSON deserialization cast is removed, assertions are rewrapped for clarity; nearby view test removes CacheManager mocking and simplifies test cases with explicit bloc stubbing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • airqo-platform/AirQo-frontend#3467: Modifies the same auth session-expiration flow in main.dart's Decider and auth_bloc.dart (token refresh/session-expired handling and state routing).

Suggested labels

mobile-app

Suggested reviewers

  • Baalmart

Poem

🔐 Passwords reset in patterns new — six digits strong, not five, it's true.
Guest states greet and tokens fade, one load per session, debounce made.
WelcomeScreen bows to NavPage's way, tests cleaned up to code more gray. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Improve guest auth and reset flow' directly reflects the main changes: authentication flow refactoring (guest user handling) and password reset improvements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/guest-reset-flow

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

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.

@coderabbitai coderabbitai Bot 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: 2

Caution

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

⚠️ Outside diff range comments (1)
src/mobile/lib/src/app/auth/pages/password_reset/password_reset.dart (1)

27-38: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Dispose of TextEditingControllers before re-instantiation to prevent resource leaks.

The field-level initialization creates controller instances that are immediately replaced in initState() without being disposed. This leaks resources. Initialize them only in initState() by removing the field-level assignment.

Proposed fix
-  late TextEditingController passwordConfirmController =
-      TextEditingController();
-  late TextEditingController passwordController = TextEditingController();
+  late final TextEditingController passwordConfirmController;
+  late final TextEditingController passwordController;
   late GlobalKey<FormState> formKey = GlobalKey<FormState>();
   bool _isPasswordVisible = false;
   bool _isConfirmPasswordVisible = false;
 
   `@override`
   void initState() {
     super.initState();
     passwordConfirmController = TextEditingController();
     passwordController = TextEditingController();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/lib/src/app/auth/pages/password_reset/password_reset.dart` around
lines 27 - 38, The TextEditingController fields passwordConfirmController and
passwordController are being instantiated at field declaration and then
reassigned in initState, leaking resources; remove the field-level initializers
so the controllers are only created inside initState, and implement dispose to
call passwordConfirmController.dispose() and passwordController.dispose();
update the class methods (initState and add a dispose override) to ensure
controllers are created once and properly disposed to prevent leaks.
🧹 Nitpick comments (2)
src/mobile/test/widget_test.dart (1)

151-151: ⚡ Quick win

Tighten the MaterialApp assertion to avoid false positives.

Using findsWidgets here will still pass if multiple MaterialApp roots are introduced accidentally. Prefer findsOneWidget for a stricter regression signal.

Suggested change
-        expect(find.byType(MaterialApp), findsWidgets);
+        expect(find.byType(MaterialApp), findsOneWidget);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/test/widget_test.dart` at line 151, The assertion using
expect(find.byType(MaterialApp), findsWidgets) is too loose; update the test in
widget_test.dart to assert exactly one MaterialApp by replacing findsWidgets
with findsOneWidget so the test fails if multiple MaterialApp roots are present
(locate the expect call using find.byType(MaterialApp) and change the matcher to
findsOneWidget).
src/mobile/test/app/shared/services/cache_manager_test.dart (1)

104-107: ⚡ Quick win

Keep an explicit map cast in the deserialization converter.

(json) => json relaxes this test and won’t verify the expected Map<String, dynamic> shape as strictly as before.

Suggested change
         final restored = CachedData<Map<String, dynamic>>.fromJson(
           jsonData,
-          (json) => json,
+          (json) => json as Map<String, dynamic>,
         );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/test/app/shared/services/cache_manager_test.dart` around lines 104
- 107, The test currently uses a lax converter `(json) => json` when calling
`CachedData<Map<String, dynamic>>.fromJson`, which fails to enforce the expected
Map<String, dynamic> shape; replace the converter with an explicit cast such as
`(json) => Map<String, dynamic>.from(json as Map)` or `(json) => (json as
Map).cast<String, dynamic>()` so the deserializer for `CachedData.fromJson` is
validated against a proper Map<String, dynamic>.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mobile/lib/src/app/auth/bloc/auth_bloc.dart`:
- Around line 73-76: When AuthHelper.refreshTokenIfNeeded() throws, clear any
persisted auth state before emitting GuestUser() to avoid repeated refresh
failures with stale tokens; update the catch block in auth_bloc.dart to call the
project's auth-clear routine (e.g., AuthHelper.clearAuthData() or
AuthRepository.clearAuthData()/clear()) to remove stored tokens and user info,
handle/ignore any error from that clear operation, then emit(GuestUser()).

In `@src/mobile/lib/src/app/dashboard/pages/dashboard_page.dart`:
- Around line 41-43: The initState in DashboardPage is dispatching LoadUser()
redundantly: locate the initState where you call context.read<AuthBloc>().state
and context.read<UserBloc>().add(LoadUser()) and remove that dispatch so
LoadUser is only triggered by Decider on AuthLoaded; if you need a safety guard,
replace the direct add with a no-op or a conditional that checks a
UserBloc-specific flag (e.g., userLoaded boolean/event) before calling add to
avoid duplicating Decider-triggered loads.

---

Outside diff comments:
In `@src/mobile/lib/src/app/auth/pages/password_reset/password_reset.dart`:
- Around line 27-38: The TextEditingController fields passwordConfirmController
and passwordController are being instantiated at field declaration and then
reassigned in initState, leaking resources; remove the field-level initializers
so the controllers are only created inside initState, and implement dispose to
call passwordConfirmController.dispose() and passwordController.dispose();
update the class methods (initState and add a dispose override) to ensure
controllers are created once and properly disposed to prevent leaks.

---

Nitpick comments:
In `@src/mobile/test/app/shared/services/cache_manager_test.dart`:
- Around line 104-107: The test currently uses a lax converter `(json) => json`
when calling `CachedData<Map<String, dynamic>>.fromJson`, which fails to enforce
the expected Map<String, dynamic> shape; replace the converter with an explicit
cast such as `(json) => Map<String, dynamic>.from(json as Map)` or `(json) =>
(json as Map).cast<String, dynamic>()` so the deserializer for
`CachedData.fromJson` is validated against a proper Map<String, dynamic>.

In `@src/mobile/test/widget_test.dart`:
- Line 151: The assertion using expect(find.byType(MaterialApp), findsWidgets)
is too loose; update the test in widget_test.dart to assert exactly one
MaterialApp by replacing findsWidgets with findsOneWidget so the test fails if
multiple MaterialApp roots are present (locate the expect call using
find.byType(MaterialApp) and change the matcher to findsOneWidget).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7786ec9a-104a-4412-802a-840de87a72ee

📥 Commits

Reviewing files that changed from the base of the PR and between 5f1bf40 and 23f2217.

📒 Files selected for processing (11)
  • src/mobile/lib/main.dart
  • src/mobile/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart
  • src/mobile/lib/src/app/auth/bloc/auth_bloc.dart
  • src/mobile/lib/src/app/auth/pages/password_reset/password_reset.dart
  • src/mobile/lib/src/app/auth/pages/password_reset/reset_link_sent.dart
  • src/mobile/lib/src/app/auth/repository/auth_repository.dart
  • src/mobile/lib/src/app/auth/repository/auth_repository_impl.dart
  • src/mobile/lib/src/app/dashboard/pages/dashboard_page.dart
  • src/mobile/test/app/dashboard/widgets/nearby_view_test.dart
  • src/mobile/test/app/shared/services/cache_manager_test.dart
  • src/mobile/test/widget_test.dart

Comment thread src/mobile/lib/src/app/auth/bloc/auth_bloc.dart
Comment thread src/mobile/lib/src/app/dashboard/pages/dashboard_page.dart Outdated
@Mozart299
Mozart299 requested a review from Baalmart May 15, 2026 06:27
@Baalmart
Baalmart merged commit c295d3e into staging May 15, 2026
18 of 20 checks passed
@Baalmart
Baalmart deleted the fix/guest-reset-flow branch May 15, 2026 07:31
@Baalmart Baalmart mentioned this pull request May 15, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants