Module: FlowChat::Security

Defined in:
lib/flow_chat/security.rb

Overview

Shared security helpers: constant-time comparison for webhook secrets and signatures, and the signed cookie that authorizes simulator mode.

Constant Summary collapse

"flowchat_simulator"
24 * 60 * 60

Class Method Summary collapse

Class Method Details

.secure_compare(a, b) ⇒ Object

Compare two strings without leaking their contents through timing.



21
22
23
24
25
26
27
28
29
30
# File 'lib/flow_chat/security.rb', line 21

def secure_compare(a, b)
  a = a.to_s
  b = b.to_s

  if defined?(ActiveSupport::SecurityUtils)
    ActiveSupport::SecurityUtils.secure_compare(a, b)
  else
    fallback_secure_compare(a, b)
  end
end

The value to store in the simulator cookie: "timestamp:signature".



33
34
35
# File 'lib/flow_chat/security.rb', line 33

def simulator_cookie(timestamp = Time.now.to_i)
  "#{timestamp}:#{simulator_signature(timestamp)}"
end

.simulator_signature(timestamp) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/flow_chat/security.rb', line 52

def simulator_signature(timestamp)
  OpenSSL::HMAC.hexdigest(
    OpenSSL::Digest.new("sha256"),
    FlowChat::Config.simulator_secret,
    "simulator:#{timestamp}"
  )
end

.valid_simulator_cookie?(cookie) ⇒ Boolean

A simulator cookie is valid when it carries a recent timestamp signed with the configured simulator secret.

Returns:

  • (Boolean)


39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/flow_chat/security.rb', line 39

def valid_simulator_cookie?(cookie)
  return false if FlowChat::Config.simulator_secret.blank? || cookie.blank?

  timestamp_str, signature = cookie.to_s.split(":", 2)
  return false unless timestamp_str && signature

  timestamp = timestamp_str.to_i
  return false if timestamp <= 0
  return false if (Time.now.to_i - timestamp).abs > SIMULATOR_COOKIE_TTL

  secure_compare(signature, simulator_signature(timestamp_str))
end