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.