Lua script context API reference for accessing AAA request data
Script Context API
The context object provides access to AAA request data through sub-contexts
and methods. See scripts for script structure, parameters, and examples.
Root Context
The root context object provides these fields and methods:
Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique request identifier |
instance_id | string | Server instance identifier (for HA deployments) |
cluster_id | string? | Cluster identifier (if configured) |
version | string | Radiator server version |
hostname | string | Server hostname |
root | context? | Root context (for nested contexts) |
parent | context? | Parent context (for nested contexts) |
Nested Contexts
Nested contexts are created automatically during tunneled EAP methods like EAP-TTLS, PEAP, and EAP-TEAP. When inner authentication runs inside the TLS tunnel, a child context is created with:
parent— the outer tunnel's contextroot— the original request context (the outermost context)
For non-tunneled requests, both root and parent are nil.
The vars sub-context is shared between parent and child contexts, allowing
data to be passed between outer and inner authentication phases.
Sub-contexts
| Field | Type | Description |
|---|---|---|
aaa | aaa | AAA processing state |
acct | acct | Accounting data |
auth | auth | Authentication state |
cache | cache | Cache operations |
cert | cert | Certificate information |
eap | eap | EAP protocol data |
eap_teap | eap_teap | EAP-TEAP specific data |
eap_ttls | eap_ttls | EAP-TTLS specific data |
http | http | HTTP request/response |
radius | radius | RADIUS packet data |
radiusproxy | radiusproxy | RADIUS proxy data |
stats | stats | Process/system stats |
tacacsplus | tacacsplus | TACACS+ protocol data |
tls | tls | TLS connection info |
user | user | User data from backend |
vars | vars | Custom variables |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
log(name, message) | logger name, message | - | Write to named AAA logger |
backend(name, query?) | backend name, optional query | result | Execute backend query |
challenge(timeout, message?) | seconds, optional message | - | Send challenge and wait for response |
count(name) | counter name string | - | Increment a statistics counter |
map(name, value) | map name, lookup value | result? | Execute a named policy map lookup |
count(name)
Increments the statistics counter identified by name. Counters are created
dynamically on first increment; no pre-declaration in the statistics block is
required.
The name argument controls which namespace the counter is placed in:
- No
::separator — counter is placed under the current pipeline context namespace (e.g.handler::<policy>::<handler>::<name>). ::separator — the parts before the last::form the namespace and the part after is the counter name. Use thecustom::prefix to create counters in the shared custom namespace.
local context, previous = ...
-- Counter in the current handler namespace
context:count("total_requests")
-- Counter in the explicit custom namespace
if context.aaa.result == "accept" then
context:count("custom::successful_logins")
else
context:count("custom::failed_logins")
end
return previous
Counter values are readable via the management API at
/api/v1/statistics/counter/<namespace>/<name> (e.g.
/api/v1/statistics/counter/custom/successful_logins).
Use the statistics block to configure history retention (samples and interval)
for counters; this is optional and separate from counter creation.
log(name, message)
Writes message to the AAA logger identified by name. The logger must be
declared in the server configuration (log-aaa block). If no logger with that
name exists, the script raises a runtime error.
The name parameter is the logger name as configured in the log-aaa block,
not a severity level. AAA loggers write structured records that include the
request identifier and timestamp automatically.
local context, previous = ...
-- Write to an AAA logger named "AUTHLOG"
context:log("AUTHLOG", "Processing " .. tostring(context.aaa.identity))
-- Conditional logging
local framed_ip = context.radius.request:attr("framed-ip-address")
if framed_ip then
context:log("AUTHLOG", "Framed-IP: " .. framed_ip)
end
return previous
Configuring the logger in radconf:
log-aaa "AUTHLOG" {
filename "log/auth.log";
format json;
}
RADIUS Packet Attribute Methods
RADIUS packet attribute access is through the context.radius.request and
context.radius.reply packet objects (see radius sub-context).
The key methods for reading attributes are:
| Method | Parameters | Returns | Description |
|---|---|---|---|
attr(name, tag?) | attribute name, optional tag | value? | Get first value of a named attribute |
attr_last(name, tag?) | attribute name, optional tag | value? | Get last value of a named attribute |
attr_all(name, tag?) | attribute name, optional tag | array? | Get all values as an array |
attrs() | - | table | Get all attributes as a table |
Attribute Name Casing
Attribute names are stored in lowercase by the RADIUS dictionary parser regardless of how they appear in the dictionary file or packet. Always pass lowercase names to all attribute methods.
-- Correct: lowercase
local ip = context.radius.request:attr("framed-ip-address")
local nas = context.radius.request:attr("nas-identifier")
local mac = context.radius.request:attr("calling-station-id")
-- Wrong: mixed-case silently returns nil
-- local ip = context.radius.request:attr("Framed-IP-Address") -- nil
This applies only to Lua attribute methods. Radconf condition expressions
(radius.request.attr.Framed-IP-Address) and template expansions
(%{radius.request.attr.Framed-IP-Address}) are handled by a separate code
path and are not affected.
attr(name, tag?)
Returns the first value of the named attribute, or nil if the attribute is
not present. The optional tag parameter (integer 1-31) filters tagged
attributes; omit it (or pass nil) for untagged lookups.
local username = context.radius.request:attr("user-name")
local ip = context.radius.request:attr("framed-ip-address")
if not ip then
-- attribute absent; treat as unknown origin
end
attrs()
Returns a Lua table where each key is a lowercase attribute name (string) and each value is an array (1-based) of all occurrences of that attribute. Even attributes that appear only once are wrapped in a one-element array so callers can iterate uniformly.
Attribute values are mapped to Lua types as follows:
| RADIUS type | Lua type |
|---|---|
| Integer / Unsigned | number |
| Text / String | string |
| IP address / network | string (dotted-decimal or CIDR) |
| Bytes / binary | string (raw bytes) |
| Boolean | boolean |
| Missing / unknown | nil |
local all = context.radius.request:attrs()
-- Single-valued attribute
local username = all["user-name"] and all["user-name"][1]
-- Multi-valued attribute
local filters = all["filter-id"] or {}
for _, f in ipairs(filters) do
context:log("AUTHLOG", "Filter: " .. tostring(f))
end
-- Dump every attribute for debugging
for name, values in pairs(all) do
for _, v in ipairs(values) do
context:log("AUTHLOG", name .. " = " .. tostring(v))
end
end