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

FieldTypeDescription
idstringUnique request identifier
instance_idstringServer instance identifier (for HA deployments)
cluster_idstring?Cluster identifier (if configured)
versionstringRadiator server version
hostnamestringServer hostname
rootcontext?Root context (for nested contexts)
parentcontext?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 context
  • root — 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

FieldTypeDescription
aaaaaaAAA processing state
acctacctAccounting data
authauthAuthentication state
cachecacheCache operations
certcertCertificate information
eapeapEAP protocol data
eap_teapeap_teapEAP-TEAP specific data
eap_ttlseap_ttlsEAP-TTLS specific data
httphttpHTTP request/response
radiusradiusRADIUS packet data
radiusproxyradiusproxyRADIUS proxy data
statsstatsProcess/system stats
tacacsplustacacsplusTACACS+ protocol data
tlstlsTLS connection info
useruserUser data from backend
varsvarsCustom variables

Methods

MethodParametersReturnsDescription
log(name, message)logger name, message-Write to named AAA logger
backend(name, query?)backend name, optional queryresultExecute 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 valueresult?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 the custom:: 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:

MethodParametersReturnsDescription
attr(name, tag?)attribute name, optional tagvalue?Get first value of a named attribute
attr_last(name, tag?)attribute name, optional tagvalue?Get last value of a named attribute
attr_all(name, tag?)attribute name, optional tagarray?Get all values as an array
attrs()-tableGet 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 typeLua type
Integer / Unsignednumber
Text / Stringstring
IP address / networkstring (dotted-decimal or CIDR)
Bytes / binarystring (raw bytes)
Booleanboolean
Missing / unknownnil
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