← Back to Blog

Credential Delegation in CrewAI: How Alter Handles Multi-Agent Hierarchies

CrewAI multi-agent credential management

When a CrewAI manager agent spawns a worker, should the worker get the same credentials? Most teams answer this wrong. The right model is scoped delegation — not credential sharing, not independent credentials, but something in between that the OAuth spec technically supports but most teams never implement.

The three wrong answers

Teams building multi-agent systems in CrewAI typically land on one of three credential models when they first think about the problem.

Model 1: Manager shares credentials with workers. The manager agent has a set of OAuth tokens. When it spawns a worker, it passes those tokens directly to the worker's context. Simple to implement. Terrible for security: now the worker has the same blast radius as the manager. If a worker is compromised or misbehaves, it has access to everything the manager can access — including resources that should be manager-only.

Model 2: Workers get independent credentials. Each worker is pre-registered with its own OAuth tokens before the run. The manager does not share credentials — each agent operates autonomously with its own identity. Better for isolation, worse for audit: the manager's instructions to workers are not reflected in the credential layer. When a worker makes an API call, the credential audit log shows the worker's identity but there is no record that the manager instructed it to do so. Attribution back to the originating manager is lost.

Model 3: No credential management — use the provider's app credential for everything. All agents in the crew use the same GitHub App installation or Slack bot token. Simple. No isolation whatsoever. Post-incident attribution is impossible. This is the "we'll fix it later" option that becomes a permanent fixture.

What scoped delegation actually means

The correct model is credential delegation with scope restriction: the manager agent holds the maximum-privilege credentials, workers receive delegated credentials that cover only what the specific task requires, and the chain of delegation is captured in the audit log.

In concrete terms: the manager has a policy that allows up to repo:write and issues:write. When it spawns a code review worker, it delegates a credential with a ceiling of repo:read and issues:read — the minimum the worker needs for that task. The worker cannot exceed those delegated scopes even if it tries. When the worker's task completes or times out, the delegated credential expires.

OAuth 2.0 supports this via token exchange (RFC 8693). The manager exchanges its existing token for a new token with narrower scope, and provides that narrower token to the worker. The original token remains at full scope, the delegated token is restricted, and both events are captured in the audit log with the exchange relationship recorded.

Why most teams do not implement token exchange

RFC 8693 token exchange is supported by a limited number of identity providers. AWS Security Token Service supports it natively. Okta supports it as an extension. GitHub, Slack, Google, and most other OAuth providers that AI agents commonly connect to do not support token exchange in the RFC 8693 sense.

This is the gap Alter fills. Instead of relying on provider-native token exchange, Alter's proxy implements the exchange pattern: the manager presents its policy-authorized token to Alter and requests a derived token for the worker with specified scope constraints. Alter issues the derived token from its own token pool for that provider, records the exchange relationship, and enforces the scope ceiling on every use of the derived token.

From the provider's perspective, both the manager and worker tokens are first-class tokens issued via Alter's registered OAuth app. From Alter's perspective, the derived token is linked to the exchange event and carries the delegation chain. The audit log shows: manager-agent issued exchange request for worker-agent, specifying scope X, resulting in token Y with TTL Z.

Implementing delegation in CrewAI

CrewAI's agent context system provides a natural place to implement delegation. Each agent in the crew has a context dictionary that is accessible during tool calls. You can store the delegated token reference in the context and retrieve it in tool implementations.

from crewai import Agent, Task, Crew
from alter import AlterClient

alter = AlterClient(
    agent_id=os.environ["ALTER_AGENT_ID"],
    agent_secret=os.environ["ALTER_AGENT_SECRET"],
)

# Manager agent requests a delegated credential for a worker
def delegate_to_worker(worker_role: str, task_context: str):
    scope_map = {
        "code_reviewer": {"github": ["repo:read", "pr:read", "issues:read"]},
        "issue_triager": {"github": ["issues:read", "issues:write"]},
        "notifier": {"slack": ["channels:read", "chat:write"]},
    }
    worker_scope = scope_map.get(worker_role, {})

    return alter.delegate(
        to_agent_role=worker_role,
        scope=worker_scope,
        ttl_seconds=1800,  # 30 min — enough for one task
        task_context=task_context,
        run_id=current_run_id,
    )

# Worker receives delegated token reference, not the raw token
worker_credential = delegate_to_worker(
    worker_role="code_reviewer",
    task_context="review-pr-4521"
)

The alter.delegate() call returns a credential reference, not the raw token. When the worker's tool code calls alter.get_token(credential_ref=worker_credential), Alter validates that the request comes from an agent registered as the specified role and issues the actual token. The token never appears in Python memory until the moment of use, which limits the exposure window for in-memory secret theft.

Audit trail for delegated credentials

The delegation chain appears in the Alter audit log as a linked set of events. For the example above, the log would show:

1. token.delegated — manager agent agt_manager requested delegation to code_reviewer role, scope [repo:read, pr:read, issues:read], TTL 1800s, task_context: review-pr-4521

2. token.minted — delegated token tok_worker_xyz issued to code_reviewer role under run_id run_abc, derived from manager's policy pol_manager-v2

3. token.used — tok_worker_xyz used to call GET /repos/{owner}/{repo}/pulls/4521 at 14:23:45

4. token.expired — tok_worker_xyz expired at 14:53:45 (TTL 1800s elapsed)

From this log, a security analyst can reconstruct: the manager gave the worker read-only PR access for 30 minutes, the worker used it to read PR 4521, and the credential expired on schedule. No manual cleanup required, no zombie credentials, attribution intact from manager instruction to specific API call.

Handling worker failures and cleanup

If a CrewAI worker fails mid-task, the delegated token should be revoked rather than left to expire naturally. Leaving tokens to expire is fine for normal completions — the TTL handles it. But a failed or hung worker has an uncertain state: it may retry, it may have partially completed, its environment may be compromised. Waiting for the TTL to expire is a 30-minute exposure window for uncertain state.

Wire the revocation call into your CrewAI error handler:

try:
    result = worker_agent.execute(task)
except Exception as e:
    # Worker failed — revoke the delegated credential immediately
    alter.revoke(credential_ref=worker_credential, reason=f"worker_failure: {str(e)}")
    raise
finally:
    # Always revoke on task completion, don't rely on TTL expiry
    alter.revoke(credential_ref=worker_credential, reason="task_completed")

Explicit revocation on completion is better than TTL-based expiry because it closes the window immediately rather than waiting for the TTL to elapse. A worker that completes a 5-minute task should not carry a valid credential for the remaining 25 minutes of the TTL. TTL is a backstop, not the primary cleanup mechanism.

Scope inheritance and scope ceiling

A delegated credential cannot exceed the scope of the credential it was delegated from. This is enforced by Alter at the policy level. If the manager's policy ceiling is repo:read, the worker cannot be delegated repo:write even if the delegation request asks for it. The delegation request is capped at the manager's ceiling.

This inheritance property means that the manager's policy defines the maximum blast radius for the entire crew. If the manager's policy is set correctly, no worker in the crew can exceed it regardless of what the worker code or the LLM generating the worker's tool calls requests. The scope ceiling propagates down the hierarchy automatically.

For teams managing large CrewAI deployments with many agent roles, this simplifies policy management: define the policy for each manager-level agent carefully, and the worker policies can be defined more broadly knowing that the manager's ceiling is the effective limit. The enforcement is at the delegation layer, not in each worker's individual policy.