Demo: RFC 8693 OBO token exchange with HBAC
Location: contrib/demo/obo/
Demonstrates ahdapa’s full RFC 8693 On-Behalf-Of (OBO) token exchange flow
with HBAC policy enforcement, actor token validation, act claim chains,
and delegation target guards. Runs on a single node over plain HTTP with
SQLite and static users — no Kerberos, LDAP, or TLS setup required.
What it shows
-
Basic OBO with
actclaim chain — A pipeline agent performs token exchange on behalf of a frontend service. The issued JWT carries anactclaim withsubset to the agent’sclient_idandworkload_typeset from the agent’s registered client profile. -
Delegation target guard — The HBAC rule restricts which Kerberos service principals the agent may delegate to (
delegation_targets). A request with the matchingtarget_serviceis allowed; a request with a non-matching SPN is denied withaccess_denied. -
OBO actor gate — Clients must have
allow_token_exchange_actor = trueto supply anactor_token. A rogue service without this flag receivesaccess_deniedimmediately, before HBAC evaluation. -
Three-way scope narrowing — The granted scope is the intersection of the requested scopes, the subject token’s scopes, and the acting client’s registered scopes. Requesting a scope the agent is not registered for yields
invalid_scope. -
HBAC client-axis deny — The HBAC rule’s
clientslist includes only the pipeline agent. A service that omitsactor_tokenis still blocked by HBAC when it attempts token exchange as a different client.
Prerequisites
| Tool | Purpose |
|---|---|
python3 | obo-hbac-demo.py; stdlib only, no extra packages needed |
ahdapa | In $PATH, or built automatically by run.sh |
Running
# Non-interactive: exits 0 on pass, 1 on failure
contrib/demo/obo/run.sh
# Interactive: keeps node running until Ctrl-C
contrib/demo/obo/run.sh --interactive
Run the Python script manually against an already-running node:
python3 contrib/demo/obo/obo-hbac-demo.py \
--base-url http://127.0.0.1:8080 \
--admin-user alice \
--admin-password alice123
Files
| File | Description |
|---|---|
node.toml | Single-node config: HTTP, SQLite, static users |
users.toml | Static user list (alice, admin) |
run.sh | Demo / CI script; starts ahdapa, runs scenarios, cleans up |
obo-hbac-demo.py | Python script that exercises all five scenarios |
Expected output
PASS scenario 1: basic OBO — act claim present and workload_type correct
PASS scenario 2: delegation target allowed (host/backend.example.com)
PASS scenario 3: delegation target denied (host/untrusted.example.com)
PASS scenario 4: actor gate — rogue service rejected (access_denied)
PASS scenario 5: scope narrowing — invalid_scope for unregistered scope
Key configuration concepts demonstrated
allow_token_exchange_actor must be true on a client for it to supply
actor_token. This is a client-level gate independent of HBAC.
workload_type is set on the client registration and embedded in
act.workload_type in OBO tokens issued when that client is the actor.
delegation_targets on an HBAC rule is a list of Kerberos SPNs the rule
permits as target_service. Only rules that already match user, client, scope,
and network axes are considered for the delegation check.
delegation_target_category = true on an HBAC rule is a wildcard: any
target_service value is permitted by that rule.
mfa_bypass = true is required for M2M rules. DWRegister (the CRDT
type backing mfa_bypass) defaults to false when no tags have been recorded.
A rule without an explicit mfa_bypass=true returns mfa_required=true from
HBAC evaluation, and the token exchange handler rejects the request because OBO
and CC flows carry no AMR.
HBAC rule structure
The demo creates two HBAC rules rather than one:
demo-cc-base — covers all clients for plain client credentials:
client_category=all, user_category=all, scope_category=all,
mfa_bypass=true, delegation_targets=["_cc_only"]. The _cc_only sentinel
prevents this rule from granting OBO delegation to any real service principal:
when a token exchange carries a real target_service, the delegation check
filters this rule out.
demo-obo-policy — explicit OBO grant for the agent:
clients=[agent_id], user_category=all, scope_category=all,
mfa_bypass=true, delegation_targets=["host/correct-server.example.com"].
Scenario 5 supplies target_service="host/correct-server.example.com" so
the delegation axis is evaluated. The _cc_only sentinel rule is excluded in
the delegation phase, and only the OBO rule satisfies the request.