Skip to content

Conversation

HarshMN2345
Copy link
Member

@HarshMN2345 HarshMN2345 commented Sep 28, 2025

What does this PR do?

Add email OTP as primary login method with separate verification page,
proper Appwrite SDK integration, and dynamic UI switching.

Test Plan

image image image

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

yes

Summary by CodeRabbit

  • New Features

    • Passwordless sign-in via email one-time code and a dedicated OTP verification page with resend and verify flows.
    • New persistent resend cooldown component for resend behavior.
  • UX Changes

    • Button label adapts to sign-in mode; password is optional; email input no longer autofocus.
  • Reliability

    • Improved notifications, error handling, and safer redirects when parameters are missing.
  • Refactor

    • Simplified resend logic in verification and modal UI by centralizing cooldown behavior.

@appwrite
Copy link

appwrite bot commented Sep 28, 2025

Console

Project ID: 688b7bf400350cbd60e9

Sites (2)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Queued Queued View Logs Preview URL QR Code
 console-cloud
688b7c18002b9b871a8f
Ready Ready View Logs Preview URL QR Code

Tip

Cursor pagination performs better than offset pagination when loading further pages.

@HarshMN2345 HarshMN2345 self-assigned this Sep 28, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 28, 2025

Walkthrough

Adds a passwordless email OTP sign-in flow and a resend cooldown component. The login page toggles between password and email-sign-in modes (password no longer required, email autofocus removed) and can send an email sign-in token. A new email-otp page verifies 6‑digit OTPs, creates console sessions, supports resending tokens, tracks analytics, shows notifications, and redirects based on coupon, campaign, or explicit redirect. A load function fetches coupon/campaign data from URL params. The send-verification modal was refactored to use the new ResendCooldown component with localStorage persistence.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat: Implement email OTP authentication" directly and clearly summarizes the main change in the changeset. The raw summary shows that all file modifications are specifically designed to implement an email OTP authentication system: a new email-otp route with OTP verification, modifications to the login page to support passwordless sign-in via email tokens, a new ResendCooldown component for managing resend timers, and refactoring of existing components to use this new abstraction. The title accurately reflects the primary objective stated in the PR description and is specific, concise, and free from vague or generic language.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-SER-191-Implement-Email-OTP-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
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e3b6c5 and efed2cc.

📒 Files selected for processing (3)
  • src/routes/(public)/(guest)/login/+page.svelte (2 hunks)
  • src/routes/(public)/(guest)/login/email-otp/+page.svelte (1 hunks)
  • src/routes/(public)/(guest)/login/email-otp/+page.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/routes/(public)/(guest)/login/email-otp/+page.ts (1)
src/lib/stores/sdk.ts (1)
  • sdk (147-170)
⏰ 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). (2)
  • GitHub Check: e2e
  • GitHub Check: build

Comment on lines +34 to +35
`${base}/login/email-otp?email=${encodeURIComponent(mail)}&userId=${sessionToken.userId}`
);
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve existing query params when switching to the OTP flow

Right now we build the OTP URL with only email and userId, which strips any pre-existing query params (code, campaign, redirect, etc.). The new loader in email-otp/+page.ts expects those params to rehydrate data, so dropping them prevents us from ever hitting the coupon/campaign apply-credit paths and also loses any redirect target after a successful OTP login. Please carry forward the current search params before adding email/userId.

-            await goto(
-                `${base}/login/email-otp?email=${encodeURIComponent(mail)}&userId=${sessionToken.userId}`
-            );
+            const params = new URLSearchParams(window.location.search);
+            params.set('email', mail);
+            params.set('userId', sessionToken.userId);
+
+            await goto(`${base}/login/email-otp?${params.toString()}`);
📝 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
`${base}/login/email-otp?email=${encodeURIComponent(mail)}&userId=${sessionToken.userId}`
);
const params = new URLSearchParams(window.location.search);
params.set('email', mail);
params.set('userId', sessionToken.userId);
await goto(`${base}/login/email-otp?${params.toString()}`);
🤖 Prompt for AI Agents
In src/routes/(public)/(guest)/login/+page.svelte around lines 34 to 35, the OTP
redirect builds a URL with only email and userId which drops any existing query
params (code, campaign, redirect, etc.); update the URL construction to preserve
current search params by copying the existing location.search (or current
URLSearchParams) into a new URLSearchParams instance, set/overwrite the email
and userId keys, and then append that full query string to
`${base}/login/email-otp` so all original params are carried forward along with
email and userId.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
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: 1

🧹 Nitpick comments (1)
src/lib/components/account/sendVerificationEmailModal.svelte (1)

12-57: Drop the no-op onDestroy.

We still import onDestroy but only call an empty handler, so both lines can go. Keeps the component leaner.

-import { onDestroy } from 'svelte';
...
-onDestroy(() => {});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efed2cc and 32180c5.

📒 Files selected for processing (3)
  • src/lib/components/account/sendVerificationEmailModal.svelte (3 hunks)
  • src/lib/components/resendCooldown.svelte (1 hunks)
  • src/routes/(public)/(guest)/login/email-otp/+page.svelte (1 hunks)
⏰ 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). (2)
  • GitHub Check: build
  • GitHub Check: e2e

localStorage.removeItem(EMAIL_SENT_KEY);
}
});
onDestroy(() => {});
Copy link
Member

Choose a reason for hiding this comment

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

empty?

import { Link } from '@appwrite.io/pink-svelte';
let {
storageKey = 'resend_cooldown_default',
Copy link
Member

Choose a reason for hiding this comment

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

lets not have a default, always mark as required.

Copy link
Member

Choose a reason for hiding this comment

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

svelte5 runes.

try {
disabled = true;
const sessionToken = await sdk.forConsole.account.createEmailToken({
userId: 'unique',
Copy link
Member

Choose a reason for hiding this comment

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

Use ID.unique()

Copy link
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: 0

♻️ Duplicate comments (1)
src/lib/components/resendCooldown.svelte (1)

75-76: Cooldown never starts after initial send.

When the component mounts with no existing timer state (e.g., after sending the initial code), restore() finds no stored deadline and leaves remaining at 0. This allows users to immediately resend, bypassing the throttle entirely.

Apply this diff to start the cooldown on mount when no stored deadline exists:

-    onMount(restore);
+    onMount(() => {
+        restore();
+        if (remaining === 0) {
+            start();
+        }
+    });
🧹 Nitpick comments (2)
src/lib/components/resendCooldown.svelte (1)

9-9: Consider one-way binding for disabled prop.

The component only reads the disabled prop and never modifies it (line 70 checks it, never assigns). Using $bindable creates an unnecessary two-way binding contract. A regular prop would be clearer.

-        disabled = $bindable(false),
+        disabled = false,
src/lib/components/account/sendVerificationEmailModal.svelte (1)

84-84: Consider one-way binding for disabled.

ResendCooldown only reads the disabled prop and never modifies it, so the two-way binding (bind:disabled) is unnecessary. A one-way binding would be clearer.

-                    bind:disabled={creating}
+                    disabled={creating}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32180c5 and fe18032.

📒 Files selected for processing (3)
  • src/lib/components/account/sendVerificationEmailModal.svelte (3 hunks)
  • src/lib/components/resendCooldown.svelte (1 hunks)
  • src/routes/(public)/(guest)/login/email-otp/+page.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/routes/(public)/(guest)/login/email-otp/+page.svelte
🔇 Additional comments (7)
src/lib/components/resendCooldown.svelte (4)

18-28: LGTM.

The start() function correctly persists the deadline to localStorage and initiates both the immediate tick and the interval-based countdown.


30-44: LGTM.

The restore() function correctly handles SSR, expired deadlines, and active timers from localStorage.


69-73: LGTM.

The function correctly starts the cooldown only after onResend succeeds. If onResend throws or rejects, the cooldown won't start, allowing the user to retry immediately.


79-83: LGTM.

The conditional rendering correctly displays the countdown message when active and the resend button when the cooldown expires.

src/lib/components/account/sendVerificationEmailModal.svelte (3)

13-13: LGTM.

Clean import and accurate documentation of the refactoring to use the ResendCooldown component.

Also applies to: 40-40


43-43: LGTM.

Simplifying the guard to only check creating is correct since ResendCooldown now manages the cooldown state and prevents premature resends.


80-90: LGTM overall.

The integration of ResendCooldown successfully replaces the inline timer logic. The conditional rendering, unique storage key, and callback wiring are all correct. Once the auto-start issue in ResendCooldown is fixed, this implementation will properly throttle resend attempts.

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.

2 participants