Skip to content

fix(web): improve auth and store setup flow#348

Merged
onerandomdevv merged 3 commits into
devfrom
fix/web-auth-store-setup-flow
Jun 2, 2026
Merged

fix(web): improve auth and store setup flow#348
onerandomdevv merged 3 commits into
devfrom
fix/web-auth-store-setup-flow

Conversation

@onerandomdevv
Copy link
Copy Markdown
Collaborator

@onerandomdevv onerandomdevv commented Jun 2, 2026

What does this PR do?

Improves the auth and store setup flow by adding safe redirect handling for login, normalizing Nigerian phone numbers during registration, removing hardcoded demo examples from onboarding/setup copy, and making store setup redirect safely after creation or when an existing store is detected.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Database migration included
  • Chore / maintenance
  • Documentation

Area affected

  • Backend
  • Web
  • WhatsApp
  • Shared package
  • Database / Prisma
  • GitHub / CI / infrastructure

How to test this

  1. Open /login, /register, and /store/setup.
  2. Confirm login defaults to /home when no safe redirect is provided.
  3. Confirm unsafe redirects such as external URLs, //..., and /login?... are rejected.
  4. Confirm Nigerian phone values normalize from 090..., 901..., and +234....
  5. Confirm store setup redirects to a safe target or /store/dashboard.

Expected result: auth/store setup flows work without unsafe redirects, hardcoded demo values, or store setup redirect loops.

Pre-commit checklist

  • Backend lint/type/build pass when backend is affected
  • Web lint/type/build pass when web is affected
  • Shared package build passes when shared is affected
  • No console.log left in production code
  • No secrets or .env files committed
  • No new any types added
  • No non-MVP legacy features reintroduced
  • All money values are BigInt kobo, never float
  • Paystack webhook changes verify HMAC before processing
  • Database migrations are Prisma migrations, not db push
  • Database migrations are backward-compatible or risk is documented

Screenshots

N/A

Notes for reviewer

This PR intentionally keeps frontend registration age validation aligned with the current backend account rule and does not change account registration to 18+. Store creation remains handled separately by the existing store eligibility flow.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Login and store setup now use safer redirect validation instead of relying on backend responses.
  • Improvements

    • Nigerian phone number input now accepts multiple formats.
    • Updated placeholder text across registration and store setup forms for better clarity.
    • Refreshed loading state UI during store verification.
    • Enhanced eligibility messaging for age requirements.

@codesandbox
Copy link
Copy Markdown

codesandbox Bot commented Jun 2, 2026

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

Warning

Review limit reached

@onerandomdevv, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 31 minutes and 27 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6a61ac6f-05ba-47f0-9667-d26418803e16

📥 Commits

Reviewing files that changed from the base of the PR and between e57287e and 5a4f52c.

📒 Files selected for processing (2)
  • apps/web/src/app/(auth)/login/page.tsx
  • apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx
📝 Walkthrough

Walkthrough

Three authentication and setup pages add safe redirect destination validation and input normalization: login sanitizes the redirect query parameter, registration normalizes Nigerian phone formats, and store setup applies both patterns alongside form placeholder standardization and a timeout guard on the existing-store check.

Changes

Secure Redirects and User Input Validation

Layer / File(s) Summary
Login safe redirect handling
apps/web/src/app/(auth)/login/page.tsx
getSafeRedirectDestination validates redirect paths to block protocol hijacking and unsafe routes, then onSubmit uses sanitized query redirect instead of backend fallback.
Register Nigerian phone normalization
apps/web/src/app/(auth)/register/page.tsx
normalizeNigerianPhone converts multiple input formats (local, +234-prefixed) into standard normalized form; step-3 validation schema now uses this helper to accept and transform the input.
Register validation messages and placeholder text
apps/web/src/app/(auth)/register/page.tsx
Step-4 age eligibility error message updated; phone, date-of-birth, display name, and username input placeholders changed from example-based to generic prompts.
Store setup redirect helpers and timeout infrastructure
apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx
getPostSetupRedirect computes safe destination from optional query param, STORE_CHECK_TIMEOUT_MS constant bounds the existing-store check duration, and PageLoadingState component imported for loading views.
Store setup existing store check with abort timeout
apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx
Existing-store status fetch uses AbortController with fixed timeout, clears timeout on completion, navigates via sanitized redirect on success, and replaces skeleton UI with PageLoadingState component.
Store setup post-submit navigation
apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx
Handle preview fallback now returns your_store_name when empty; both success and STORE_ALREADY_EXISTS responses route through getPostSetupRedirect() instead of hardcoded dashboard path.
Store setup form placeholder text
apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx
Store name, handle, address, city, and state input placeholders updated to generic, non-example copy throughout the form.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • coded-devs/twizrr#294: Introduces login/register pages and Nigerian phone validation that this PR directly extends with redirect sanitization and phone normalization improvements.
  • coded-devs/twizrr#293: Middleware that redirects unauthenticated users to /login?redirect=<path> complements the login page's new getSafeRedirectDestination sanitization of that parameter.

Poem

🐰 A hop through safe redirects, fields made plain,
Nigerian numbers now speak one refrain,
Timeouts and guards on each auth stride,
Your store awaits without a misdirected guide!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes across all affected files: improvements to auth flows (login, register) and store setup with better redirect handling and input normalization.
Description check ✅ Passed The PR description comprehensively covers all required template sections: purpose, change type, affected areas, testing steps, pre-commit checklist completion, and notes for reviewers with clear explanations of the changes.
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.

✏️ 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/web-auth-store-setup-flow

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.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (2)
apps/web/src/app/(auth)/login/page.tsx (1)

24-34: ⚡ Quick win

Consider aligning the return-type pattern with store setup for consistency.

The store setup flow uses getSafePostSetupRedirect() which returns a fallback string directly, while this function returns null and relies on nullish coalescing at the call site. Both approaches work, but unifying the pattern would improve consistency.

♻️ Optional refactor to match store setup pattern
-function getSafeRedirectDestination(value: string | null): string | null {
+function getSafeRedirectDestination(value: string | null): string {
   if (!value || !value.startsWith("/") || value.startsWith("//")) {
-    return null;
+    return DEFAULT_LOGIN_DESTINATION;
   }
 
   if (value === "/login" || value.startsWith("/login?")) {
-    return null;
+    return DEFAULT_LOGIN_DESTINATION;
   }
 
   return value;
 }

Then simplify the call site:

-      const destination =
-        getSafeRedirectDestination(searchParams.get("redirect")) ??
-        DEFAULT_LOGIN_DESTINATION;
+      const destination = getSafeRedirectDestination(searchParams.get("redirect"));
🤖 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 `@apps/web/src/app/`(auth)/login/page.tsx around lines 24 - 34, The
getSafeRedirectDestination function currently returns null for unsafe values;
change it to follow the store pattern by returning a fallback string (e.g., "/")
instead of null so its signature becomes string and callers don't have to use
nullish coalescing. Update getSafeRedirectDestination to return the fallback for
all invalid inputs (preserve the existing checks for value,
value.startsWith("/"), "//", and "/login" cases) and remove the ?? fallback at
call sites that use this helper so the call site simply uses
getSafeRedirectDestination(...).
apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx (1)

144-148: 💤 Low value

Abort the controller on cleanup to cancel in-flight requests.

The cancelled flag prevents state updates, but the fetch request continues until completion or timeout. Aborting the controller on unmount frees network resources immediately.

Proposed fix

Move controller outside the async function and abort in cleanup:

   useEffect(() => {
     let cancelled = false;
+    const controller = new AbortController();

     async function checkExistingStore() {
       if (!API_BASE) {
         setCheckError("The API URL is not configured for this environment.");
         setCheckingStore(false);
         return;
       }

-      const controller = new AbortController();
       const timeoutId = window.setTimeout(
         () => controller.abort(),
         STORE_CHECK_TIMEOUT_MS,
       );
       // ... rest of function
     }

     checkExistingStore();

     return () => {
       cancelled = true;
+      controller.abort();
     };
   }, [router]);

Also applies to: 187-189

🤖 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 `@apps/web/src/app/`(store)/store/setup/StoreSetupClient.tsx around lines 144 -
148, The AbortController instance is created inside the async task so it never
gets aborted during component cleanup; move the AbortController creation (const
controller = new AbortController()) and its timeout (window.setTimeout(...,
STORE_CHECK_TIMEOUT_MS)) to the outer scope of the effect (outside the inner
async function) and in the effect cleanup callback call controller.abort() and
clearTimeout(timeoutId) so in-flight fetches started by the fetch that uses
controller.signal are cancelled on unmount; apply the same change to the other
occurrence that creates a controller (the block around lines 187-189) so both
fetch flows cancel properly.
🤖 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 `@apps/web/src/app/`(auth)/login/page.tsx:
- Line 51: The call to api.post in apps/web/src/app/(auth)/login/page.tsx
includes an unused response type annotation { redirectTo: string }; remove this
unused generic from the api.post call (or replace it with a more accurate/empty
type like void or unknown) so the code no longer claims to use redirectTo.
Locate the api.post invocation in the login form submit handler (the call site
shown as await api.post<{ redirectTo: string }>("/auth/login", data)) and update
the generic to reflect that the response is ignored or not relied upon.

In `@apps/web/src/app/`(store)/store/setup/StoreSetupClient.tsx:
- Around line 62-72: getSafePostSetupRedirect currently misses variants like
trailing slash or fragments; update it to parse the input using URL (e.g., new
URL(value, location.origin)) to extract pathname and detect setup routes
robustly: if the pathname equals "/store/setup" or "/store/setup/" (or otherwise
matches the exact setup route) treat it as unsafe and return "/store/dashboard";
keep the existing guard rejecting null, non-leading-slash, and "//" values and
return the original value for all other cases. Use the function name
getSafePostSetupRedirect to locate and modify the logic.

---

Nitpick comments:
In `@apps/web/src/app/`(auth)/login/page.tsx:
- Around line 24-34: The getSafeRedirectDestination function currently returns
null for unsafe values; change it to follow the store pattern by returning a
fallback string (e.g., "/") instead of null so its signature becomes string and
callers don't have to use nullish coalescing. Update getSafeRedirectDestination
to return the fallback for all invalid inputs (preserve the existing checks for
value, value.startsWith("/"), "//", and "/login" cases) and remove the ??
fallback at call sites that use this helper so the call site simply uses
getSafeRedirectDestination(...).

In `@apps/web/src/app/`(store)/store/setup/StoreSetupClient.tsx:
- Around line 144-148: The AbortController instance is created inside the async
task so it never gets aborted during component cleanup; move the AbortController
creation (const controller = new AbortController()) and its timeout
(window.setTimeout(..., STORE_CHECK_TIMEOUT_MS)) to the outer scope of the
effect (outside the inner async function) and in the effect cleanup callback
call controller.abort() and clearTimeout(timeoutId) so in-flight fetches started
by the fetch that uses controller.signal are cancelled on unmount; apply the
same change to the other occurrence that creates a controller (the block around
lines 187-189) so both fetch flows cancel properly.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c2ef920-6738-4a52-b5df-43f55ffa9a76

📥 Commits

Reviewing files that changed from the base of the PR and between 59fecbe and e57287e.

📒 Files selected for processing (3)
  • apps/web/src/app/(auth)/login/page.tsx
  • apps/web/src/app/(auth)/register/page.tsx
  • apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx

Comment thread apps/web/src/app/(auth)/login/page.tsx Outdated
Comment thread apps/web/src/app/(store)/store/setup/StoreSetupClient.tsx
@onerandomdevv onerandomdevv merged commit 0ce7c98 into dev Jun 2, 2026
8 checks passed
@onerandomdevv onerandomdevv deleted the fix/web-auth-store-setup-flow branch June 2, 2026 03:32
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.

1 participant