Skip to content

PREVENT_CONCURRENT_LOGINS evicts shared service-account sessions (xqueue-watcher login storm); no exemption mechanism #38824

Description

@blarghmatey

Summary

When FEATURES['PREVENT_CONCURRENT_LOGINS'] is enabled, any user account that is shared by more than one concurrent client becomes unusable, because every login deletes the account's previously-registered session. There is no way to exempt legitimate shared/service accounts (e.g. the xqueue-watcher grader account), so they fall into a self-sustaining login storm.

Mechanism

enforce_single_login (common/djangoapps/student/models/user.py) runs on every user_logged_in and calls UserProfile.set_login_session():

def set_login_session(self, session_id=None):
    meta = self.get_meta()
    old_login = meta.get('session_id', None)
    if old_login:
        SessionStore(session_key=old_login).delete()   # deletes the prior session
    meta['session_id'] = session_id
    self.set_meta(meta)
    self.save()

The profile holds a single session_id slot per user. For human users this is the intended "one active session" behavior. But when multiple clients authenticate as the same account, each login deletes the other clients' sessions. Each client then gets 401/403 on its next request, re-authenticates, and in doing so evicts another client — a continuous storm.

Real-world impact: xqueue-watcher

xqueue-watcher runs many workers (multiple replicas × multiple queues × CONNECTIONS) that all authenticate as one shared LMS service account. With PREVENT_CONCURRENT_LOGINS on, those workers continuously evict each other's sessions, producing:

  • a login storm (thousands of logins/hour for one account),
  • ~1:1 Forbidden: /xqueue/get_submission/ and /xqueue/put_result/ responses,
  • stalled external grading.

We confirmed this in production and QA via a faithful reproduction: a fresh service-account session authenticates for a few seconds, then its session row is deleted from the session cache by the next login of the same account → 403. (We ruled out cache eviction, cookie TTL, password-hash drift, and SECRET_KEY skew — the deletion is set_login_session.)

Proposed fix

Add an opt-in exemption so specific service/automation accounts can be excluded from single-login enforcement, via two new settings (both default empty, so behavior is unchanged unless configured):

  • SINGLE_LOGIN_EXEMPT_USERNAMES
  • SINGLE_LOGIN_EXEMPT_GROUPS

enforce_single_login returns early for exempt users, leaving PREVENT_CONCURRENT_LOGINS fully in force for everyone else. PR to follow.

Steps to reproduce

  1. Set FEATURES['PREVENT_CONCURRENT_LOGINS'] = True.
  2. Authenticate as the same user from two clients; make an authenticated request from the first.
  3. The first client is logged out (session deleted). Repeat rapidly from N clients → login storm.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    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