lib/tynn/session.rb in tynn-1.4.0 vs lib/tynn/session.rb in tynn-2.0.0.alpha

- old
+ new

@@ -1,23 +1,25 @@ +# frozen_string_literal: true + class Tynn - # Public: Adds simple cookie based session management. You can pass a secret + # Adds simple cookie based session management. You can pass a secret # token to sign the cookie data, thus unauthorized means can't alter it. # - # Examples - # # require "tynn" # require "tynn/session" # - # Tynn.plugin(Tynn::Session, secret: "__change_me__") + # Tynn.plugin(Tynn::Session, secret: "__change_me_not_secure__") # # Tynn.define do - # root do - # res.write(sprintf("hei %s", session[:username])) - # end + # on("login") do + # post do + # # ... # - # on(:username) do |username| - # session[:username] = username + # session[:user_id] = user.id + # + # res.redirect("/admin") + # end # end # end # # The following command generates a cryptographically secure secret ready # to use: @@ -26,31 +28,31 @@ # # It's important to keep the token secret. Knowing the token allows an # attacker to tamper the data. So, it's recommended to load the token # from the environment. # - # Examples - # # Tynn.plugin(Tynn::Session, secret: ENV["SESSION_SECRET"]) # - # Under the hood, Tynn::Session uses the +Rack::Session::Cookie+ middleware. - # Thus, supports all the options available for this middleware: + # Under the hood, Tynn::Session uses the <tt>Rack::Session::Cookie</tt> + # middleware. Thus, supports all the options available for this middleware: # - # key - The name of the cookie. Defaults to <tt>"rack.session"</tt>. + # [key] + # The name of the cookie. Defaults to <tt>"rack.session"</tt>. # - # httponly - If +true+, sets the +HttpOnly+ flag. This mitigates the - # risk of client side scripting accessing the cookie. Defaults - # to +true+. + # [httponly] + # If <tt>true</tt>, sets the <tt>HttpOnly</tt> flag. This mitigates the + # risk of client side scripting accessing the cookie. Defaults to <tt>true</tt>. # - # secure - If +true+, sets the +Secure+ flag. This tells the browser - # to only transmit the cookie over HTTPS. Defaults to `false`. + # [secure] + # If <tt>true</tt>, sets the <tt>Secure</tt> flag. This tells the browser + # to only transmit the cookie over HTTPS. Defaults to <tt>false</tt>. # - # expire_after - The lifespan of the cookie. If +nil+, the session cookie - # is temporary and is no retained after the browser is - # closed. Defaults to +nil+. + # [expire_after] + # The lifespan of the cookie. If <tt>nil</tt>, the session cookie is temporary + # and is no retained after the browser is closed. Defaults to <tt>nil</tt>. # - # Examples + # <tt></tt> # # Tynn.plugin( # Tynn::Session, # key: "app", # secret: ENV["SESSION_SECRET"], @@ -58,30 +60,62 @@ # httponly: true, # secure: true # ) # module Session - # Internal: Configures Rack::Session::Cookie middleware. - def self.setup(app, options = {}) - if app.settings[:ssl] - options = { secure: true }.merge(options) + SECRET_MIN_LENGTH = 30 # :nodoc: + + def self.setup(app, options = {}) # :nodoc: + secret = options[:secret] + + if secret.nil? + raise <<~MSG + No secret option provided to Tynn::Session. + + Tynn::Session uses a secret token to sign the cookie data, thus + unauthorized means can't alter it. Please, add the secret option + to your code: + + #{ app }.plugin(Tynn::Session, secret: "__a_long_random_secret__", ...) + + If you're sharing your code publicly, make sure the secret key + is kept private. Knowing the secret allows an attacker to tamper + the data. You can use environment variables to store the secret: + + #{ app }.plugin(Tynn::Session, secret: ENV.fetch("SESSION_SECRET"), ...) + MSG end - app.use(Rack::Session::Cookie, options) + if secret.length < SECRET_MIN_LENGTH + raise <<~MSG + The secret provided is shorter than the minimum length. + + Make sure the secret is long and all random. You can generate a + secure secret key with: + + $ ruby -r securerandom -e "puts SecureRandom.hex(64)" + MSG + end + + app.use(Rack::Session::Cookie, { + coder: Rack::Session::Cookie::Base64::JSON.new, + hmac: OpenSSL::Digest::SHA256, + same_site: :Lax + }.merge(options)) end module InstanceMethods - # Public: Returns the session hash. + # Returns the session hash. # - # Examples + # session + # # => {} # - # session # => {} - # # session[:foo] = "foo" - # session[:foo] # => "foo" + # session[:foo] + # # => "foo" # def session - return req.session + req.session end end end end