Skip to content

Sessions screen silently loads only the first 50 sessions and ignores the API's pagination #220

Description

@Bccorb

Summary

GET /admin/sessions is paginated with a default limit of 50, and useSessions() calls it with no parameters. Every figure and every row on the Sessions screen therefore describes at most the first 50 sessions, and the remainder are unreachable. Nothing on the screen says so.

Verified against the API

From openapi.json in seamless-auth-api at 4bbed05:

GET /admin/sessions
    limit   { type: number, minimum: 1, maximum: 100, default: 50 }
    offset  { type: number, nullable: true, minimum: 0, default: 0 }

Response is { sessions, total }, both required, where total is the count of all sessions rather than the returned page.

Where

src/hooks/useSessions.ts

queryFn: () => apiFetch<SessionListResponse>("/admin/sessions"),

No limit, no offset, so the server applies its default of 50.

src/pages/Sessions.tsx then treats that page as the whole set:

  • const sessions = data?.sessions ?? [] (at most 50 rows)
  • activeCount and expiredCount are derived from sessions
  • filteredSessions searches and filters within sessions
  • the table is given total={filteredSessions.length} and paginated client-side with limit = 10
  • the CSV export writes filteredSessions

data.total is never read.

Impact

On any deployment with more than 50 sessions:

  • Active Sessions is wrong. It counts the returned page, so it plateaus at 50 no matter how many sessions exist, while presenting as the deployment's session count.
  • Distinct IPs and the activity filter counts are wrong for the same reason.
  • Sessions past the first 50 cannot be reached. The table's pager walks the loaded 50 in pages of 10 and stops. There is no control that fetches more.
  • Search misses. Typing an IP that exists on session 51 returns nothing, indistinguishable from that IP not existing.
  • The export is truncated with no indication.
  • The screen looks internally consistent throughout, which is what makes this bad: five pages of ten rows and a total of 50 reads as a complete picture.

This is the Sessions half of #146 ("reported total is not used for counts", Sessions.tsx:97). #215 fixed the Security half of that issue and corrected the active-session derivation from #164, but both changes still operate on the capped page, so neither surfaced this.

Expected

Push pagination to the server, the way Events already does with /admin/auth-events:

  • useSessions({ limit, offset }), with the page size in the query key
  • feed data.total to Table's total prop and let onPageChange drive offset
  • derive the header counts from data.total where the figure is meant to describe the deployment, and label anything that genuinely describes the current page as page-scoped, matching the treatment the users directory got in fix: repair the user directory, detail screen, and user dialogs #210
  • decide what search and the activity filter mean once rows are server-paged. /admin/sessions has no search or filter parameter, so either they become explicitly page-local and are labelled that way, or they need API support (see Organizations cannot be deleted, and the list cannot be paginated or searched server-side #219 for the same question on organizations)
  • make the CSV export state that it covers the loaded page, or have it fetch the full set before writing

Note on the expired-session count

#164 excluded expired sessions from the active count. That logic stays correct, but its input is the capped page, so the resulting number is still bounded by 50 until this is fixed. Worth re-checking that hint text once pagination is real.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions