Skip to content

Instantly share code, notes, and snippets.

@arkadiyt
Last active January 12, 2023 20:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arkadiyt/4328e4dd937645a8946e589c44adad63 to your computer and use it in GitHub Desktop.
Save arkadiyt/4328e4dd937645a8946e589c44adad63 to your computer and use it in GitHub Desktop.
Generate signed tokens in ruby
require 'base64'
require 'json'
require 'openssl'
require 'time'
def secure_compare(a, b)
return false unless a.bytesize == b.bytesize
l = a.unpack "C#{a.bytesize}"
res = 0
b.each_byte { |byte| res |= byte ^ l.shift }
res == 0
end
def generate_token(expiration, secret, data = {})
encoded_data = Base64.urlsafe_encode64(JSON.generate(data.merge(expiration: (Time.now.utc + expiration).to_i.to_s)))
signature = OpenSSL::HMAC.hexdigest('sha256', secret, encoded_data)
"#{encoded_data}.#{signature}"
end
def data_from_token(token, secret)
encoded_data, signature, _ = *token.split('.')
expected_signature = OpenSSL::HMAC.hexdigest('sha256', secret, encoded_data)
raise StandardError, 'Invalid signature' unless secure_compare(signature, expected_signature)
data = JSON.parse(Base64.urlsafe_decode64(encoded_data))
expiration = Time.at(data.delete('expiration').to_i).utc
raise StandardError, 'Expired token' if Time.now.utc > expiration
data
end
# Usage:
SECRET = 'my secret'
token = generate_token(10, SECRET, {a: 1, b: 2})
# => "eyJhIjoxLCJiIjoyLCJleHBpcmF0aW9uIjoiMTYxNDg4OTYxOSJ9.c40ccd52<snipped>"
data_from_token(token, SECRET)
# => {"a"=>1, "b"=>2}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment