# How to Manage Sessions and Implement Auto-Logout on Authentication Expiry in FinceptTerminal

> Learn how FinceptTerminal manages user sessions and implements auto-logout on authentication expiry using AuthManager and SessionGuard for seamless security.

- Repository: [Fincept Corporation/FinceptTerminal](https://github.com/Fincept-Corporation/FinceptTerminal)
- Tags: how-to-guide
- Published: 2026-04-20

---

**FinceptTerminal manages sessions through the `AuthManager` singleton for state persistence and `SessionGuard` for periodic validation, automatically logging users out when the backend returns HTTP 401/403 or `"valid": false`.**

FinceptTerminal provides a robust authentication architecture that enables developers to manage sessions securely and implement auto-logout on authentication expiry. The system's design centers on two primary components: the `AuthManager` singleton, which maintains the current `SessionData` and handles persistence, and the `SessionGuard` watchdog, which monitors session validity through periodic pulses to the backend.

## Core Architecture

The session management system relies on three primary components working in concert:

| Component | Responsibility | Key API / Signals | Typical Usage |
|-----------|---------------|-------------------|---------------|
| **`AuthManager`** | Central state machine; stores `session_` (`SessionData`). Provides methods for login, signup, OTP/MFA verification, password reset, logout, session init/recovery, and refresh of user data. Emits a rich set of signals to inform the UI of auth state changes. | `auth_state_changed()`, `login_succeeded()`, `login_failed()`, `session_expired()`, `logged_out()`, `terminal_unlocked()` … | UI widgets obtain the singleton via `AuthManager::instance()` and read `session()` or connect to the signals. |
| **`SessionGuard`** | Periodic background task (Qt `QTimer`) that calls `AuthApi::session_pulse`. Handles HTTP 401/403 or a `"valid": false` response, attempts session recovery, and on failure logs out the user automatically. | Emits its own `session_expired()` (mirrored from `AuthManager`) and stops the timer when the session becomes unauthenticated. | Instantiated once (e.g. in the main window) and left running for the lifetime of the app. |
| **`AuthApi`** (stateless) | Thin wrapper around HTTP calls (`login`, `session_pulse`, `logout`, …). All network interaction for auth lives here; the API key is stored in the shared `HttpClient`. | No signals – callbacks (`std::function<void(ApiResponse)>`). | Called by `AuthManager` and `SessionGuard` to talk to the backend. |

## Session Lifecycle

Understanding the lifecycle is essential to manage sessions effectively:

1. **App start** → `AuthManager::initialize()` reads any persisted session from disk (`load_session()`).
2. If the session is *authenticated* and contains a valid `api_key`, UI components (e.g., `ToolBar`, `NavigationBar`) automatically call `SessionGuard::start()`.
3. **Login flow** – UI calls `AuthManager::login(email, password, force)`. On success `AuthManager` updates `session_`, emits `auth_state_changed()` and `login_succeeded()`.
4. **Pulse** – `SessionGuard` sends a `session_pulse` every `PULSE_INTERVAL_MS`.
   * 401/403 → token may be stale → `AuthManager::attempt_session_recovery()` is invoked.
   * `valid:false` in body → same recovery path.
5. **Recovery** – If the backend can refresh the session (e.g., via a refresh token), `AuthManager` restores `session_` and emits `auth_state_changed()`.
6. **Failure** – If recovery fails, `SessionGuard` emits `session_expired()`, stops its timer, and forces a logout via `AuthManager::logout()`. The UI receives `logged_out()` / `auth_state_changed()` and redirects the user to the login screen.

## Implementation Examples

### Accessing the Session and Reacting to Changes

UI components connect to `AuthManager` signals to update their state dynamically:

```cpp
// Example: UI toolbar that shows the user’s name and logout button
#include "auth/AuthManager.h"

ToolBar::ToolBar(QWidget *parent) : QWidget(parent) {
    connect(&auth::AuthManager::instance(),
            &auth::AuthManager::auth_state_changed,
            this,
            &ToolBar::refresh_all);
    refresh_all();
}

void ToolBar::refresh_all() {
    const auto &s = auth::AuthManager::instance().session();
    userLabel_->setText(s.username);
    logoutBtn_->setEnabled(s.authenticated);
}

```

*Source*: [[`fincept-qt/src/ui/navigation/ToolBar.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/ui/navigation/ToolBar.cpp)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/ui/navigation/ToolBar.cpp)

### Starting the Auto-Logout Watchdog

Instantiate `SessionGuard` in your main window to enable automatic session validation:

```cpp
// Usually instantiated in the main window constructor
#include "auth/SessionGuard.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
    sessionGuard_ = new auth::SessionGuard(this);
    // SessionGuard automatically starts when a valid session appears
}

```

*Source*: [[`fincept-qt/src/auth/SessionGuard.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/SessionGuard.cpp)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/SessionGuard.cpp)

### Handling Expired Sessions

The `SessionGuard` automatically triggers logout when the server rejects the session pulse:

```cpp
void SessionGuard::check_pulse() {
    // …
    if (!r.success && (r.status_code == 401 || r.status_code == 403)) {
        // Attempt recovery…
        AuthManager::instance().attempt_session_recovery([this](bool recovered) {
            if (!recovered) {
                stop();                     // stop further pulses
                emit session_expired();      // UI can also listen to this
                AuthManager::instance().logout(); // forces full logout
            }
        });
    }
}

```

*Source*: same file as above.

### Full Logout Implementation

The `AuthManager` clears local state regardless of server response:

```cpp
void AuthManager::logout() {
    if (is_logging_out_) return;
    is_logging_out_ = true;
    AuthApi::instance().logout([this](ApiResponse r) {
        // Regardless of server response we clear the local session
        clear_session();
        set_loading(false);
        is_logging_out_ = false;
        emit logged_out();
        emit auth_state_changed();
    });
}

```

*Source*: [[`fincept-qt/src/auth/AuthManager.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/AuthManager.cpp)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/AuthManager.cpp)

## Customization Guide

| Goal | What to modify | Where |
|------|----------------|-------|
| Change pulse interval | Adjust `PULSE_INTERVAL_MS` constant in [`SessionGuard.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/SessionGuard.h) | [`fincept-qt/src/auth/SessionGuard.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/SessionGuard.h) |
| Add custom recovery logic (e.g., refresh token) | Override `AuthManager::attempt_session_recovery` or plug a new API endpoint in `AuthApi::session_pulse` | [`AuthManager.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthManager.cpp) & [`AuthApi.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthApi.cpp) |
| Show a UI warning before auto‑logout | Connect to `SessionGuard::session_expired` in your main UI and present a modal dialog | Your application’s main window or a dedicated `SessionExpiredDialog` |
| Persist additional session fields (e.g., device fingerprint) | Extend `SessionData` in [`AuthTypes.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthTypes.h) and update `AuthManager::save_session`/`load_session` | [`fincept-qt/src/auth/AuthTypes.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/AuthTypes.h) & [`AuthManager.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthManager.cpp) |

## Key Files Reference

| File | Role |
|------|------|
| [[`AuthManager.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthManager.h)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/AuthManager.h) | Declares the singleton, session data accessor, public auth methods, and signals. |
| [[`AuthManager.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthManager.cpp)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/AuthManager.cpp) | Implements login, logout, session persistence, recovery, and signal emission. |
| [[`AuthApi.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthApi.h)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/AuthApi.h) | Stateless HTTP wrapper for auth endpoints, used by `AuthManager` and `SessionGuard`. |
| [[`SessionGuard.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/SessionGuard.h)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/SessionGuard.h) | Declares the watchdog timer and its public API. |
| [[`SessionGuard.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/SessionGuard.cpp)](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/SessionGuard.cpp) | Implements the pulse loop, recovery handling, and auto‑logout trigger. |
| UI components (e.g., [`ToolBar.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/ToolBar.cpp), [`NavigationBar.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/NavigationBar.cpp), [`ProfileScreen.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/ProfileScreen.cpp)) | Show how the rest of the application observes `AuthManager` signals and reacts to logout. |

## Summary

- **Use `AuthManager`** as the central singleton for all authentication state, session persistence, and signal emission.
- **Deploy `SessionGuard`** to automatically monitor session validity via periodic `session_pulse` calls to the backend.
- **Handle expiry** by listening to `AuthManager::auth_state_changed` or `SessionGuard::session_expired` signals to update UI state and redirect to login.
- **Customize behavior** by modifying `PULSE_INTERVAL_MS`, overriding `attempt_session_recovery`, or extending `SessionData` for additional fields.
- **Key files** are located in `fincept-qt/src/auth/` and follow the `AuthManager` → `SessionGuard` → `AuthApi` hierarchy.

## Frequently Asked Questions

### How does FinceptTerminal detect when a session has expired?

The `SessionGuard` component sends a periodic `session_pulse` to the backend every `PULSE_INTERVAL_MS`. If the server responds with HTTP 401/403 or a JSON body containing `"valid": false`, the guard initiates recovery via `AuthManager::attempt_session_recovery()`. When recovery fails, it emits `session_expired()` and calls `AuthManager::logout()` to clear local state.

### Where is session data stored between application restarts?

`AuthManager` persists session data to disk via `save_session()` and reloads it on startup through `load_session()` inside `initialize()`. The storage location and serialization format are handled within [`AuthManager.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthManager.cpp), allowing the session to survive application restarts while the `SessionData` structure in [`AuthTypes.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/AuthTypes.h) defines the fields being persisted.

### Can I customize the auto-logout timer interval?

Yes. The pulse interval is controlled by the `PULSE_INTERVAL_MS` constant in [`fincept-qt/src/auth/SessionGuard.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/auth/SessionGuard.h). Adjusting this value changes how frequently the client validates the session with the server. For recovery customization, override `AuthManager::attempt_session_recovery` to implement refresh token logic or other backend-specific renewal mechanisms.

### How should the UI respond to automatic logout?

UI components should connect to `AuthManager::auth_state_changed` or `SessionGuard::session_expired` signals. When these fire, the application should redirect to the login screen, disable sensitive controls, or display a modal dialog. The [`ToolBar.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/ToolBar.cpp) implementation demonstrates reading `AuthManager::instance().session()` to dynamically update visibility of user-specific elements based on authentication state.