lib/faye/authentication.rb in faye-authentication-0.1.0 vs lib/faye/authentication.rb in faye-authentication-0.2.0

- old
+ new

@@ -1,32 +1,34 @@ -require "faye/authentication/version" +require 'jwt' +require 'faye/authentication/version' require 'faye/authentication/extension' require 'faye/authentication/http_client' require 'faye/authentication/engine' module Faye module Authentication + class AuthError < StandardError; end + class ExpiredError < AuthError; end + class PayloadError < AuthError; end - def self.sign(message, secret) - OpenSSL::HMAC.hexdigest('sha1', secret, "#{message['channel']}-#{message['clientId']}") + # Return jwt signature, pass hash of payload including channel and client_id + def self.sign(payload, secret, options = {}) + options = {expires_at: Time.now + 12*3600, algorithm: 'HS256'}.merge(options) + JWT.encode(payload.merge(exp: options[:expires_at].to_i), secret, options[:algorithm]) end - def self.valid?(message, secret) - signature = message.delete('signature') - return false unless signature - secure_compare(signature, sign(message, secret)) + # Return signed payload or raise + def self.decode(signature, secret) + payload, _ = JWT.decode(signature, secret) rescue raise(AuthError) + raise ExpiredError if Time.at(payload['exp'].to_i) < Time.now + payload end - # constant-time comparison algorithm to prevent timing attacks - # Copied from ActiveSupport::MessageVerifier - def self.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 + # Return true if signature is valid and correspond to channel and clientId or raise + def self.validate(signature, channel, clientId, secret) + payload = self.decode(signature, secret) + raise PayloadError if channel.to_s.empty? || clientId.to_s.empty? + raise PayloadError unless channel == payload['channel'] && clientId == payload['clientId'] + true end - end end