Sessions

Sessions

State management between the client and server

Sessions are essential for allowing users to login to your application. Like Rails, Terminalwire uses sessions to manage state between commands, but there are some differences that are covered after the big picture.

Working with sessions

Sessions are accessible in Thor via the session method. In this example, a current_user getter and setter wrap the uaer_id key in sesion.

# Learn how to use Thor at http://whatisthor.com.
class ApplicationTerminal < Thor
  # .. Code ...
  private

  def current_user=(user)
    # The Session object is a hash-like object that encrypts and signs a hash that's
    # stored on the client's file sytem. Conceptually, it's similar to Rails signed
    # and encrypted client-side cookies.
    session["user_id"] = user.id
  end

  def current_user
    @current_user ||= User.find(session["user_id"])
  end
end

When a session is access from the server, it streams the JWT file from the users workstation to your server. The server then decrypts and validates the contents of the token with the Rails.application.secret_key_base and then reads the contents of the token. If the token is invalid, the server will raise an error that you’ll need to handle.

Writing session data encryptes the hash as a JWT, then streams it back to the users workstation and writes it to the ~/.terminalwire/authorities/example.com/storage/session.jwt file.

Bulk session changes

Since each change to a session requires a round-trip between the server and the client, it’s best to make bulk changes to the session. This is done by passing a block to the Session#edit method.

session.edit |data|
  data["uuid"] = SecureRandom.uuid
  data["id"] = user.id
  data["expires_at"] = 1.month.from_now
end

This code will make all the changes to the session in a single round-trip to the client.

Destroying sessions

To reset a session, you can call the Session#destroy method. This deletes the session file from the users workstation.

Implementation details

Different frameworks deal with sessions differently, which is why Terminalwire tries to model its session management after Rails.

Sessions are JavaScript Web Tokens

Sessions are stored in a JavaScript Web Token, or JWT,

on your users workstation at ~/.terminalwire/authorities/$DOMAIN/storage/session.jwt. The file is encrypted and signed using the Rails.application.secret_key_base as the secret. Because it’s encrypted, users can’t read or tamper with the contents of the session token. If users try to tamper with a token, the server will raise an error.

Stored in the client’s file system

Sessions are similar to cookies in web browsers in that they’re stored on the client’s file system. The server stores session files on the client’s workstation at ~/.terminalwire/authorities/$AUTHORITY/storage/session.jwt where $AUTHORITY is the domain and non-default port of the server.

Encrypted and signed

Session files are encrypted on the server using the Rails.application.secret_key_base as the default secret. This means that users can’t read or tamper with the contents of the session file. If users try to tamper with a session file, the server will raise an error.

No expiration or IDs

Unlike Rails, Sessions do not have an ID or an expiration. You can do this manually by storing the expiration and ID in the session.

session.edit do |data|
  data["expires_at"] = 1.month.from_now
  data["uuid"] = SecureRandom.uuid
end

Then, in your application code you can check for the existence of the expires_at key and compare it to the current time to see if the session has expired.