Modeling your App’s User Session
If you’ve been keeping an eye on your cookies, you may have noticed some recent changes GitHub has made to how we track your session. You shouldn’t notice any difference…
If you’ve been keeping an eye on your cookies, you may have noticed some recent changes GitHub has made to how we track your session. You shouldn’t notice any difference in session behavior (beyond the new ability to revoke sessions), but we’d like to explain what prompted the change.
Replay attacks on stateless session stores have been known and documented for quite some time in Rails and Django. Using signed cookies for sessions is still incredibly easy to use and scales to high traffic web apps. You just need to understand its limitations. When implementing authentication, simply storing a user ID in the session cookie leaves you open to replay attacks and provides no means for revocation.
The other option is to switch to persisted storage for sessions. Either using a database, memcache or redis. On a high traffic site this may be a performance concern since a session may be allocated even for anonymous browsing traffic. Another downside, there is no clear insight into these sessions. They are stored as serialized objects. So there’s no way to query the store to see if a user has any sessions. It’s all abstracted away by Rails.
Hybrid Cookie Store / DB approach
After ruling out Rails’ built in method for DB backed sessions, we decided that the concept of user sessions ought to be treated as a first class domain concern. Something with a real application API we can query, test and extend with other app concerns.
The UserSession
class is just a normal ActiveRecord class like any other. There’s no excess Rails abstraction layer between it. We’ve extended it with other concerns such as manual revocation, sudo mode tracking and data like IP and user agent to help users identify sessions on the active sessions page.
class UserSession < ActiveRecord::Base
belongs_to :user
before_validation :set_unique_key
scope :active, lambda {
{ :conditions => ["accessed_at >= ? AND revoked_at == NULL", 2.weeks.ago] }
}
def self.authenticate(key)
self.active.find_by_key(key)
end
def revoke!
self.revoked_at = Time.now
save!
end
def sudo?
sudo_enabled_at > 1.hour.ago
end
def sudo!
self.sudo_enabled_at = Time.now
save!
end
def access(request)
self.accessed_at = Time.now
self.ip = request.ip
self.user_agent = request.user_agent
save
end
private
def set_unique_key
self.key = SecureRandom.urlsafe_base64(32)
end
end
Staying true to the restful authentication spirit, SessionsController#create
creates a new UserSession
and SessionsController#destroy
deletes it.
A separate cookie called user_session
is set referencing the record unique random key. Only signed in users allocate this record. Anonymous traffic to GitHub never creates junk data in our sessions table.
We still have our signed cookie store around as session
in our controllers. This handles non-sensitive data like flash notices and multi-step form state. Then we have a separate user_session
helper that references the current user’s session record.
This infrastructure change took a few months. For a month, we ran both the old session code path on this new user session path at once. This allowed users to transition over to the new cookie without noticing.
Overall, we are pretty happy with the change. It has made our authentication logic much more clear and explicit. This opens up some new potential now that we have the data on the server.
Written by
Related posts
Inside the research: How GitHub Copilot impacts the nature of work for open source maintainers
An interview with economic researchers analyzing the causal effect of GitHub Copilot on how open source maintainers work.
OpenAI’s latest o1 model now available in GitHub Copilot and GitHub Models
The December 17 release of OpenAI’s o1 model is now available in GitHub Copilot and GitHub Models, bringing advanced coding capabilities to your workflows.
Announcing 150M developers and a new free tier for GitHub Copilot in VS Code
Come and join 150M developers on GitHub that can now code with Copilot for free in VS Code.