assay.ory.kratos
Ory Kratos identity management. Self-service login, registration, recovery and settings flows,
identity CRUD via the admin API, session introspection (whoami), and identity schemas. Client:
kratos.client({public_url="...", admin_url="..."}).
Sessions (c.sessions):
c.sessions:whoami(cookie_or_token)→ session|nil — Introspect a session (nil on 401)c.sessions:list(identity_id)→ [session] — Admin: list sessions for an identityc.sessions:revoke(identity_id)→ nil — Admin: revoke all sessions for an identity
Flows (c.flows):
c.flows:create_login(opts?)→ flow — Initialize a login flow (browser or api)c.flows:get_login(flow_id, cookie?)→ flow — Fetch an existing login flowc.flows:submit_login(flow_id, payload, cookie?)→{session, session_token?}— Submit login flowc.flows:create_registration(opts?)→ flow — Initialize a registration flowc.flows:get_registration(flow_id, cookie?)→ flow — Fetch a registration flowc.flows:submit_registration(flow_id, payload, cookie?)→{identity, session?}— Submit registration flowc.flows:create_recovery(opts?)→ flow — Initialize a recovery flowc.flows:get_recovery(flow_id, cookie?)→ flow — Fetch a recovery flowc.flows:submit_recovery(flow_id, payload, cookie?)→ flow — Submit recovery flowc.flows:create_settings(cookie)→ flow — Initialize a settings flowc.flows:get_settings(flow_id, cookie?)→ flow — Fetch a settings flowc.flows:submit_settings(flow_id, payload, cookie?)→ flow — Submit settings flow
Identities (c.identities):
c.identities:list(opts?)→ [identity] — Admin: list identitiesc.identities:get(id)→ identity|nil — Admin: get identity by IDc.identities:create(payload)→ identity — Admin: create identityc.identities:update(id, payload)→ identity — Admin: update identityc.identities:delete(id)→ nil — Admin: delete identity
Schemas (c.schemas):
c.schemas:list()→ [schema] — List identity schemasc.schemas:get(id)→ schema|nil — Get schema by ID
Example:
local kratos = require("assay.ory.kratos")
local c = kratos.client({
public_url = "http://kratos-public:4433",
admin_url = "http://kratos-admin:4434",
})
local session = c.sessions:whoami(cookie)
log.info("Logged in as: " .. session.identity.traits.email)
assay.ory.hydra
Ory Hydra OAuth2 and OpenID Connect server. OAuth2 client CRUD via the admin API, authorize URL
builder, token exchange, accept/reject login and consent challenges, introspection, JWK endpoint,
and OIDC discovery. Client: hydra.client({public_url="...", admin_url="..."}).
Clients (c.clients):
c.clients:list(opts?)→ [client] — Admin: list OAuth2 clientsc.clients:get(id)→ client|nil — Admin: get client by IDc.clients:create(payload)→ client — Admin: create OAuth2 clientc.clients:update(id, payload)→ client — Admin: update OAuth2 clientc.clients:delete(id)→ nil — Admin: delete OAuth2 client
OAuth2 (c.oauth2):
c.oauth2:authorize_url(client_id, opts)→ string — Build an authorization URLc.oauth2:exchange_code(opts)→{access_token, id_token?, refresh_token?, expires_in}— Exchange code for tokensc.oauth2:refresh_token(client_id, client_secret, refresh_token)→ tokens — Refresh an access tokenc.oauth2:introspect(token)→{active, sub, scope, ...}— Admin introspectionc.oauth2:revoke_token(client_id, client_secret, token)→ nil — Revoke a token
Login challenges (c.login):
c.login:get(challenge)→{challenge, subject, client, ...}— Fetch a pending login challengec.login:accept(challenge, subject, opts?)→{redirect_to}— Accept a login challengec.login:reject(challenge, error?)→{redirect_to}— Reject a login challenge
Consent challenges (c.consent):
c.consent:get(challenge)→{challenge, subject, requested_scope, ...}— Fetch a pending consent challengec.consent:accept(challenge, opts)→{redirect_to}— Accept a consent challengec.consent:reject(challenge, error?)→{redirect_to}— Reject a consent challenge
Logout challenges (c.logout):
c.logout:get(challenge)→{request_url, rp_initiated, sid, subject, client}— Fetch a pending logout challengec.logout:accept(challenge)→{redirect_to}— Accept a logout challengec.logout:reject(challenge)→ nil — Reject a logout challenge
Discovery (c.discovery):
c.discovery:openid_config()→{issuer, authorization_endpoint, ...}— OIDC discovery documentc.discovery:jwks()→{keys}— JSON Web Key Set
Example:
local hydra = require("assay.ory.hydra")
local c = hydra.client({
public_url = "https://hydra.example.com",
admin_url = "http://hydra-admin:4445",
})
local client = c.clients:create({
client_name = "my-app",
grant_types = { "authorization_code", "refresh_token" },
redirect_uris = { "https://app.example.com/callback" },
})
assay.ory.keto
Ory Keto relationship-based access control (Zanzibar-style ReBAC). Relation-tuple CRUD, permission
checks, role/group membership queries, and the expand API. Client:
keto.client(read_url, {write_url="..."}).
Tuples (c.tuples):
c.tuples:list(query)→{relation_tuples, next_page_token}— List tuples matching query filtersc.tuples:create(tuple)→ nil — Create a relation tuple{namespace, object, relation, subject_id|subject_set}c.tuples:delete(tuple)→ nil — Delete a relation tuplec.tuples:delete_all(filters)→ nil — Delete all matching relation tuples
Permissions (c.permissions):
c.permissions:check(namespace, object, relation, subject)→ bool — Check if a relation tuple allows accessc.permissions:check({namespace, object, relation, subject_id})→ bool — Check (table form)c.permissions:batch_check(tuples)→ [bool] — Check multiple tuples in one callc.permissions:expand(namespace, object, relation, depth?)→ tree — Expand a subject tree (Zanzibar expand)
Roles (c.roles):
c.roles:user_roles(user_id, namespace?)→ [{object, relation}] — Get all role memberships for a userc.roles:has_any(user_id, role_objects, namespace?)→ bool — Check if a user has any of the given roles
Example:
local keto = require("assay.ory.keto")
local c = keto.client("http://keto-read:4466", {
write_url = "http://keto-write:4467",
})
c.tuples:create({
namespace = "apps", object = "cc", relation = "admin",
subject_id = "user:alice",
})
assert(c.permissions:check("apps", "cc", "admin", "user:alice"))
assay.ory.rbac
Capability-based RBAC engine layered on top of Ory Keto. Define a policy once (role → capability
set) and get user lookups, capability checks, and membership management for free. Users can hold
multiple roles and the effective capability set is the union, so separation of duties is enforceable
at the authorization layer (an approver role can have approve without also getting trigger,
even if listed above an operator role with trigger).
Policy: rbac.policy({namespace, keto, roles, default_role?}). namespace filters Keto tuples
(e.g. "command-center"); keto is a Keto client; roles maps role names to
{rank, capabilities, label?, description?}; default_role is the role assumed for users with no
memberships.
Users (p.users):
p.users:roles(user_id)→{role}— held roles, sorted by rank descendingp.users:primary_role(user_id)→ role — highest-ranked, for compact UI badgesp.users:capabilities(user_id)→{cap=true,...}— union over all held roles, falls back todefault_rolecaps when emptyp.users:has_capability(user_id, cap)→ bool — single capability check
Members (p.members):
p.members:add(user_id, role)— idempotent membership add (no-op if already a member)p.members:remove(user_id, role)— membership remove (swallows 404)p.members:list(role)→{user_id}— direct members of a rolep.members:list_all()→{[role]={user_id,...}}— full snapshotp.members:reset(role)— delete all members of a role (for bootstrap/seed scripts)
Policy (p.policy):
p.policy:roles()→[role_name]— all configured role names, highest rank firstp.policy:get(role_name)→{rank, capabilities}— role metadata from the policy definition
Middleware (p.middleware):
p.middleware:require_capability(cap, handler)→ handler —http.servemiddleware that 403s callers withoutcap
Example:
local keto = require("assay.ory.keto")
local rbac = require("assay.ory.rbac")
local kc = keto.client("http://keto-read:4466", { write_url = "http://keto-write:4467" })
local policy = rbac.policy({
namespace = "command-center",
keto = kc,
default_role = "viewer",
roles = {
owner = { rank = 5, capabilities = { "manage_roles", "approve", "trigger", "view" } },
admin = { rank = 4, capabilities = { "manage_roles", "approve", "trigger", "view" } },
approver = { rank = 3, capabilities = { "approve", "view" } },
operator = { rank = 2, capabilities = { "trigger", "view" } },
viewer = { rank = 1, capabilities = { "view" } },
},
})
policy.members:add("user:alice", "approver")
assert(policy.users:has_capability("user:alice", "approve"))
assert(not policy.users:has_capability("user:alice", "trigger"))
assay.ory
Convenience wrapper re-exporting assay.ory.kratos, assay.ory.hydra, assay.ory.keto, and
assay.ory.rbac, with ory.connect(opts) to build all three Ory clients in a single call.
M.kratos— re-export ofassay.ory.kratosM.hydra— re-export ofassay.ory.hydraM.keto— re-export ofassay.ory.ketoM.rbac— re-export ofassay.ory.rbacM.connect(opts)→{kratos, hydra, keto}— Build all three clients.opts:{kratos_public, kratos_admin, hydra_public, hydra_admin, keto_read, keto_write}
Example:
local ory = require("assay.ory")
local o = ory.connect({
kratos_public = "http://kratos-public:4433",
kratos_admin = "http://kratos-admin:4434",
hydra_public = "https://hydra.example.com",
hydra_admin = "http://hydra-admin:4445",
keto_read = "http://keto-read:4466",
keto_write = "http://keto-write:4467",
})
local allowed = o.keto.permissions:check("apps", "cc", "admin", "user:alice")