From dd759f6a582064973f0b702f0589aaa4e4cd45b2 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 26 Aug 2026 09:00:00 +0200 Subject: [PATCH] feat(session): add signed session cookie codec --- lib/prism/session.rb | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/prism/session.rb diff --git a/lib/prism/session.rb b/lib/prism/session.rb new file mode 100644 index 0000000..6c557ac --- /dev/null +++ b/lib/prism/session.rb @@ -0,0 +1,43 @@ +module Prism + module Session + Separator = '--'.freeze + + module_function + + def encode(hash, secret) + payload = encode64(JSON.generate(hash)) + "#{payload}#{Separator}#{sign(payload, secret)}" + end + + def decode(cookie, secret) + return {} if cookie.nil? || cookie.empty? + payload, signature = cookie.split(Separator, 2) + return {} if payload.nil? || signature.nil? + return {} unless secure_compare(signature, sign(payload, secret)) + JSON.parse(decode64(payload)) + rescue JSON::ParserError, ArgumentError + {} + end + + def sign(payload, secret) + OpenSSL::HMAC.hexdigest('SHA256', secret, payload) + end + + def secure_compare(left, right) + return false unless left.bytesize == right.bytesize + difference = 0 + left.bytes.zip(right.bytes) { |a, b| difference |= a ^ b } + difference.zero? + end + + def encode64(text) + [text].pack('m0').tr('+/', '-_').delete('=') + end + + def decode64(text) + restored = text.tr('-_', '+/') + padding = (4 - restored.bytesize % 4) % 4 + (restored + ('=' * padding)).unpack1('m0') + end + end +end