Skip to content

[bug]: /auth/sign-in/ returns HTTP 500 when the request has no User-Agent header #9672

Description

@beantownbytes

Describe the bug

POST /auth/sign-in/ (email/password) returns an unhandled HTTP 500 when the
request does not send a User-Agent header. The failure happens after the
credentials are verified, so a client with correct credentials still cannot sign in.
Any programmatic client that omits a User-Agent hits this (for example a CLI/API
consumer; Rust's reqwest sends no User-Agent by default).

Root cause

AuthenticationAdapter.save_user_data() reads the header with no default:

# apps/api/plane/authentication/adapter/base.py
user.last_login_uagent = self.request.META.get("HTTP_USER_AGENT")

When the header is absent, .get("HTTP_USER_AGENT") returns None. The field is
declared:

# apps/api/plane/db/models/user.py
last_login_uagent = models.TextField(blank=True)

blank=True but not null=True, so the column is NOT NULL. Saving None raises an
IntegrityError. It is not an AuthenticationException, so it is not caught by the
sign-in view's handler and Django returns a generic 500.

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

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

To reproduce

Sign in with valid credentials but with the User-Agent header removed:

# 1) get a CSRF token and the csrftoken cookie
curl -s -c cookies.txt http://<plane-host>/auth/get-csrf-token/
#    -> {"csrf_token": "<token>"}

# 2) sign in with NO User-Agent header (curl removes it when the value is empty)
curl -s -o /dev/null -w '%{http_code}\n' -X POST http://<plane-host>/auth/sign-in/ \
  -b cookies.txt -H 'User-Agent:' \
  --data-urlencode 'email=<you@example.com>' \
  --data-urlencode 'password=<password>' \
  --data-urlencode 'csrfmiddlewaretoken=<token>'
#    -> 500

The identical request with any User-Agent header succeeds (302 to the dashboard).

Expected behavior

Sign-in should succeed regardless of whether a User-Agent header is present. A
missing header should be treated as an empty string, not None.

Suggested fix

Default the header to an empty string in save_user_data, matching login.py:

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

(Alternatively make the field null=True, but the empty-string default is the
smaller change and keeps it consistent with the sibling code.)

Environment

  • Plane self-hosted, v1.4.1 (Docker Compose).
  • Reproduced against the plane-api service (makeplane/plane-backend:v1.4.1).
  • Endpoint: the app auth API POST /auth/sign-in/.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions