Membership Service Reviewer's Guide
This guide helps evaluators trial the EmpowerNow Membership Service with a focus on AI authorization, user-bound identities, spending/budget controls, and graph-powered delegation. It provides business context, architecture, data/schema overview, key APIs, and a real-world case study with hands-on steps.
See reference: ../reference/schema-and-endpoints.md and Postman collection ../reference/postman/membership_review.postman_collection.json.
Executive introduction: what you’re reviewing and why it’s different
- What this is: an authoritative graph (FastAPI + Neo4j) for identities, delegations, capabilities, tenant/data scope, step-up hints, and identity-chaining eligibility. It exposes PIP endpoints to the PDP and is consulted by the IdP when issuing passports; Redis accelerates hot paths.
- ARIA profile and extensions: ARIA (Agent Relationship-based Identity & Authorization) defines how rich authorization context is carried and enforced. In our system, the IdP emits OIDC tokens with
aria_extensions(e.g.,budget,max_steps,schema_pins,call_id), the PDP returns an ARIA v1 context (constraints/obligations), and the Gateway/BFF enforces them. - Enterprise AI problems addressed:
- On-behalf-of execution: bind agents to specific users/services with explicit capabilities and lifetimes.
- Data scope and tenancy: constrain which tenants/rows/columns an agent can access for safe RAG.
- Prompt/content safety: enforce egress allowlists and schema pins via PDP constraints.
- Cost control: attach budget_usd and max_steps to delegations to bound spend and chain length.
- Downstream calls: express identity-chaining eligibility (audiences/scopes) for provider tokens.
- How it works (Authzen-aligned extensions implemented):
- PIP under
/api/v1/pip/membership:capabilities,delegations(includesbudget_usd,max_steps),data-scope,step-up,chain-eligibility. - Governance endpoints now available:
- CONTROLLED_BY:
PUT /api/v1/agent/agents/{agent_id}/control,GET /api/v1/agent/agents/{agent_id}/controllers,DELETE /api/v1/agent/agents/{agent_id}/control - Resource ownership:
POST /api/v1/resources/{resource_id}/owners,GET /api/v1/resources/{resource_id}/owners,DELETE /api/v1/resources/{resource_id}/owners - RTR authoring:
POST /api/v1/rtrs/assign,POST /api/v1/rtrs/assign-at
- CONTROLLED_BY:
- IdP passports embed
aria_extensions.budgetandaria_extensions.max_stepsderived from active delegations. - PDP composes decision + constraints; Gateway/BFF enforces egress/schema/data_scope/step_up/identity_chain.
- Graph storage:
DELEGATES_TOcarriesbudget_usdandmax_steps; delegations link toCapability; tenants attach to users; tools map to SaaS apps with required scopes.
- PIP under
- What’s unique vs typical IAM:
- Delegation-first, relationship-centric model purpose-built for AI agents (user-bound behavior).
- Cost and step limits on the delegation edge, not just coarse account quotas.
- Identity-chaining eligibility as a first-class API for safe provider-scoped tokens.
- Data-scope computed and returned in enforcement-ready form (e.g., SQL filter + tenant_ids).
- Reproducible evaluation: seeds, unit/integration tests, and Postman collection included.
- How to evaluate quickly:
- Seed Neo4j with
membership/seeds/pip_minimal.cypherand runmembership/scripts/seed_neo4j.py. - Exercise the PIP endpoints with the sample IDs (see examples below) via curl/Postman.
- Compare PDP constraints output with your enforcement layer to validate fit.
- Seed Neo4j with
MCP catalog & discovery (new)
Membership now models MCP assets and discovery metadata:
- Nodes:
MCPService,MCPResource,MCPPrompt, referenceMCPPolicyBinding - Edges:
PROVIDES_RESOURCE,OFFERS_PROMPT - Endpoints (base
/api/v1/mcp): register/list services and tools, and register discovered resources/prompts
ARIA at a glance (where it shows up):
- PDP: returns ARIA v1 rich context under
context.constraintsandcontext.obligations(AuthZEN request semantics). - Membership: PIP supplies the graph truth PDP needs (capabilities, data_scope, step_up, chain_eligibility).
- Gateway/BFF: enforces constraints (egress allowlist, schema pins, budget, step_up, identity_chain).
See also:
- PDP obligations processing and delegation provisioning →
/docs/services/pdp/backend/obligations-and-delegation - IdP PEP → PDP request shape (mappings, scopes, context) →
/docs/services/idp/backend/pep-pdp-request
Glossary (quick):
- ARIA: Agent Relationship-based Identity & Authorization; rich context carried in
aria_extensionsand PDP responses. - AuthZEN: authorization request semantics
{subject, action, resource, context}used by our PDP. - Actor (act) claim: standard OIDC claim indicating on‑behalf‑of principal for user‑bound agents.
- ID‑JAG: Identity Assertion JWT used as a JWT Bearer grant (RFC 7523) after Token Exchange (RFC 8693) per the Identity & Authorization Chaining profile.
Cross‑App Access (Identity & Authorization Chaining) – Membership’s role:
- Why it matters: eliminates per‑app interactive consent for AI tools while centralizing policy at the IdP.
- Membership tie‑in:
chain-eligibilityPIP returns allowed audiences/scopes for safe chaining; PDP consumes this to constrain downstream token issuance by resource providers.
Industry momentum & naming:
- Vendors (e.g., Okta) describe this profile as “Cross‑App Access.” Our implementation follows the same RFC pattern (8693 + 7523) with a dedicated Identity Assertion endpoint and Membership PIP
chain-eligibilityto bound audiences/scopes. - Names vary, but the flow is the same: IdP sits in the middle, issues a short‑lived assertion, resource app AS validates and mints its own token.
1. Why this matters: AI authorization in the enterprise
- Problem: AI agents operate “on behalf of” users and services, touching sensitive data, systems, and budgets.
- Risks addressed:
- Identity binding: ensure an agent is truly acting for a specific user/service pair.
- Delegation clarity: explicitly constrain what an agent can do (capabilities) and for how long.
- Data scope: enforce tenant/row/column restrictions for safe retrieval-augmented generation.
- Prompt/content security: prevent data exfiltration, schema drift, and over-permissive calls.
- Spend control: bound per-call, per-plan budgets and chain-token issuance.
- Outcome: auditable authorization decisions with graph truth and policy overlays, enabling safe AI adoption.
2. Architecture at a glance
- Membership Service (FastAPI + Neo4j): source of truth for identities, delegations, capabilities, and metadata.
- PDP (AuthZ engine): computes allow/deny with constraints; calls Membership PIP for data_scope, step_up, and identity_chain.
- IdP: issues passports with budget and max_steps based on active delegations.
- BFF/Gateway: enforces constraints (egress allowlist, schema pins, budget, data_scope) at runtime.
- Redis: caching for hot paths; Kafka (optional) for events and audit.
3. Neo4j schema (core nodes/edges)
- Nodes:
User(Identity),Agent(AI/service principal),Delegation,Capability,Tenant,Tool,SaaSApp. - Relationships:
(:User)-[:DELEGATES_TO]->(:Delegation)-[:BINDS]->(:Agent)(:Delegation)-[:HAS_CAPABILITY]->(:Capability)(:User)-[:BELONGS_TO]->(:Tenant)(:Agent)-[:CAN_INVOKE]->(:Tool)-[:REQUIRES]->(:SaaSApp {scopes: [...]})
- Indices/constraints: see
ServiceConfigs/membership/config/neo4j_indices.yaml.
Model invariants and lifecycles:
- Labels:
Identityis the root label;User/Agentare specializations;Account,Tenant,Tool,SaaSAppas typed nodes. - Uniqueness:
Identity.id,Agent.id,Tool.id,MCPService.id, andDELEGATES_TO.idare unique; seestorage/graph_modules/graph_schema.pyandstorage/graph_store.py. DELEGATES_TOlifecycle:status in {active, revoked, expired}; validity checked asd.expires_at IS NULL OR d.expires_at > datetime(); expiry sweeper marks overdue edges (APOC with fallback).
Common Cypher snippets:
// Active capabilities for a user-bound agent
MATCH (u:User {id:$user_id})-[:DELEGATES_TO]->(d:Delegation {status:'active'})-[:BINDS]->(a:Agent {id:$agent_id})
MATCH (d)-[:HAS_CAPABILITY]->(c:Capability)
RETURN collect(DISTINCT c.id) AS capabilities;
// Delegations with budget/steps
MATCH (u:User {id:$user_id})-[:DELEGATES_TO]->(d:Delegation)-[:BINDS]->(a:Agent {id:$agent_id})
RETURN d.id AS delegation_id, d.status, d.budget_usd, d.max_steps, d.expires_at;
// Tenants for data scope
MATCH (u:User {id:$sid})-[:BELONGS_TO]->(t:Tenant)
OPTIONAL MATCH (u)-[:ASSIGNED_TO]->(:Project)-[:OWNED_BY]->(tp:Tenant)
WITH collect(DISTINCT t.id) + collect(DISTINCT tp.id) AS tids
RETURN [x IN tids WHERE x IS NOT NULL] AS tenant_ids;
4. Key features mapped to business needs
- User-bound identities and delegation
- Why: bind an agent to a specific user; avoid “loose” service accounts.
- How:
DELEGATES_TOwith capabilities, status, time bounds, budget_usd, max_steps.
- Data scope and tenancy
- Why: ensure agents only query/see allowed tenants/rows/columns.
- How: derive
tenant_idsandrow_filter_sql(fail-closed to 1=0).
- Step-up hints
- Why: trigger MFA for sensitive actions.
- How: compute from user’s
mfa_levelor policy.
- Identity chaining (downstream API calls)
- Why: safely exchange the agent-user binding into provider-scoped tokens.
- How: return allowable audiences/scopes for a tool’s provider.
- Spend and budget controls
- Why: prevent runaway cost; enforce plan budgets and step limits.
- How: aggregate budget across delegations; IdP embeds
budget.initialandmax_stepsin JWT.
- Prompt/content security (via PDP + Gateway)
- Why: block secret leakage, unsafe egress, schema violations.
- How: PDP returns constraints (egress allowlist, schema pins, disallowed phrases) that gateway enforces.
5. PIP endpoints (Membership → PDP)
Base prefix: /api/v1/pip/membership
GET /capabilities?user_id=&agent_id=→{ capabilities: ["mcp:flights:search", ...] }- Example (seeded demo):
{ "capabilities": ["mcp:flights:book", "mcp:flights:search"] }
GET /delegations?user_id=&agent_id=&status=active→ list withbudget_usd,max_steps.- Pagination:
limit(1–500, default 50),offset(>=0). Server caps large responses. - Example (seeded demo):
[
{ "delegation_id": "delegation:demo1-to-agent1", "status": "active", "max_steps": 5, "budget_usd": 25.0, "expires_at": null }
]
GET /data-scope?subject_id=&resource_type=→{ tenant_ids: [...], row_filter_sql, column_mask }- Example (seeded demo):
{ "tenant_ids": ["tenant:acme"], "row_filter_sql": "tenant_id IN ('tenant:acme')", "column_mask": {} }
GET /step-up?subject_id=→{ mfa_required, level }- Example (seeded demo; default strong when mfa_level is none):
{ "mfa_required": true, "level": "strong" }
GET /chain-eligibility?user_id=&agent_id=&tool_id=→[ { audience, scopes } ]- Example (seeded demo):
[{ "audience": "api.flights.com", "scopes": ["flights.read", "flights.write"] }]
6. Core APIs (search and identity)
/api/v1/identity_nodes/search→ search bynode_type,search,system, optional filters; pagination.- Example request (seeded demo):
/api/v1/identity_nodes/search?node_type=Person&search=John&system=active_directory&limit=10&skip=0
- Returns: list of nodes; optional filters excluded when unset.
/api/v1/identity_nodes/search/with-metadata→ addstotal,has_more, and optionalrelationships.- Example response shape:
{
"nodes": [ { "id": "person1", "name": "John", "relationships": { } } ],
"total": 1,
"limit": 10,
"skip": 0,
"has_more": false,
"relationships": {}
}
/api/v1/identity_nodes/count→ count bynode_type/search/system.- Example query params are normalized in routes to exclude nulls.
All routes accept only non-null parameters; optional filters are excluded when unset.
Search semantics:
- Filters are ANDed.
- Substring matches are case-insensitive by default (implementation-specific in graph layer).
- Use
use_fulltext=true(metadata route) when relevance scoring and fuzzy matching are desirable.
Indexes and constraints (selection):
- From
ServiceConfigs/membership/config/neo4j_indices.yaml:pip_support:Tenant(id,name),MCPService(id,api_endpoint),Tool(id)identity_pk:Person(id),Account(id),Group(id), etc.identity_search:Identity(job_title,email,country,telephone)- Full-text: labels
Identity, Person, AIAgent, Group, Accounton propertiesname, display_name, email, job_title, country, telephone
7. Hands-on: trial setup
- Seed Neo4j with a minimal dataset:
python membership/scripts/seed_neo4j.py --uri bolt://localhost:7687 --user neo4j --password test- Or against the compose container:
bolt://localhost:7688, passwordpassword123.
- Verify seeds:
membership/scripts/quick_check.pyprints counts and sample relations.
- Run focused tests:
- PIP router unit/integration tests in
membership/tests(see README snippets). - Identity search unit tests (routes/services) should pass as of this guide.
- PIP router unit/integration tests in
8. Case study: Travel booking copilot
- Scenario: A travel copilot agent books flights “on behalf of” employees.
- Identities:
user:demo1belongs to tenanttenant:acme.- Agent
agent:svc-123:for:demo1bound by an active delegation with capabilitiesmcp:flights:search,mcp:flights:book, budget$25, max_steps5.
- Flow:
- IdP issues a token with
aria_extensions.budget={"unit":"usd","amount":25},aria_extensions.max_steps=5and schema pins. - PDP calls Membership PIP: verifies capability for
mcp:flights:search; returnsdata_scopeandstep_up(strong if needed); returnsidentity_chainforapi.flights.comwith scopes. - Gateway enforces egress allowlist, budget, schema pins; tool call proceeds.
- IdP issues a token with
- Why it matters:
- The agent is strictly constrained on actions, data, and cost.
- Auditability: all decisions logged with graph truth and constraints.
9. Observability & ops
- Health:
/healthendpoints from services. - Metrics: Prometheus counters/histograms for key operations.
- Logs: structured logs on request start/success/error with correlation IDs.
- Kafka (optional): delegation events for audit and analytics.
10. Review checklist
- Identity binding:
- Active
DELEGATES_TOedges exist with status/ttl/jkt as appropriate - Capabilities anchored on delegations
- Active
- Data scope:
-
data_scopereturns correct tenant_ids and fail-closed when unknown
-
- Policy integration:
- PDP includes constraints for egress/schema/prompt rules
- Budget/steps:
- Token includes
aria_extensions.budgetandaria_extensions.max_stepsper delegations
- Token includes
- Tool chaining:
-
chain-eligibilitylists correct audiences/scopes for target tool
-
- Search UX:
- Search endpoints perform as expected; metadata endpoints return
total/has_moreand optionalrelationships
- Search endpoints perform as expected; metadata endpoints return
11. Reference
- Config:
ServiceConfigs/membership/config/settings.yamlServiceConfigs/membership/config/neo4j_indices.yaml
- Seeds/tests:
membership/seeds/pip_minimal.cyphermembership/scripts/seed_neo4j.py,membership/scripts/quick_check.pymembership/tests/*
- Design docs:
mcp_gateway/docs/________newdesign10_membership.md- PDP and IdP design counterparts for constraints and passport flows
For questions or deep-dive sessions, contact the engineering team and reference decision logs with correlation IDs from your trial runs.
Appendix A: PIP evaluation sequence
Appendix B: PDP decision/constraints example
Appendix F: Query cookbook (with DDL)
- Capability check (user→agent): uses
DELEGATES_TOwithstatus='active'and validity window; indexed by relationship properties. Seestorage/graph_modules/graph_delegation.pyandstorage/graph_store.pyfor constraints and indexes. - Data scope:
MATCH (u {id:$sid}) OPTIONAL MATCH (u)-[:BELONGS_TO]->(t:Tenant) OPTIONAL MATCH (u)-[:ASSIGNED_TO]->(:Project)-[:OWNED_BY]->(tp:Tenant) ... LIMIT 1– backed byTenant(id)and full‑text indices for identity search. - Chain eligibility:
MATCH (t:Tool {id:$tool_id})<-[:PROVIDES]-(s:MCPService) RETURN s.api_endpoint, s.auth_config_metadata LIMIT 1– backed byTool(id)andMCPService(id).
Tip: Run EXPLAIN on these queries against the seeded dataset; indexes are declared in code (see graph_mcp.py, graph_agent.py, graph_store.py).
Appendix G: Audit and lineage
- Events: delegation add/update/revoke and relation changes emit Kafka events when configured; see
services/membership.py. - Logs:
structured_logaround delegation and identity operations with correlation IDs; use these to reconstruct decision trails.
Appendix H: PIP data contracts and error taxonomy
- Version: responses include stable shapes; future changes will add a
versionfield. Suggested consumer default is strict field validation. - Error taxonomy (minimum):
- 200 + empty/safe default when no relationship:
{capabilities: []},{tenant_ids: [], row_filter_sql: "1=0"},[]for chain eligibility. - 4xx for validation errors (missing params), 5xx for backend errors (Neo4j/Redis unavailable).
- 200 + empty/safe default when no relationship:
{
"decision": true,
"context": {
"constraints": {
"egress": {"allow": ["api.openai.com:443", "tools.example.com:443"]},
"schema_pins": {"tool:flights": {"schema_version": "1.0", "schema_hash": "sha256:..."}},
"data_scope": {"tenant_ids": ["tenant:acme"], "row_filter_sql": "tenant_id IN ('tenant:acme')", "column_mask": {}},
"step_up": {"mfa_required": false, "level": "strong"},
"identity_chain": {"allowed_audiences": ["api.flights.com"], "allowed_scopes": ["flights.read","flights.write"], "max_token_ttl_seconds": 300}
},
"obligations": [{"id": "emit_receipt"}],
"decision_id": "..."
}
}
Appendix C: Identity chain example
Eligibility response:
[{"audience": "api.flights.com", "scopes": ["flights.read", "flights.write"]}]
Provider token request (conceptual):
POST /oauth/token
audience=api.flights.com&scope=flights.read%20flights.write&act_sub=agent:svc-123:for:...
Appendix D: Quickstart (curl) and Postman
curl examples:
# Capabilities
curl -s "https://membership.ocg.labs.empowernow.ai/api/v1/pip/membership/capabilities?user_id=user:demo1&agent_id=agent:svc-123:for:demo1"
# Data scope
curl -s "https://membership.ocg.labs.empowernow.ai/api/v1/pip/membership/data-scope?subject_id=user:demo1&resource_type=record"
# Search
curl -s "https://membership.ocg.labs.empowernow.ai/api/v1/identity_nodes/search?node_type=Person&search=John&system=active_directory&limit=10&skip=0"
Postman: import the collection from docs/services/membership/reference/postman/membership_review.postman_collection.json and set the baseUrl variable.
Appendix E: ReBAC walk‑through and policy sketch
ReBAC example: “John can approve expense in cost center CC-100”
Graph walk (conceptual):
Cypher check (illustrative):
MATCH (u:User {id:$user_id})-[:MEMBER_OF]->(:Group)-[:ASSIGNED_TO]->(br:BusinessRole {name:'ExpenseApprover'})
MATCH (br)-[:SCOPED_TO]->(cc:CostCenter {id:$cost_center_id})
MATCH (r:ExpenseReport {id:$report_id})-[:BELONGS_TO]->(cc)
RETURN r.id AS report_id LIMIT 1;
Policy sketch (conceptual) – PDP combines graph truth with authored conditions:
allow:
when: action == "expense.approve"
and: constraints.capabilities contains "expense:approve"
and: data_scope.tenant_ids contains resource.tenant_id
and: rebac.expense_approver_for(resource.cost_center_id) == true
obligations:
- id: emit_receipt
- id: tee_analytics
Notes:
- The Membership PIP can expose helper endpoints to answer specific relationship predicates, but most trials use PDP orchestration to merge constraints (capabilities + data_scope) with authored rules.
- Keep ReBAC queries narrow and index-backed (CostCenter.id, BusinessRole.name).