Malachi. Auth. SessionManager
(malachi v0.8.13)
View Source
Advanced session management with IP binding and trusted proxy support.
Extends the basic session system with:
- IP Binding: Binds sessions to source IP address to prevent hijacking
- Trusted Proxies: Supports CIDR ranges of trusted proxies (NAT, load balancers)
- Activity Tracking: Tracks last activity for dynamic timeout
- Session Revocation: Allows manual revocation of specific sessions or all sessions for a user
Configuration
session_timeout_seconds- Session expiration time (default: 3600 = 1 hour)session_ip_binding- Enables IP binding (default: true)session_ua_binding- Enables User-Agent binding (default: false)trusted_proxy_ranges- List of CIDR ranges for trusted proxies (default:[], nothing is trusted)
Trusted Proxies
The default is an empty list, so no range is trusted and no session is exempted from the binding
check (which itself runs unless session_ip_binding is false). The ranges below are an example to
copy, not a default: without this setting a client whose public address changes (mobile, a NAT pool
that rotates) fails the check on its next request.
For environments with NAT or corporate proxies, configure trusted ranges:
config :malachi, trusted_proxy_ranges: [
"10.0.0.0/8", # RFC1918 - Class A private network
"172.16.0.0/12", # RFC1918 - Class B private network
"192.168.0.0/16" # RFC1918 - Class C private network
]Sessions created from IPs in these ranges will have IP binding disabled.
Summary
Functions
Creates a new session with optional IP binding.
Lists active sessions.
Revokes all sessions for a user.
Revokes a specific session.
Validates an existing session.
Functions
Creates a new session with optional IP binding.
Parameters
username- Usernamepermissions- List of permissions (:admin,:produce,:consume)client_ip- Client IP address (tuple)user_agent- User-Agent string stored on the session (compared on validation when session_ua_binding is on)
Returns
{:ok, token}- Token of the created session
Examples
iex> SessionManager.create_session("admin", [:admin], {192, 168, 1, 1}, "")
{:ok, "abc123..."}
Lists active sessions.
Parameters
username- Username (:allfor all sessions)
Returns
List of maps with session information:
[
%{
token_prefix: "abc123...",
username: "admin",
ip: "192.168.1.1",
created_at: 1234567890,
expires_at: 1234571490,
last_activity: 1234570000,
ip_binding_disabled: false
},
...
]
Revokes all sessions for a user.
Parameters
username- Username
Returns
{:ok, count}- Number of sessions revoked
Revokes a specific session.
Parameters
token- Token of the session to revoke
Returns
:ok- Session successfully revoked{:error, :session_not_found}- Token does not exist
Validates an existing session.
Checks:
- Token exists
- Session has not expired
- IP binding (if enabled and not behind a trusted proxy)
- User-Agent binding (if session_ua_binding is enabled; applies even behind a trusted proxy)
Parameters
token- Session tokenclient_ip- Current client IPuser_agent- Current request User-Agent (compared when session_ua_binding is on)
Returns
{:ok, session_data}- Valid session, returns data{:error, :session_expired}- Session expired{:error, :session_hijack_attempt}- IP or UA do not match{:error, :invalid_session}- Token does not exist
Audit effects
Expiry is reported ahead of a binding mismatch, because expiry is the accurate reason and a legitimate
client whose IP moved (NAT, mobile) should not be told it looks like an attacker. The mismatch is still
recorded, so one call can emit two audit events: a token that is both expired and presented from a
new IP logs :session_expired and :session_hijack_attempt together, and returns :session_expired.
Two consequences for anyone reading Malachi.Metrics, which exposes these as separate counters:
- The audit counters are not disjoint. Summing them does not give a count of failed validations.
:session_hijack_attemptcounts a token presented with a binding that does not match (an unexpected IP, or a different User-Agent when session_ua_binding is on), not a live session stolen. The event metadata'smismatchfield lists which dimension(s) differed. Ordinary timeouts behind a changing NAT reach it, so alert thresholds should be set against that broader meaning.