How to clear session and detect when user leaves app?

So I made an app and the end user can log in and log out in it. My problem is where a user completely closes all tabs and reopens them and is still logged in. I need a way to detect when all tabs are closed and log out the user automatically.

I store the user with put_session/3 in the conn struct.

By the design of the web it is impossible to know when the tabs are closed.

Some common workarounds are things like keeping a websocket open and closing the session when it dies, but that will cause people to be randomly logged out with spotty connections or if their computer/phone/whatever suspends or switches apps.

You can just do it based on a time limit, say 10 minutes auto-logout from the time they last did anything on the site.

You could just not use a session, encode all the data into the page itself (the old PHP session ID in the url style), however this can leak data out so it’s generally not recommended.

I think this is more of an XYProblem. Rather for what reason are you trying to log them out when they ‘stop using the service’ or so?

1 Like

Okay so I tried using websockets and I can detect when the page is left and send it to phoenix but I can’t figure out a way to get to the conn struct from there.

Oh and to explain for what reason I want that.

I am basically implementing webrtc for voice chat but it gets some data from an external server via websockets (this part is done) and I want it to be a single-session-chat. The user would have to go through the process of logging in every time they want to re-enter the channel.

You shouldn’t be getting the conn struct, conn is for an HTTP request. If you want to clear the session then you’ll need to clear it out of your database or so.

Why don’t they just get a unique token for just that chat then, and when they go back they need a new token, which can be like the login screen.

1 Like

Okay I managed to make it work similar to what I intended. When the user logs in they get a :logout key with the value of false and then when they load the page right after they log in the value is set to true. After that I added a plug to my router file:
Disconnect plug:

defmodule Voicer.Plugs.Disconnect do
  import Phoenix.Controller, only: [redirect: 2]
  import Plug.Conn, only: [halt: 1, clear_session: 1]
  import Voicer.Helpers
  alias Voicer.Router.Helpers, as: Helper

  def init(default), do: default

  def call(conn, _opts) do
    if should_be_logged_out?(conn) do
      conn
      |> clear_session()
      |> redirect(to: Helper.session_path(conn, :new))
      |> halt()
    else
      conn
    end
  end

end

Helpers file:

defmodule Voicer.Helpers do
  alias Plug.Conn, as: Connection

  def is_logged_in?(conn) do
    Connection.get_session(conn, :username) != nil
  end

  def should_be_logged_out?(conn) do
    if is_logged_in?(conn) do
      Connection.get_session(conn, :logout)
    else
      false
    end
  end

  def get_username!(conn) do
    Connection.get_session(conn, :username)
  end

end

But I am still giving you a solution because mentioning websockets you gave me the idea to send the page leave event to my remote server, so thanks!

1 Like