Create Delegated Automation Access
A signed-in KDCube user can now mint a short-lived bearer token for their own automation, scoped server-side by
resource_grants (resource → grants[]) plus selected operations. The token is only a handle
— the guard loads the server-side grant record and enforces resource-grant matching per call. Grants never union
across resources: {A: read, B: write} cannot use write on A.
A user can now create a bounded credential for their own automation. That sounds small, but it closes a gap: KDCube could let an external app negotiate access through OAuth/MCP, and let a hosted application protect a managed surface — but a user also needs a simpler path: "give this script, agent, CI job, or DevOps task a token that represents me, only for the resources and operations I choose."
That is Delegated Access in Connection Hub — the Delegated by KDCube tab.
signed-in KDCube user
|
| creates delegated access in Connection Hub
v
short-lived bearer token for automation
|
| Authorization: Bearer ...
v
managed REST / MCP / platform surface
|
| guard checks resource_grants + selected operation
v
route runs as the grantor user, with delegated provenance
The token is not a password and not a full login session. It is a delegated credential: revocable, scoped, tied to the approving platform user, and checked server-side on every call.
The whole idea in one line
Connection Hub lets a signed-in user mint a short-lived bearer token for automation, where the real authority is
stored server-side as resource_grants: resource → grants[], plus selected operations. We
do not store separate resources and grants lists — that would let the meaning
drift. We store one shape:
{
"resource_grants": {
"https://runtime.example/A": ["records:read"],
"https://runtime.example/B": ["records:write"]
},
"operations": ["records_search", "records_export"]
}
For a request to A, the guard uses grants from the matching A entry. It does not take
a global union of every grant on the token. A token with {A: read, B: write} cannot use
write on A.
What this is, and what it is not
Delegated Access is a KDCube-issued credential for entering KDCube. It is not the same as a user connecting their Gmail, Slack, iCloud, or LinkedIn account. Those are user-connected provider integrations — KDCube stores provider credentials so an application can call an external service for the user. Delegated Access points the other way: the user gives an automation a bounded way to call KDCube.
user-connected provider account
KDCube -> external provider API
example: KDCube reads the user's Gmail
delegated automation access
automation -> KDCube API
example: a script publishes news, exports conversations, or calls an app API
It is also different from OAuth/MCP connector setup such as Claude. Claude can negotiate with KDCube as a client and receive a delegated credential after a consent flow. Delegated Access is the manual, user-facing version: the user opens Connection Hub and creates a token directly.
The user flow
The UI should feel like selecting surfaces, not assembling OAuth internals:
Create automation access
Name: nightly news publisher
Resources:
[x] News operations API
[x] publish article news:write
[ ] rebuild all indexes news:admin
[x] KDCube conversations MCP
[x] export conversations conversations:read
[ ] All platform and application APIs
admin-only kdcube:role:super-admin
After creation, the UI shows the raw token once (Authorization: Bearer kst1...).
The Granted access list then shows metadata only: name, origin, approval/creation time, expiry,
resource_grants, selected operations, token last-four, and revoke.
One registry for manual tokens and OAuth connects
Granted access lists more than the tokens created in this form. When an external client (for example an MCP client such as Claude) connects through the KDCube OAuth consent flow, that approval registers in the same per-user registry:
manual token badge: manual token Created <time>
OAuth connect badge: connected app Approved <time>
OAuth rows keep one row per (user, client, resource) — reconsent widens the same row and keeps the
first-approval time. The label is the client's own registration name, so users see "Claude", not a
dcr-... id. The row expires with the connection's refresh-token lifetime, so a connection that can
no longer mint tokens disappears on its own.
Revoke is immediate for both kinds. A manual token's bound session is logged out. An OAuth connect loses its refresh token (no new access tokens) and its current access-grant binding — the bearer in flight is rejected by the guards.
The runtime flow
The automation does not become the browser user. It becomes a delegated client actor whose grantor is the signed-in platform user who approved the token.
automation
| Authorization: Bearer kst1...
v
KDCube request-auth surface
| hash token -> load server-side grant record
| check resource_grants against current URL/resource
| check selected operation/tool
| project grantor as platform UserSession
v
route handler / MCP tool / application operation
| sees platform user context + delegated provenance
v
product code runs
The route receives a projected platform context, so application code can use the normal user/session path rather than parsing tokens:
UserSession.user_id -> grantor platform user id
UserSession.user_type -> derived from projected grantor roles
UserSession.identity_authority -> delegated credential provenance:
delegate actor, grantor, authority,
resource_grants, operations, expiry
Product code should treat the request as delegated platform-user context. It should not parse the bearer token or infer the grantor itself.
The data model
The important persisted shape is resource_grants. The bearer token is only a handle — runtime
guards read the server-side record by token hash.
{
"label": "nightly news publisher",
"grantor_user_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"delegate_user_id": "automation:7d9f...",
"authority_id": "delegated_client",
"resource_grants": {
"*/api/integrations/bundles/*/*/news@2026-05-20-12-05/operations/publish*": ["news:write"],
"*/api/integrations/bundles/*/*/kdcube-services@1-0/public/mcp/conversations*": ["conversations:read"]
},
"operations": ["news_publish", "conversations_export"],
"expires_at": "2026-07-06T12:00:00Z"
}
- Resource and grant cannot drift apart — grants live inside
resource_grants[resource]. - Revocation is immediate — the guard must load the server-side record.
- Wildcards are explicit resource entries —
"*"is just another key inresource_grants.
Admin all-resource automation
Sometimes an administrator wants a token for DevOps automation: deploy, publish, sync descriptors, or call any
platform/application API. That is not a separate synthetic grant — the platform admin role
(kdcube:role:super-admin) is already a grant. So the all-resource token is:
{ "resource_grants": { "*": ["kdcube:role:super-admin"] } }
resource: "*" is deliberately special: it is visible only to users who can delegate
kdcube:role:super-admin; it means all platform and application APIs; it still uses the same guard
path; the request is projected as the grantor admin user; and the delegated actor is still recorded for
provenance. If a token mixes entries, the * entry still matches every resource:
{
"resource_grants": {
"*": ["kdcube:role:super-admin"],
"https://runtime.example/B": ["records:read"]
}
}
# the * entry matches B too — admin authority on B, because it is a real matching entry
* that legitimately matches B.Configuration: Connection Hub owns the registry
The catalog of delegable grants and protected resources belongs in Connection Hub configuration. Applications may expose managed surfaces, but Connection Hub owns the registry a user sees when creating delegated access.
bundles:
items:
- id: connection-hub@1-0
config:
connections:
delegated_credentials:
oauth:
enabled: true
capabilities:
- { grant: kdcube:role:super-admin, label: Use all platform and application APIs,
delegable_roles: [kdcube:role:super-admin] }
- { grant: news:write, label: Publish news,
delegable_roles: [kdcube:role:super-admin] }
- { grant: conversations:read, label: Read conversations,
delegable_roles: [kdcube:role:super-admin] }
resources:
- resource: "*"
label: All platform and application APIs
admin_only: true
grants: [kdcube:role:super-admin]
- resource: "*/api/integrations/bundles/*/*/news@2026-05-20-12-05/operations/publish*"
label: News publishing API
grants: [news:write]
operations:
news_publish: { label: Publish news content, grants: [news:write] }
- resource: "*/api/integrations/bundles/*/*/kdcube-services@1-0/public/mcp/conversations*"
label: KDCube conversations MCP
grants: [conversations:read]
operations:
conversations_export: { label: Export conversations, grants: [conversations:read] }
This is the same registry used by OAuth delegated credentials and managed MCP. Delegated Access adds a user-created token path on top of that registry.
Configuration: an application REST operation
For an application REST operation, the application declares that its operation is managed by Connection Hub. The application does not parse delegated tokens.
@api(method="POST", alias="news_publish", route="operations")
async def news_publish(self, **params):
# Product code runs only after the managed REST guard accepts the request.
...
bundles:
items:
- id: news@2026-05-20-12-05
config:
surfaces:
as_provider:
api:
operations:
news_publish:
POST:
auth:
mode: managed
authority_id: delegated_client
selected_operation_grants: true
operations:
news_publish: { label: Publish news content, grants: [news:write] }
selected_operation_grants: true means the operation must be present in the server-side delegated
credential record. MCP calls this protocol-level object a tool; REST calls it an operation. The
Connection Hub model is resource and operation based.
Configuration: a platform REST resource
Some APIs are not application operations — they are platform APIs. Connection Hub still owns the resource catalog, and the shared request-auth surface validates the bearer before the normal route handler runs.
connections:
delegated_credentials:
oauth:
capabilities:
- { grant: devops:deploy, label: Deploy runtime, delegable_roles: [kdcube:role:super-admin] }
resources:
- resource: "*/api/platform/admin/redeploy*"
label: Platform redeploy API
operations:
platform_admin_redeploy: { label: Redeploy runtime, grants: [devops:deploy] }
If the URL is not configured as a Connection Hub delegated resource, the bearer token is not treated as magic — normal platform authentication rules apply.
How automation calls it
The call is ordinary HTTP with a bearer token — no browser cookie needed:
curl -sS \
-H "Authorization: Bearer ${KDCUBE_DELEGATED_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"limit": 5}' \
"https://runtime.example/api/integrations/bundles/demo/demo/news@2026-05-20-12-05/operations/publish"
The same token can call MCP if its resource_grants and selected operations cover that MCP
resource:
POST /api/integrations/bundles/<tenant>/<project>/kdcube-services@1-0/public/mcp/conversations
Authorization: Bearer kst1...
tools/call conversations_export
Failure modes
The useful failures are explicit:
401 unauthorized
no bearer token, expired token, revoked token, or unknown token
403 forbidden
resource_grants has no matching resource entry
matching resource entry lacks the required grant
selected operation/tool is absent
grantor can no longer delegate the grant
The important security rule — grant checks use only the matching resource entry, never a union of all grants on the token:
resource_grants: A: [read] B: [write]
request to A with write -> forbidden
What to test
- Sign in as a regular user; open Connection Hub → Delegated by KDCube.
- Confirm all-resource admin access is not visible.
- Create a token for one concrete resource; call it with
Authorization: Bearer .... - Call a different resource and confirm it fails; revoke the token and confirm the same call now fails.
- Connect an external MCP client through the OAuth consent flow; confirm it appears under Granted access as a connected app; revoke it and confirm the client's next call is rejected.
- Sign in as an admin; create an all-resource token with
kdcube:role:super-admin; call an admin-only API. - Confirm logs show delegated projection and the automation actor.
# REST
[connection-hub.oauth.rest_guard] accepted ...
Managed REST runtime projection applied ...
# MCP
[connection-hub.oauth.mcp_guard] accepted ...
Managed MCP runtime projection applied ...
Why this belongs in Connection Hub
This is not a one-off token feature — it is another way to create a delegation edge. Connection Hub already owns who the platform user is, which grants they may delegate, which resources/operations are delegable, where durable consent and delegated credentials are stored, and how the runtime projects actor, grantor, and economics identity. Delegated Access reuses that infrastructure; the application owns product code, Connection Hub owns the boundary.
Related publications
resource_grants and the selected operation/tool before product code.