76
Views
4
Comments
How to track user logout and session timeout?

I need to record the date and time whenever a user's session ends, whether they clicked "Logout" or their session just expired from inactivity.

For logout, it's straightforward, but I'm stuck on the best way to reliably catch the session expiry event. Has anyone tackled this before?

Any tips or pointers would be awesome!

2026-03-12 10-32-06
Wahaj Adil

hi @Rajith PC
you can try using Javascript to detect inactivity [eg: settimeout after Session.Timeout * 60 * 1000 ms]
when the timer triggers call an action to log the date time.

2025-12-29 06-17-15
Deepak Raj M
  • On Logout button click (explicit logout)

    • Call a Logout Action in OutSystems.

    • Inside it, before User_Logout(), save an entry to your Audit / Log table like:

      • UserId

      • Action = "Logout"

      • TimeStamp

    This way, you know the user explicitly clicked Logout.

  • On Session Timeout (implicit logout)

    • No logout action will be called, so no record will be inserted.

    • Next time you check logs, if you don’t see a Logout entry but the session ended, you can assume it was a timeout.

2025-08-07 06-30-56
Amit J
Champion

OutSystems does not raise a “session expired” event you can hook. You must log it yourself. The reliable pattern is to track session start, heartbeat (last activity), and then close stale sessions as “expired” with a background timer.

Do this:

  1. Create an entity UserSessionLog Fields: Id, UserId, SessionToken (Text GUID), StartAt (DateTime), LastSeenAt (DateTime), EndAt (DateTime nullable), EndReason (Text: Logout|Timeout), IsOpen (Boolean). Index on SessionToken and on IsOpen.

  2. On successful login Generate SessionToken = GUID(). Save it in a Session variable (per module) or Client variable (Reactive) named CurrentSessionToken. Create UserSessionLog row with UserId = GetUserId(), StartAt = CurrDateTime(), LastSeenAt = CurrDateTime(), IsOpen = True.

  3. Heartbeat to capture activity Reactive Web: put a small web block in your common layout. In OnReady, start a JavaScript setInterval that calls a server action every N minutes (e.g., 3–5). The server action receives CurrentSessionToken and does: Find UserSessionLog by SessionToken where IsOpen = True If found, update LastSeenAt = CurrDateTime() JavaScript example: window._hb = window._hb || setInterval(function(){ $actions.UpdateHeartbeat($parameters.CurrentSessionToken); }, 180000); Traditional Web: add a web block with a periodic Ajax refresh (using a timer via JavaScript setInterval that triggers an Ajax call to a server action that updates LastSeenAt). Mobile: same idea; use a JS block with setInterval and call a server action through an Ajax request to update LastSeenAt. Also call the heartbeat on common navigation events (screen OnInitialize).

    Note: do not rely on onbeforeunload or visibilitychange for expiry; users can close the app or go offline. Heartbeat plus timeout reconciliation is the robust approach.

  4. On Logout click Server action should: Read CurrentSessionToken Find open UserSessionLog by SessionToken If found, set EndAt = CurrDateTime(), EndReason = "Logout", IsOpen = False Clear CurrentSessionToken and execute OutSystems Logout.

  5. Mark timeouts with a background Timer Create a Site Property SessionTimeoutMinutes that matches your environment’s session timeout (e.g., 20 or 30; keep it in sync with IT). Create a Timer CloseExpiredSessions that runs every 10 minutes. Timer logic: NowUtc = CurrDateTime() Threshold = NowUtc - SessionTimeoutMinutes For all UserSessionLog where IsOpen = True and LastSeenAt <= Threshold: Set EndAt = AddMinutes(LastSeenAt, SessionTimeoutMinutes)  // or simply EndAt = LastSeenAt if you want “last activity time” EndReason = "Timeout" IsOpen = False

  6. Use one row per device/session Keep SessionToken per browser/tab group or per device. If you want per-tab tracking, generate a token per tab and store it in localStorage and include it in every request.

  7. Reporting Active sessions: IsOpen = True. Ended sessions: EndAt not null, with EndReason. Last activity: LastSeenAt.

Minimal server actions you need:
StartSession(UserId) → returns SessionToken, inserts a log.
UpdateHeartbeat(SessionToken) → updates LastSeenAt.
EndSession(SessionToken, reason) → sets EndAt and closes.
CloseExpiredSessions() → used by Timer. 

2019-11-11 17-10-24
Manish Jawla
 
MVP

Hi @Rajith PC ,

For the logout case, you’re right—it’s simple since you can hook into the OnLogout logic and record the date/time directly.

For session expiry due to inactivity, OutSystems doesn’t provide a direct “session ended” event you can capture server-side. A common approach is to use a client-side timer or idle detection pattern that pings the server and, when it detects the session is no longer valid, logs the expiry time.

You might also find it easier to leverage some Forge components:

https://www.outsystems.com/forge/component-overview/20540/sessiontimeout-o11 

https://www.outsystems.com/forge/component-overview/18823/session-timeout-alert-o11 

https://www.outsystems.com/forge/component-overview/4030/session-timeout-o11 

https://www.outsystems.com/forge/component-overview/14662/user-session-timeout-o11 

Regards,

Manish Jawla

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.