Security & Access Control

Manage authentication actors, authorization scopes, and access policies.

Loading

local security = require("security")

actor

Returns the current security actor from the execution context.

local actor = security.actor()
if actor then
    local id = actor:id()
    local meta = actor:meta()

    logger:info("Request from", {
        user_id = id,
        role = meta.role
    })
end

Returns: Actor|nil

scope

Returns the current security scope from the execution context.

local scope = security.scope()
if scope then
    local policies = scope:policies()
    for _, policy in ipairs(policies) do
        print("Active policy:", policy:id())
    end
end

Returns: Scope|nil

can

Checks if the current context allows an action on a resource.

-- Check read permission
if not security.can("read", "user:" .. user_id) then
    return nil, errors.new("PERMISSION_DENIED", "Cannot read user data")
end

-- Check write permission
if not security.can("write", "order:" .. order_id) then
    return nil, errors.new("PERMISSION_DENIED", "Cannot modify order")
end

-- Check with metadata
local allowed = security.can("delete", "document:" .. doc_id, {
    owner_id = doc.owner_id,
    department = doc.department
})
Parameter Type Description
action string Action to check
resource string Resource identifier
meta table Additional metadata (optional)

Returns: boolean

new_actor

Creates a new actor with ID and metadata.

-- Create user actor
local actor = security.new_actor("user:" .. user.id, {
    role = user.role,
    department = user.department,
    email = user.email
})

-- Create service actor
local service_actor = security.new_actor("service:payment-processor", {
    type = "service",
    version = "1.0.0"
})
Parameter Type Description
id string Unique actor identifier
meta table Metadata key-value pairs

Returns: Actor

new_scope

Creates a new custom scope.

-- Empty scope
local scope = security.new_scope()

-- Scope with policies
local read_policy = security.policy("app:read-only")
local scope = security.new_scope({read_policy})

-- Build scope incrementally
local scope = security.new_scope()
local policy1 = security.policy("app:read")
local policy2 = security.policy("app:write")
scope = scope:with(policy1):with(policy2)

Returns: Scope

policy

Retrieves a policy from the registry.

local policy, err = security.policy("app:admin-access")
if err then
    return nil, err
end

-- Evaluate policy
local result = policy:evaluate(actor, "delete", "user:123")
if result == "allow" then
    -- permitted
elseif result == "deny" then
    -- forbidden
else
    -- undefined, check other policies
end
Parameter Type Description
id string Policy ID "namespace:name"

Returns: Policy, error

named_scope

Retrieves a pre-defined policy group.

-- Get admin scope
local admin_scope, err = security.named_scope("app:admin")
if err then
    return nil, err
end

-- Use for elevated operations
local result = admin_scope:evaluate(actor, "delete", "user:123")
Parameter Type Description
id string Policy group ID

Returns: Scope, error

token_store

Acquires a token store for managing authentication tokens.

local store, err = security.token_store("app:tokens")
if err then
    return nil, err
end

-- Use store...
store:close()
Parameter Type Description
id string Token store ID "namespace:name"

Returns: TokenStore, error

Actor Methods

Method Returns Description
actor:id() string Actor identifier
actor:meta() table Actor metadata

Scope Methods

with / without

Add or remove policies from scope.

local scope = security.new_scope()

-- Add policy
local write_policy = security.policy("app:write")
scope = scope:with(write_policy)

-- Remove policy
scope = scope:without("app:read-only")

evaluate

Evaluate all policies in scope.

local result = scope:evaluate(actor, "read", "document:123")
-- "allow", "deny", or "undefined"

if result ~= "allow" then
    return nil, errors.new("PERMISSION_DENIED", "Access denied")
end

contains

Check if scope contains a policy.

if scope:contains("app:admin") then
    show_admin_features()
end

policies

Returns all policies in scope.

local policies = scope:policies()
for _, policy in ipairs(policies) do
    print(policy:id())
end

Returns: Policy[]

Policy Methods

Method Returns Description
policy:id() string Policy identifier
policy:evaluate(actor, action, resource, meta?) string "allow", "deny", or "undefined"

TokenStore Methods

create

Create authentication token.

local actor = security.new_actor("user:123", {role = "user"})
local scope = security.named_scope("app:default")

local token, err = store:create(actor, scope, {
    expiration = "24h",  -- or milliseconds
    meta = {
        login_ip = request_ip,
        user_agent = user_agent
    }
})
Parameter Type Description
actor Actor Actor for the token
scope Scope Permissions scope
options.expiration string/number Duration string or ms
options.meta table Token metadata

Returns: string, error

validate

Validate token and get actor/scope.

local actor, scope, err = store:validate(token)
if err then
    return nil, errors.new("UNAUTHENTICATED", "Invalid token")
end

Returns: Actor, Scope, error

revoke

Invalidate a token.

local ok, err = store:revoke(token)

Returns: boolean, error

close

Release the token store resource.

store:close()

Returns: boolean

Permissions

Security operations are subject to security policy evaluation.

Security Actions

Action Resource Description
security.policy.get Policy ID Access policy definitions
security.policy_group.get Group ID Access named scopes
security.scope.create custom Create custom scopes
security.actor.create Actor ID Create actors
security.token_store.get Store ID Access token stores
security.token.validate Store ID Validate tokens
security.token.create Store ID Create tokens
security.token.revoke Store ID Revoke tokens

See Security Model for policy configuration.

Errors

Condition Kind Retryable
No context errors.INTERNAL no
Empty token store ID errors.INVALID no
Permission denied errors.INVALID no
Policy not found errors.INTERNAL no
Token store not found errors.INTERNAL no
Token store closed errors.INTERNAL no
Invalid expiration format errors.INVALID no
Token validation failed errors.INTERNAL no
local store, err = security.token_store("app:tokens")
if err then
    if errors.is(err, errors.INVALID) then
        print("Invalid request:", err:message())
    end
    return nil, err
end

See Error Handling for working with errors.

See Also