Skip to content

Account activation fixes#4270

Merged
hlbmtc merged 3 commits intomainfrom
fix/account-confirmation-link-reactivation
Feb 6, 2026
Merged

Account activation fixes#4270
hlbmtc merged 3 commits intomainfrom
fix/account-confirmation-link-reactivation

Conversation

@hlbmtc
Copy link
Contributor

@hlbmtc hlbmtc commented Feb 6, 2026

  • Adjusted account activation flow: more strict rules for account activation
  • Updated get_tokens_for_user function to bump last_login field

Summary by CodeRabbit

  • Bug Fixes

    • Improved activation token validation to prevent invalid or expired tokens from proceeding and to surface a clear validation error.
    • Ensure last-login timestamp is recorded when issuing authentication tokens.
  • Refactor

    • Simplified activation-eligibility checks during login by centralizing the decision, reducing duplicated conditions and clarifying control flow.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Centralized activation eligibility into a new User.check_can_activate() method; updated authentication service and view logic to delegate activation checks to it, added an upfront token validity check in the service, and updated last_login handling before token generation. A new unit test was added for invalid token on active user.

Changes

Cohort / File(s) Summary
User model
users/models.py
Added check_can_activate() public method (inactive, no last_login, not spam). Reordered ApiKey import.
Auth service
authentication/services.py
Perform upfront token validity check using default_token_generator.check_token; raise on invalid. Enforce user.check_can_activate() guard in activation flow. Set last_login = timezone.now() and save before generating tokens.
Auth view
authentication/views/common.py
Simplified activation eligibility check to use user.check_can_activate() and retained password check; activation email and inactive validations aligned with new condition.
Tests
tests/unit/test_auth/test_services.py
Added test_check_and_activate_user__active_user_invalid_token to assert invalid token raises ValidationError for already-active user.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant View as AuthView
participant Service as AuthService
participant UserModel as User
participant TokenGen as TokenGenerator
Client->>AuthView: POST /login or /activate (credentials/token)
AuthView->>AuthService: validate credentials / request activation
AuthService->>User: load user, check_can_activate()
alt token-based activation
AuthService->>TokenGenerator: check_token(token)
TokenGenerator-->>AuthService: valid/invalid
AuthService->>User: set is_active / last_login; save
AuthService-->>AuthView: activation success
else login/token generation
AuthService->>User: set last_login; save
AuthService->>TokenGenerator: create tokens
TokenGenerator-->>AuthService: tokens
AuthService-->>AuthView: return tokens
end
AuthView-->>Client: response (tokens / activation result)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇✨ I nibble bugs and stitch a single rule,
One method now guards the activation pool.
Tokens checked early, last_login set just so,
A tidy hop-forward — off I go! 🥕

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Account activation fixes' directly relates to the main changes: stricter account activation rules, a new check_can_activate() method, and updates to the activation flow.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/account-confirmation-link-reactivation

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

🤖 Fix all issues with AI agents
In `@authentication/services.py`:
- Around line 138-139: The call that sets user.last_login (user.last_login =
timezone.now(); user.save(update_fields=["last_login"])) affects Django's
default_token_generator and thus invalidates prior activation/reset tokens;
either remove/avoid updating last_login in the token-generation path or
(preferably) add a guard in resend_activation_link_api_view to only send
activation emails to users that pass check_can_activate() so users with an
existing last_login aren't emailed. Locate the token-generation code that sets
last_login and either stop updating last_login there, or modify
resend_activation_link_api_view to call user.check_can_activate() and only send
when it returns True.
🧹 Nitpick comments (1)
authentication/services.py (1)

67-78: Redundant conditions before check_can_activate().

At line 77, check_can_activate() tests not is_active and not last_login and not is_spam. By this point, is_active is guaranteed False (line 71 returns early if active) and is_spam is guaranteed False (line 74 raises). So the only net-new condition added by check_can_activate() is not self.last_login.

This works correctly but is misleading — a reader might think the method performs a comprehensive gate here when most of its conditions are already covered. Consider either:

  • Using check_can_activate() as the sole gate (replacing the individual checks), or
  • Checking last_login directly here instead, for clarity.
Option A: Consolidate into a single gate
     if not user:
         raise ValidationError({"token": ["Invalid user"]})

     if not default_token_generator.check_token(user, token):
         raise ValidationError({"token": ["Activation Token is expired or invalid"]})

-    # Skip if user is already active
-    if user.is_active:
-        return user
-
-    if user.is_spam:
-        raise ValidationError({"user": ["User is marked as spam"]})
-
-    if not user.check_can_activate():
+    if user.is_active:
+        return user
+
+    if not user.check_can_activate():
         raise ValidationError({"user": ["User can't be activated"]})
Option B: Explicit last_login check instead
-    if not user.check_can_activate():
-        raise ValidationError({"user": ["User can't be activated"]})
+    if user.last_login is not None:
+        raise ValidationError({"user": ["User can't be activated"]})

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

🧹 Preview Environment Cleaned Up

The preview environment for this PR has been destroyed.

Resource Status
🌐 Preview App ✅ Deleted
🗄️ PostgreSQL Branch ✅ Deleted
⚡ Redis Database ✅ Deleted
🔧 GitHub Deployments ✅ Removed
📦 Docker Image ⚠️ Retained (auto-cleanup via GHCR policies)

Cleanup triggered by PR close at 2026-02-06T17:49:42Z

@hlbmtc hlbmtc merged commit 746963a into main Feb 6, 2026
8 of 9 checks passed
@hlbmtc hlbmtc deleted the fix/account-confirmation-link-reactivation branch February 6, 2026 17:49
Copy link
Contributor

@lsabor lsabor left a comment

Choose a reason for hiding this comment

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

lgtm

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