Skip to content

fix(auth): default HTTP_USER_AGENT to empty string in save_user_data - #9700

Open
jadhavgaurav wants to merge 1 commit into
makeplane:previewfrom
jadhavgaurav:fix/signin-missing-user-agent
Open

fix(auth): default HTTP_USER_AGENT to empty string in save_user_data#9700
jadhavgaurav wants to merge 1 commit into
makeplane:previewfrom
jadhavgaurav:fix/signin-missing-user-agent

Conversation

@jadhavgaurav

@jadhavgaurav jadhavgaurav commented Aug 27, 2026

Copy link
Copy Markdown

Description

Fixes #9672. POST /auth/sign-in/ (email/password) returns an unhandled
HTTP 500 when the request has no User-Agent header, after credentials
are verified successfully. Any programmatic client that omits the header
(for example Rust's reqwest, which sends no User-Agent by default)
cannot sign in even with correct credentials.

Root cause: Adapter.save_user_data() in
apps/api/plane/authentication/adapter/base.py reads the header with no
default:

user.last_login_uagent = self.request.META.get("HTTP_USER_AGENT")

When the header is absent this returns None. last_login_uagent is
models.TextField(blank=True) - blank=True but not null=True, so the
column is NOT NULL. Saving None raises an IntegrityError, which is
not an AuthenticationException, so it isn't caught by the sign-in view's
error handling and Django returns a generic 500 instead of completing the
sign-in.

The sibling login code path already handles this correctly, which is the
inconsistency this PR resolves:

# apps/api/plane/authentication/utils/login.py
"user_agent": request.META.get("HTTP_USER_AGENT", ""),

Fix

One-line change, defaulting the header to "" in save_user_data() to
match the existing pattern in login.py:

user.last_login_uagent = self.request.META.get("HTTP_USER_AGENT", "")

Tests

Added test_user_login_without_user_agent_header to TestSignInEndpoint
in apps/api/plane/tests/contract/app/test_authentication.py. It signs in
with a plain Client() (no User-Agent header, unlike the module's
django_client fixture which always sets one), and asserts a 302 redirect
(successful sign-in) instead of a 500, plus that last_login_uagent is
saved as "".

Verified the test fails against the pre-fix code: reverted the one-line
fix locally and reran the new test, which failed with exactly the
reported error -
django.db.utils.IntegrityError: null value in column "last_login_uagent" of relation "users" violates not-null constraint - reproducing the bug
end-to-end. Re-applied the fix and the test passes.

Ran the full TestSignInEndpoint class (7 tests) and the full
test_authentication.py file locally against Postgres + Redis:

  • All 7 TestSignInEndpoint tests pass, including the new regression test.
  • 12 pre-existing failures in TestMagicLinkGenerate /
    TestMagicSignIn / TestMagicSignUp / verify-attempt classes, all with
    SMTP_NOT_CONFIGURED (error code 5025) - this local environment has no
    SMTP configured. Confirmed these are unrelated to this change and
    pre-existing on preview before this diff (same failures reproduce with
    the fix fully reverted).
  • ruff check clean on both changed files.
  • ruff format --check on both files flags only pre-existing, unrelated
    formatting drift elsewhere in each file (confirmed identical against
    preview before this change) - none of it touches the lines this PR
    changes.

AI assistance disclosure

This fix was implemented with Claude Code assistance (code, tests, and PR
description), reviewed and verified locally before submission, per this
repo's own convention of Co-authored-by: Claude <noreply@anthropic.com>
trailers on AI-assisted commits.

Summary by CodeRabbit

  • Bug Fixes
    • Sign-in now succeeds when the browser does not provide a User-Agent header.
    • Prevented server errors during login caused by missing User-Agent information.
    • Missing User-Agent data is safely recorded as an empty value.
save_user_data() read HTTP_USER_AGENT with no default, so a request with
no User-Agent header (e.g. Rust's reqwest sends none by default) stored
None into the non-nullable last_login_uagent column. Django raised an
IntegrityError, which is not an AuthenticationException, so it wasn't
caught by the sign-in view and surfaced as an unhandled HTTP 500 even
with correct credentials.

The sibling login path (authentication/utils/login.py) already defaults
this header to "" - this brings save_user_data() in line with that
existing pattern.

Fixes makeplane#9672.

Co-authored-by: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f8f8f2e-aafd-4430-be50-20527d8734b6

📥 Commits

Reviewing files that changed from the base of the PR and between ddac107 and 51e137b.

📒 Files selected for processing (2)
  • apps/api/plane/authentication/adapter/base.py
  • apps/api/plane/tests/contract/app/test_authentication.py

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The sign-in authentication adapter now stores an empty string when the User-Agent header is absent. A regression test verifies that sign-in succeeds and persists the expected value.

Changes

Sign-in User-Agent handling

Layer / File(s) Summary
Persist a valid User-Agent value
apps/api/plane/authentication/adapter/base.py, apps/api/plane/tests/contract/app/test_authentication.py
save_user_data uses an empty-string fallback for a missing User-Agent header. The regression test verifies a successful redirect and an empty persisted value.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 51e13

The change allows valid sign-ins without a User-Agent header to complete successfully while preserving existing authentication behavior. No actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: dheeru0198

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. 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 and concisely identifies the primary fix: defaulting a missing HTTP_USER_AGENT value in save_user_data.
Description check ✅ Passed The description provides the root cause, fix, regression test, validation results, linked issue reference, and unrelated test failure details. It does not reproduce every template heading or mark the …
Linked Issues check ✅ Passed The changes directly satisfy issue [#9672] by storing an empty string when HTTP_USER_AGENT is missing and adding a regression test that verifies sign-in succeeds without the header.
Out of Scope Changes check ✅ Passed The pull request contains only the required one-line fix and a focused regression test. The changes are directly related to issue [#9672].
Full details: Description check

Explanation

The description provides the root cause, fix, regression test, validation results, linked issue reference, and unrelated test failure details. It does not reproduce every template heading or mark the Bug fix checkbox, but the required information is present.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant