Class: SignalWire::Security::SessionManager
- Inherits:
-
Object
- Object
- SignalWire::Security::SessionManager
- Defined in:
- lib/signalwire/security/session_manager.rb
Overview
Stateless HMAC-SHA256 session manager for secure SWAIG tool tokens.
Tokens are self-contained: all information needed for validation is encoded inside the token itself. No server-side session state is stored.
mgr = SessionManager.new(token_expiry_secs: 900)
token = mgr.create_token("lookup_order", "call-abc-123")
mgr.validate_token("lookup_order", token, "call-abc-123") # => true
Instance Method Summary collapse
-
#create_token(function_name, call_id) ⇒ String
Create a secure, self-contained token for a function call.
-
#initialize(token_expiry_secs: 3600, secret_key: nil) ⇒ SessionManager
constructor
A new instance of SessionManager.
-
#validate_token(function_name, token, call_id) ⇒ Boolean
Validate a function-call token.
Constructor Details
#initialize(token_expiry_secs: 3600, secret_key: nil) ⇒ SessionManager
Returns a new instance of SessionManager.
26 27 28 29 |
# File 'lib/signalwire/security/session_manager.rb', line 26 def initialize(token_expiry_secs: 3600, secret_key: nil) @token_expiry_secs = [token_expiry_secs, 1].max @secret_key = secret_key || SecureRandom.hex(32) end |
Instance Method Details
#create_token(function_name, call_id) ⇒ String
Create a secure, self-contained token for a function call.
Token format (before Base64):
call_id.function_name..nonce.hmac_hex
39 40 41 42 43 44 45 46 47 48 |
# File 'lib/signalwire/security/session_manager.rb', line 39 def create_token(function_name, call_id) expiry = (Time.now.to_i + @token_expiry_secs).to_s nonce = SecureRandom.hex(8) = "#{call_id}:#{function_name}:#{expiry}:#{nonce}" signature = compute_hmac() token_raw = "#{call_id}.#{function_name}.#{expiry}.#{nonce}.#{signature}" Base64.urlsafe_encode64(token_raw, padding: false) end |
#validate_token(function_name, token, call_id) ⇒ Boolean
Validate a function-call token.
Checks:
-
Correct Base64 / structure (5 dot-separated parts)
-
HMAC signature (timing-safe comparison)
-
Function name matches
-
Call ID matches
-
Token not expired
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/signalwire/security/session_manager.rb', line 63 def validate_token(function_name, token, call_id) return false if token.nil? || token.empty? return false if call_id.nil? || call_id.empty? decoded = Base64.urlsafe_decode64(token) parts = decoded.split(".") return false unless parts.length == 5 token_call_id, token_function, token_expiry, token_nonce, token_signature = parts # Verify function name return false unless token_function == function_name # Verify call ID return false unless token_call_id == call_id # Check expiry expiry = Integer(token_expiry) return false if expiry < Time.now.to_i # Recompute HMAC and compare with timing-safe comparison = "#{token_call_id}:#{token_function}:#{token_expiry}:#{token_nonce}" expected_signature = compute_hmac() secure_compare(token_signature, expected_signature) rescue ArgumentError, TypeError # Bad Base64, bad integer, etc. false end |