Active Sessions
The Active Sessions add-on adds a page to the Loggedin admin that lists every user with at least one live WordPress session — one row per user, sortable and searchable — and a per-row modal that shows each of their individual sessions (IP address, device, login time) with sign-out buttons for one session at a time, or all at once.
Where the core plugin's Sessions page needs you to know the user upfront and is all-or-nothing, and the Users screen shows only a count per user, this add-on gives you a discoverable list and per-device control. Useful for membership sites, LMS courses, and any install where an admin needs operational visibility into who is signed in right now.
When active, the add-on adds Manage › Active sessions to Users → Loggedin, right after the core Sessions page (&tab=active-sessions). It also removes the "Want to see who is signed in, or end one device at a time?" line from the core Sessions page.
Version 1.2.0 and later
Active Sessions 1.2.0 is the first version built for the settings screen in Loggedin 3.4.0. Older versions still work: Loggedin moves their page to Manage › Active sessions and lists the add-on in a notice asking you to update it. With Loggedin older than 3.4.0, 1.2.0 keeps its old Sessions tab. The add-on needs WordPress 6.9 or later; if the scripts it depends on are missing, an admin notice says so instead of showing an empty page.
Default session storage only
This add-on reads sessions from WordPress's default WP_User_Meta_Session_Tokens storage backend (the one that ships with core). Sites that replace the session manager via the session_token_manager filter — uncommon, mostly large enterprise hosts that use Redis or a custom store — will see a warning at the top of the page and an empty list. Since 1.2.0 the list stays empty rather than showing rows left in user meta from before the site switched backends, and single sessions can't be signed out. Custom-backend support may come in a future release.
The Active sessions page
The page renders a DataViews table in a Signed-in users card, with one row per user. Each row shows:
| Column | What it shows |
|---|---|
| User | Display name and email (with avatar). Falls back to the username when there's no display name. |
| Role | Translated role labels — comma-separated for multi-role users. |
| Active sessions | Count of live (non-expired) session tokens. |
| Last login | Date / time of the newest active session, in the site's date and time formats (Settings → General). |
| Last IP | IP address recorded against the newest active session. IPv6 addresses are shown in their short form (::1, not 0000:…:0001). |
Username and Email columns are also available, hidden by default (Username since 1.2.0). Turn them on, or hide or reorder any column, from the DataViews View options menu in the top-right of the table. The view (sort, columns, page size, density) is persisted to localStorage under loggedin_active_sessions_view, so your preferences survive a reload. A view saved by an older version is kept as it was, so after updating you may still see Username until you hide it.
Search and sort
Type into the search box at the top of the table to free-text match against username, email, and display name. Sortable columns:
- User (sorts by display name)
- Username
- Active sessions
- Last login
The default sort is Last login, newest first — admins most often arrive on this screen looking for "who's signed in right now," and newest-first puts the freshest activity at the top.
Pagination
The page size selector offers 10 / 25 / 50 / 100. The list endpoint returns X-WP-Total and X-WP-TotalPages headers so DataViews drives the pager directly from the response — no count query running on every page change.
Empty state
If nobody is signed in, the table shows a "No active sessions" empty state — users appear here as soon as they log in. If your search / filters hide every row, the empty state offers a Clear filters button instead. If the list can't be loaded, a The active sessions could not be loaded notice with a Try again button appears above the table.
Viewing one user's sessions
Click the ⋮ menu on any row and pick View sessions. A modal opens listing every active session that user holds, one section per session:
| Field | What it shows |
|---|---|
| Device | The user-agent string WordPress recorded at login. |
| IP address | The IP the session was started from. IPv6 addresses are shown in their short form. |
| Signed in | When the session was created. |
| Expires | When WordPress will stop honouring this token, with a "in X days/hours/minutes" hint. |
Sessions are listed newest-first. Dates use the site's date and time formats, the same as the Last login column.
Sign out a single session
Click Sign out on a session section. The button flips to an inline Yes, sign out / Cancel confirmation — click Yes, sign out to destroy that single session token. The modal refreshes in place; the list, the row's session count, and the row's "Last login" all update on close.
What the user sees
The user's other open tabs keep rendering the cached page until they make their next request — at which point WordPress sees no matching token and redirects them to wp-login. Pair this add-on with Real-time Logout to shrink that window to whatever polling interval you've configured.
Sign out every session for the user
The modal's footer has a Sign out all sessions button. Click it, then confirm — every session token for that user is destroyed in one call, same as Sign out a user on the core Sessions page. The loggedin_destroy_all_sessions action fires on success, so any audit-logging or webhook code you've hooked already covers this path too.
Bulk sign-out from the list
The table supports row selection. Tick one or more rows and use the Sign out all bulk action in the action bar above the list (the same action is in each row's ⋮ menu). A Sign out users confirmation asks "Sign out every session for N users?" (or names the user, when there's only one); click Yes, sign out to confirm.
Behind the scenes:
- Single-user selection → calls
DELETE /loggedin/v1/active-sessions/{user}. - Multi-user selection → calls
DELETE /loggedin/v1/active-sessionswith auser_idsbody.
Both paths fire loggedin_destroy_all_sessions once per user cleared.
REST endpoints
The add-on registers its routes inside the parent plugin's loggedin/v1 namespace, so they share the same manage_options capability gate and the same X-WP-Nonce requirement as every other Loggedin REST endpoint.
GET /loggedin/v1/active-sessions
List users with at least one live session. Paginated; the response carries X-WP-Total and X-WP-TotalPages headers.
| Query param | Type | Default | Notes |
|---|---|---|---|
search | string | empty | Free-text match against login / email / display name. |
orderby | string | last_login | One of user_login, display_name, user_email, session_count, last_login. Anything else is ignored. |
order | string | desc | asc or desc. |
page | integer | 1 | 1-indexed. |
per_page | integer | 25 | 1–100. A value outside that range is rejected. |
Each row carries user_id, user_login, user_email, display_name, roles (translated role names), avatar_url, session_count, last_login, last_ip.
On a site with a custom session storage backend, this returns an empty list with X-WP-Total: 0. Since 1.2.0.
GET /loggedin/v1/active-sessions/{user}
Return every active session for a single user. The response shape:
{
"user": {
"id": 7,
"login": "jdoe",
"display_name": "Jane Doe",
"email": "jane@example.com",
"avatar_url": "https://…"
},
"sessions": [
{
"token_hash": "abc123…",
"login": 1719420000,
"expiration": 1720629600,
"ip": "203.0.113.4",
"ua": "Mozilla/5.0 …"
}
]
}token_hash is the verifier WordPress keys the token by. The raw token is never returned. Use the hash as the {hash} segment of the per-session destroy endpoint below.
On a site with a custom session storage backend, user is returned as usual and sessions is an empty array. Since 1.2.0.
DELETE /loggedin/v1/active-sessions/{user}/{hash}
Destroy one specific session for a user. The hash must be a 64-char hex string (the route regex enforces this).
Response on success carries the user's refreshed sessions array so the caller can re-render without an extra GET. Fires loggedin_active_sessions_destroyed, then the parent's loggedin_destroy_session.
| Status | Code | Cause |
|---|---|---|
409 | loggedin_as_custom_storage | The site uses a custom session storage backend, so single sessions can't be signed out. Since 1.2.0. |
404 | loggedin_as_user_not_found | No user with that id. |
404 | loggedin_as_session_not_found | The hash didn't match any active token for the user. |
DELETE /loggedin/v1/active-sessions/{user}
Destroy every session for one user. Same effect as Sign out a user on the core Sessions page. Fires loggedin_destroy_all_sessions.
DELETE /loggedin/v1/active-sessions
Bulk-destroy every session for many users. Body:
{ "user_ids": [7, 12, 88] }Invalid / unknown ids are dropped silently; the response's cleared array lists the user ids that were processed. Fires loggedin_destroy_all_sessions once per successful clear.
Hooks
loggedin_active_sessions_destroyed
Fires after one single session is destroyed via the DELETE /loggedin/v1/active-sessions/{user}/{hash} endpoint. Use it for audit logging where the "all sessions" action is too coarse.
Since 1.2.0 the parent's loggedin_destroy_session fires right after it, with the same arguments, so code hooked to the parent's single sign-outs covers this path too.
do_action(
'loggedin_active_sessions_destroyed',
int $user_id,
string $token_hash
);add_action(
'loggedin_active_sessions_destroyed',
function ( $user_id, $token_hash ) {
error_log(
sprintf(
'Loggedin: admin destroyed session %s for user #%d',
$token_hash,
$user_id
)
);
},
10,
2
);"Sign out all" reuses the parent's hook
The destroy-all paths (both single-user and bulk) fire the parent's loggedin_destroy_all_sessions action, not a new add-on-specific one. Anything you've already hooked to the core action automatically covers this add-on too.
Permissions
Every route is gated on manage_options — the same capability that guards the parent's settings screen. There's no per-role override; if you need a custom capability check, hook user_has_cap and remove manage_options from the user before the request hits.
Performance notes
The list view runs a single SELECT user_id, meta_value FROM wp_usermeta WHERE meta_key = 'session_tokens' query, unserialises each row in PHP, drops expired tokens, and applies search / sort / pagination on the result. The row count is bounded by "users who have ever logged in" — typically far fewer than total registered users on a site. Since 1.2.0, roles and avatars are looked up only for the rows on the current page, not for every signed-in user.
For installs past tens of thousands of users with active sessions this becomes the bottleneck; a dedicated index table would replace the unserialise-in-PHP path. It hasn't been needed yet — let us know if your install is feeling it.
Related
- Force Logout — the core plugin's Sessions page. Single-user, all-or-nothing; this add-on is the discoverable / per-device counterpart.
- User Sessions — session counts on the Users screen, and each user's own devices on their profile.
- Real-time Logout — make a forced sign-out feel instant on the user's open tabs.
loggedin_destroy_all_sessions— the parent action this add-on fires on every destroy-all path.loggedin.admin.tabs— the JS filter the add-on uses to add its page to the admin.