Skip to content

feat email verification flow#382

Open
saurabhhhcodes wants to merge 1 commit into
Kuldeeep18:mainfrom
saurabhhhcodes:fix/email-verification-flow
Open

feat email verification flow#382
saurabhhhcodes wants to merge 1 commit into
Kuldeeep18:mainfrom
saurabhhhcodes:fix/email-verification-flow

Conversation

@saurabhhhcodes

@saurabhhhcodes saurabhhhcodes commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Added is_email_verified to the user model and blocked JWT auth until the email is verified.
  • Added a signed email verification link that is sent on registration.
  • Added a /auth/verify-email/<token>/ endpoint and a frontend verification page.
  • Updated registration to send users to the verification page instead of logging them in immediately.
  • Added tests for registration, verification, and token gating.

Why

  • New accounts should not access the API until the email address is confirmed.

Validation

  • python3 backend/manage.py test users -v 2
  • python3 -m py_compile backend/users/models.py backend/users/views.py backend/users/serializers.py backend/users/jwt.py backend/users/authentication.py backend/users/permissions.py backend/users/tests.py backend/backend/settings.py
  • git diff --check

Closes #335

Summary by CodeRabbit

  • New Features

    • Email verification is now required before accessing the API
    • Registration sends a verification email; users must verify via link before gaining full access
    • Added email verification page for managing verification workflow
  • Tests

    • Added coverage for email verification authentication flow
  • Chores

    • Database migrations to support email verification

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an end-to-end email verification flow: a new is_email_verified boolean field on the User model, signed-token generation on registration, a verify-email endpoint that marks users verified, JWT and permission guards that block unverified users from obtaining tokens or accessing the API, and a new email-verification.html frontend page.

Changes

Email Verification Flow

Layer / File(s) Summary
User model field and migrations
backend/users/models.py, backend/users/migrations/0002_user_is_email_verified.py, backend/users/migrations/0003_merge_*.py, backend/campaigns/migrations/0010_merge_*.py
Adds is_email_verified = BooleanField(default=False) to User, introduces the migration that creates it, and two merge migrations resolving parallel migration history branches in users and campaigns.
JWT and permission enforcement guards
backend/users/authentication.py, backend/users/jwt.py, backend/users/permissions.py, backend/users/serializers.py, backend/backend/settings.py
EmailVerifiedJWTAuthentication raises AuthenticationFailed in get_user for unverified users; CustomTokenObtainSerializer.validate raises ValidationError at token issuance; IsEmailVerified DRF permission class added; UserSerializer exposes is_email_verified; RegisterSerializer.create sets it to False explicitly; settings wires the new auth class as the default.
Registration rewire and verify-email endpoint
backend/users/views.py
Adds signed-token generation, frontend URL construction from FRONTEND_BASE_URL, and send_verification_email with exception logging. AuthViewSet.register now sends the verification email and returns verification_url + activation message instead of JWT tokens. New AuthViewSet.verify_email decodes/validates the signed token with expiry, sets is_email_verified=True, and returns a success payload; bad tokens return HTTP 400.
Backend tests
backend/users/tests.py
Four new RegisterViewTests: registration sends email and omits JWT; verify-email endpoint marks user verified; unverified token obtain returns 400; verified token obtain returns access+refresh.
Frontend verification page and API wiring
frontend/api.js, frontend/email-verification.html, frontend/register.html
register() no longer sets tokens, returns JSON instead. New verifyEmail(token) GETs the verify endpoint and throws on failure. New email-verification.html shows spinner, calls verifyEmail, and displays success/failure UI. register.html redirects to email-verification.html?email=<encoded> on success.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant register.html
  participant api.js
  participant AuthViewSet
  participant EmailProvider
  participant email-verification.html
  participant verify_email_endpoint

  User->>register.html: Submit registration form
  register.html->>api.js: register(email, password, ...)
  api.js->>AuthViewSet: POST /api/v1/auth/register/
  AuthViewSet->>AuthViewSet: Create User(is_email_verified=False)
  AuthViewSet->>EmailProvider: send_mail(verification_url with signed token)
  AuthViewSet-->>api.js: 201 {message, verification_url}
  api.js-->>register.html: response JSON
  register.html->>email-verification.html: redirect ?email=<encoded>

  User->>email-verification.html: Clicks link from email (?token=<signed>)
  email-verification.html->>api.js: verifyEmail(token)
  api.js->>verify_email_endpoint: GET /api/v1/auth/verify-email/<token>/
  alt valid token
    verify_email_endpoint->>verify_email_endpoint: user.is_email_verified = True
    verify_email_endpoint-->>api.js: 200 {detail, user}
    api.js-->>email-verification.html: success result
    email-verification.html-->>User: Show success alert + login link
  else invalid/expired token
    verify_email_endpoint-->>api.js: 400 {detail}
    api.js-->>email-verification.html: throws error
    email-verification.html-->>User: Show failure alert + register link
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Hoppy news from the warren today,
No unverified emails shall pass our way!
A signed token sent, a link in your mail,
Click to confirm — you'll never fail.
The bunny guards the gate with glee,
✉️ Verified users only — that's the decree!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat email verification flow' accurately summarizes the main change: implementing an email verification feature for user registration.
Linked Issues check ✅ Passed The PR successfully implements all objectives from issue #335: adds is_email_verified field to User model, sends verification emails with signed tokens, creates verify-email endpoint, restricts API access for unverified users, includes email-verification.html frontend page, and adds comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are directly related to the email verification flow implementation. Changes to authentication settings, JWT serialization, permissions, and frontend components all support the core verification feature.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 8

🧹 Nitpick comments (2)
backend/users/tests.py (1)

47-54: ⚡ Quick win

Assert the verification token is not returned in registration JSON.

This test guards “no JWTs,” but the stronger ownership-proof invariant is that the verification URL/token appears only in the email body. Add an assertion so the regression in the registration response cannot come back.

🧪 Proposed test assertion
         self.assertIn('message', response.data)
         self.assertNotIn('access', response.data)
         self.assertNotIn('refresh', response.data)
+        self.assertNotIn('verification_url', response.data)
         user = User.objects.get(email='new@example.com')
🤖 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 `@backend/users/tests.py` around lines 47 - 54, Add an assertion to verify that
the verification token or URL is not exposed in the registration response JSON.
After checking that the token appears in the email body via
mock_send_mail.call_args, add an assertNotIn statement to ensure the token or
verification URL pattern (such as the email-verification token string) does not
appear in response.data, similar to how the test already checks that 'access'
and 'refresh' tokens are not in the response data.
backend/users/serializers.py (1)

14-14: Make is_email_verified read-only in UserSerializer.

This field is security-sensitive and should never be writable at the serializer level. While UserSerializer is currently only used for responses, marking it read-only prevents accidental future misuse and hardens the trust boundary around email verification state.

🔒 Proposed serializer hardening
 class UserSerializer(serializers.ModelSerializer):
     organization = OrganizationSerializer(read_only=True)
     class Meta:
         model = User
-        fields = ['id', 'email', 'role', 'organization', 'is_active', 'is_email_verified']
+        fields = ('id', 'email', 'role', 'organization', 'is_active', 'is_email_verified')
+        read_only_fields = ('is_email_verified',)
🤖 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 `@backend/users/serializers.py` at line 14, In the `UserSerializer` class, the
`is_email_verified` field is listed in the fields tuple but is not marked as
read-only, which could allow it to be writable. Add `is_email_verified` to a
`read_only_fields` tuple within the Meta class of `UserSerializer` to ensure
this security-sensitive field cannot be modified through the serializer,
preventing accidental future misuse.
🤖 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 `@backend/users/authentication.py`:
- Around line 8-9: The authentication check in the conditional at line 8-9 that
validates `is_email_verified` will reject all existing users since the new field
defaults to False and was not backfilled. Create a Django data migration that
updates all existing User records to set `is_email_verified = True` before
deploying this authentication guard. This ensures that existing users are not
locked out when the email verification check is enabled globally. The migration
should run before the authentication code is deployed to production, or
alternatively disable this check temporarily until the migration and any email
verification flow are ready.

In `@backend/users/migrations/0002_user_is_email_verified.py`:
- Around line 11-15: The AddField migration in the migrations file adds
is_email_verified with default=False, which will mark all existing users as
unverified and lock them out of authentication. Add a data migration function to
backfill all existing User records by setting is_email_verified=True for
pre-existing accounts before the AddField operation takes effect. This ensures
current authenticated users retain access when EmailVerifiedJWTAuthentication is
enforced.

In `@backend/users/views.py`:
- Around line 41-45: The send_mail() call failure is being caught and logged but
then the function continues to return verification_url as if the email was
successfully sent. Instead of swallowing the exception, you need to handle email
delivery failures explicitly by either rolling back the user registration,
raising an exception to return a failure response to the client, or queuing the
email for asynchronous retry. Additionally, remove the user.email parameter from
the logger.exception() call to avoid logging sensitive information in the
failure path.
- Around line 73-76: The verify_email endpoint in the UserViewSet currently
exposes sensitive user data via the UserSerializer in the response, which is a
security concern since this is an AllowAny endpoint with only a bearer secret
for protection. Remove the 'user': UserSerializer(user).data line from the
Response in verify_email and return only the 'detail' message confirming
successful verification, as the client only needs a success or failure status
from this public endpoint.
- Around line 53-58: Remove the verification_url from the Response dictionary in
the registration endpoint. The send_verification_email function should continue
to be called to deliver the link via email, but the Response object should only
include the user data and success message without exposing the verification_url,
as returning the signed token in the API response defeats the purpose of
email-based verification and creates a security risk where users could verify
email addresses they do not own.

In `@frontend/api.js`:
- Around line 149-150: The registration response is exposing the
verification_url field which contains a signed verification token, allowing
users to verify their account without accessing their email. Although this code
in frontend/api.js at line 149 is just returning the backend response as-is, the
actual fix must be implemented on the backend server: modify the registration
endpoint to exclude the verification_url field from public registration
responses, or alternatively gate this field to non-production test-only paths
only. Do not return the verification_url in production registration API
responses.
- Around line 148-149: The registration error handler at the if (!res.ok) check
throws a generic "Registration failed" error message instead of propagating the
actual backend validation errors. Instead of throwing a fixed error message,
parse the response body to extract field-level validation errors returned by the
backend, and throw an error object that includes these details so that
register.html can display specific validation feedback for each field. This
requires awaiting the response JSON before throwing the error to include the
actual error information in the thrown error.

In `@frontend/register.html`:
- Line 195: The email address is being exposed as PII in the URL query parameter
on the window.location.href assignment. Instead of appending the email to the
query string, store the email in sessionStorage before navigation, then redirect
to the email-verification page without the email parameter. Update the
email-verification page to retrieve the email from sessionStorage rather than
parsing it from the URL query parameters. This prevents the sensitive
information from being exposed in browser history, logs, and screenshots.

---

Nitpick comments:
In `@backend/users/serializers.py`:
- Line 14: In the `UserSerializer` class, the `is_email_verified` field is
listed in the fields tuple but is not marked as read-only, which could allow it
to be writable. Add `is_email_verified` to a `read_only_fields` tuple within the
Meta class of `UserSerializer` to ensure this security-sensitive field cannot be
modified through the serializer, preventing accidental future misuse.

In `@backend/users/tests.py`:
- Around line 47-54: Add an assertion to verify that the verification token or
URL is not exposed in the registration response JSON. After checking that the
token appears in the email body via mock_send_mail.call_args, add an assertNotIn
statement to ensure the token or verification URL pattern (such as the
email-verification token string) does not appear in response.data, similar to
how the test already checks that 'access' and 'refresh' tokens are not in the
response data.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 86a475d5-45b4-4eb1-a715-e8556270f9fe

📥 Commits

Reviewing files that changed from the base of the PR and between 0474fe9 and 353ac5b.

📒 Files selected for processing (14)
  • backend/backend/settings.py
  • backend/campaigns/migrations/0010_merge_0009_campaign_cached_counters_0009_campaignlead_bounce_metadata.py
  • backend/users/authentication.py
  • backend/users/jwt.py
  • backend/users/migrations/0002_user_is_email_verified.py
  • backend/users/migrations/0003_merge_0002_user_is_email_verified_0002_user_member_role_default.py
  • backend/users/models.py
  • backend/users/permissions.py
  • backend/users/serializers.py
  • backend/users/tests.py
  • backend/users/views.py
  • frontend/api.js
  • frontend/email-verification.html
  • frontend/register.html

Comment on lines +8 to +9
if not getattr(user, 'is_email_verified', False):
raise AuthenticationFailed('Please verify your email before accessing the API.')

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect user migrations for an is_email_verified backfill for existing rows.
fd '\.py$' backend/users/migrations -x sh -c 'echo "### $1"; sed -n "1,220p" "$1"' sh {}

Repository: Kuldeeep18/LeadOrbit

Length of output: 4952


Backfill existing users before enforcing email verification.

With is_email_verified defaulting to False and this authentication class configured globally, existing users will be rejected from JWT-protected API endpoints. The migration that adds is_email_verified does not backfill existing rows—it only applies the default to newly created users. Add a data migration to mark existing users as verified, or implement and ship a resend/verification flow before enabling this guard.

🤖 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 `@backend/users/authentication.py` around lines 8 - 9, The authentication check
in the conditional at line 8-9 that validates `is_email_verified` will reject
all existing users since the new field defaults to False and was not backfilled.
Create a Django data migration that updates all existing User records to set
`is_email_verified = True` before deploying this authentication guard. This
ensures that existing users are not locked out when the email verification check
is enabled globally. The migration should run before the authentication code is
deployed to production, or alternatively disable this check temporarily until
the migration and any email verification flow are ready.

Comment on lines +11 to +15
migrations.AddField(
model_name='user',
name='is_email_verified',
field=models.BooleanField(default=False),
),

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 | ⚡ Quick win

Backfill existing users before enforcing email-verified auth.

Adding is_email_verified with default=False without a data backfill will mark pre-existing accounts unverified, and they will be denied auth once the default authentication class is switched to EmailVerifiedJWTAuthentication (backend/backend/settings.py Line 135).

💡 Suggested migration adjustment
 from django.db import migrations, models
 
 
+def mark_existing_users_verified(apps, schema_editor):
+    User = apps.get_model('users', 'User')
+    User.objects.filter(is_email_verified=False).update(is_email_verified=True)
+
+
 class Migration(migrations.Migration):
@@
     operations = [
         migrations.AddField(
             model_name='user',
             name='is_email_verified',
             field=models.BooleanField(default=False),
         ),
+        migrations.RunPython(
+            mark_existing_users_verified,
+            migrations.RunPython.noop,
+        ),
     ]
🤖 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 `@backend/users/migrations/0002_user_is_email_verified.py` around lines 11 -
15, The AddField migration in the migrations file adds is_email_verified with
default=False, which will mark all existing users as unverified and lock them
out of authentication. Add a data migration function to backfill all existing
User records by setting is_email_verified=True for pre-existing accounts before
the AddField operation takes effect. This ensures current authenticated users
retain access when EmailVerifiedJWTAuthentication is enforced.

Comment thread backend/users/views.py
Comment on lines +41 to +45
try:
send_mail(subject, message, getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@leadorbit.local'), [user.email], fail_silently=False)
except Exception:
logger.exception('Failed to send verification email for user %s', user.email)
return verification_url

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 | 🏗️ Heavy lift

Do not report success after email delivery fails.

send_mail() failures are logged and swallowed, so registration still returns 201 even when no verification email was delivered. Handle this with rollback, durable queuing plus retry/resend, or an explicit failure response; also avoid logging the raw email address in the failure path.

🤖 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 `@backend/users/views.py` around lines 41 - 45, The send_mail() call failure is
being caught and logged but then the function continues to return
verification_url as if the email was successfully sent. Instead of swallowing
the exception, you need to handle email delivery failures explicitly by either
rolling back the user registration, raising an exception to return a failure
response to the client, or queuing the email for asynchronous retry.
Additionally, remove the user.email parameter from the logger.exception() call
to avoid logging sensitive information in the failure path.

Comment thread backend/users/views.py
Comment on lines +53 to 58
verification_url = send_verification_email(user)
return Response({
'user': UserSerializer(user).data,
'refresh': str(refresh),
'access': str(refresh.access_token),
'message': 'Verification email sent. Please check your inbox to activate your account.',
'verification_url': verification_url,
}, status=status.HTTP_201_CREATED)

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 | ⚡ Quick win

Do not return the verification URL from registration.

Returning verification_url gives the requester the signed token immediately, so they can verify an address they do not own without opening that mailbox. The verification link must be delivered only through email.

🔒 Keep the verification token email-only
-            verification_url = send_verification_email(user)
+            send_verification_email(user)
             return Response({
                 'user': UserSerializer(user).data,
                 'message': 'Verification email sent. Please check your inbox to activate your account.',
-                'verification_url': verification_url,
             }, status=status.HTTP_201_CREATED)
🤖 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 `@backend/users/views.py` around lines 53 - 58, Remove the verification_url
from the Response dictionary in the registration endpoint. The
send_verification_email function should continue to be called to deliver the
link via email, but the Response object should only include the user data and
success message without exposing the verification_url, as returning the signed
token in the API response defeats the purpose of email-based verification and
creates a security risk where users could verify email addresses they do not
own.

Comment thread backend/users/views.py
Comment on lines +73 to +76
return Response({
'detail': 'Email verified successfully.',
'user': UserSerializer(user).data,
})

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 | ⚡ Quick win

Return only verification status from this public endpoint.

verify_email is AllowAny, and the verification link is a bearer secret; anyone with the link can receive UserSerializer data. The client only needs success/failure here, so avoid exposing user and organization metadata.

🔒 Minimize the public verification response
         return Response({
             'detail': 'Email verified successfully.',
-            'user': UserSerializer(user).data,
         })
📝 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
return Response({
'detail': 'Email verified successfully.',
'user': UserSerializer(user).data,
})
return Response({
'detail': 'Email verified successfully.',
})
🤖 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 `@backend/users/views.py` around lines 73 - 76, The verify_email endpoint in
the UserViewSet currently exposes sensitive user data via the UserSerializer in
the response, which is a security concern since this is an AllowAny endpoint
with only a bearer secret for protection. Remove the 'user':
UserSerializer(user).data line from the Response in verify_email and return only
the 'detail' message confirming successful verification, as the client only
needs a success or failure status from this public endpoint.

Comment thread frontend/api.js
Comment on lines 148 to +149
if (!res.ok) throw new Error("Registration failed");
const data = await res.json();
setTokens(data.access, data.refresh);
return await res.json();

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 | ⚡ Quick win

Propagate backend validation errors during registration.

Line 148 throws a fixed message, so the updated register.html alert path cannot show field-level serializer errors from /auth/register/ responses.

Suggested patch
 export const register = async (userData) => {
     const res = await fetch(`${API_BASE}/auth/register/`, {
         method: 'POST',
         headers: { 'Content-Type': 'application/json' },
         body: JSON.stringify(userData)
     });
-    if (!res.ok) throw new Error("Registration failed");
-    return await res.json();
+    const data = await res.json().catch(() => ({}));
+    if (!res.ok) {
+        const firstFieldError = Object.values(data)?.[0]?.[0];
+        throw new Error(data.detail || firstFieldError || 'Registration failed. Please try again.');
+    }
+    return data;
 };
📝 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
if (!res.ok) throw new Error("Registration failed");
const data = await res.json();
setTokens(data.access, data.refresh);
return await res.json();
export const register = async (userData) => {
const res = await fetch(`${API_BASE}/auth/register/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
const firstFieldError = Object.values(data)?.[0]?.[0];
throw new Error(data.detail || firstFieldError || 'Registration failed. Please try again.');
}
return data;
};
🤖 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 `@frontend/api.js` around lines 148 - 149, The registration error handler at
the if (!res.ok) check throws a generic "Registration failed" error message
instead of propagating the actual backend validation errors. Instead of throwing
a fixed error message, parse the response body to extract field-level validation
errors returned by the backend, and throw an error object that includes these
details so that register.html can display specific validation feedback for each
field. This requires awaiting the response JSON before throwing the error to
include the actual error information in the thrown error.

Comment thread frontend/api.js
Comment on lines +149 to +150
return await res.json();
};

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 | ⚡ Quick win

Remove verification tokens from registration responses (email verification bypass).

Line 149 returns backend payload as-is, and the backend contract currently includes verification_url. That exposes the signed verification token to the same client that just registered, allowing account verification without inbox access. This defeats the control this PR is introducing.

Fix must be server-side: do not return verification_url on public registration responses (or gate it to non-production test-only paths).

🤖 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 `@frontend/api.js` around lines 149 - 150, The registration response is
exposing the verification_url field which contains a signed verification token,
allowing users to verify their account without accessing their email. Although
this code in frontend/api.js at line 149 is just returning the backend response
as-is, the actual fix must be implemented on the backend server: modify the
registration endpoint to exclude the verification_url field from public
registration responses, or alternatively gate this field to non-production
test-only paths only. Do not return the verification_url in production
registration API responses.

Comment thread frontend/register.html
});

window.location.href = '/dashboard.html';
window.location.href = `/email-verification.html?email=${encodeURIComponent(email)}`;

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 | ⚡ Quick win

Avoid putting user email addresses in query parameters.

Line 195 appends the raw email into the URL. That creates unnecessary PII exposure via browser history, logs, and shared screenshots. Pass this through sessionStorage (or render a generic message without echoing the email) instead of query params.

Suggested patch
- window.location.href = `/email-verification.html?email=${encodeURIComponent(email)}`;
+ sessionStorage.setItem('pending_verification_email', email);
+ window.location.href = '/email-verification.html';
🤖 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 `@frontend/register.html` at line 195, The email address is being exposed as
PII in the URL query parameter on the window.location.href assignment. Instead
of appending the email to the query string, store the email in sessionStorage
before navigation, then redirect to the email-verification page without the
email parameter. Update the email-verification page to retrieve the email from
sessionStorage rather than parsing it from the URL query parameters. This
prevents the sensitive information from being exposed in browser history, logs,
and screenshots.

@Kuldeeep18

Copy link
Copy Markdown
Owner

Hi @saurabhhhcodes 👋

LeadOrbit Bot here 🤖

A repository star ⭐ is required for PR review and merge.

We noticed that you haven't starred the repository yet. Please star the repo and reply once completed so we can continue with the review process.

Thanks for contributing to LeadOrbit! 🚀

@Kuldeeep18

Copy link
Copy Markdown
Owner

Hi @saurabhhhcodes 👋

LeadOrbit Bot here 🤖

We noticed you've opened a Pull Request but haven't starred the repository yet.

Starring the repository is mandatory for PR review and merge.

Please:

  1. Star the repository.
  2. Reply to this comment with "Done".

Once you've done that, the bot will continue processing your PR.

Note: PRs from contributors who haven't starred the repository will remain pending until this requirement is completed.

Thanks for contributing to LeadOrbit! 🚀

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.

LO-087 [Hard - Feature]: Add Email Verification Flow on User Registration

2 participants