> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usefused.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication and credentials

> Authenticate callers and resolve downstream credentials without exposing secrets.

Harnest separates caller identity from downstream credentials.

| Concern            | Type                | Purpose                                      |
| ------------------ | ------------------- | -------------------------------------------- |
| Caller identity    | `AuthPrincipal`     | Stable user ID and validated claims          |
| Incoming secret    | `Credential`        | Wrapped token accepted by your auth policy   |
| Downstream request | `CredentialRequest` | Audience, scopes, and invocation context     |
| Downstream secret  | `Credential`        | Value revealed only by trusted outbound code |

```text theme={null}
request → authenticate → principal → credential provider → outbound request
```

## Authenticate a request

An authentication listener reads connection metadata and returns one principal. It never receives the request body.

```python theme={null}
# extensions/authentication.py
from harnest import Credential, lifecycle
from harnest.runtime_auth import AuthPrincipal, AuthenticationError
from harnest.lib.identity import verify_browser_token


@lifecycle.authenticate
async def authenticate(connection, principal):
    if principal is not None:
        return None

    token = connection.headers.get("authorization")
    claims = await verify_browser_token(token)
    if claims is None:
        raise AuthenticationError()

    return AuthPrincipal(
        user_id=claims.subject,
        claims={"tenant_id": claims.tenant_id},
        credentials={"browser": Credential(token)},
    )
```

| Principal property | Put here                        |
| ------------------ | ------------------------------- |
| `user_id`          | Stable authenticated identity   |
| `claims`           | Non-secret authorization facts  |
| `credentials`      | Wrapped tokens or signed values |

Listeners run in order. A later listener can enrich the principal but cannot change an established `user_id`.

## Resolve a downstream credential

Declare one optional credential provider at the root:

```python theme={null}
# extensions/credentials.py
from harnest import CredentialProvider, lifecycle
from harnest.lib.identity import exchange_for_engine


class EngineCredentials(CredentialProvider):
    async def resolve(self, request):
        incoming = request.principal.credentials["browser"]
        return await exchange_for_engine(
            incoming,
            audience=request.audience,
            scopes=request.scopes,
        )


@lifecycle.credential_provider
def credential_provider():
    return EngineCredentials()
```

| Request property | Meaning                         |
| ---------------- | ------------------------------- |
| `principal`      | Authenticated caller            |
| `audience`       | Target service                  |
| `scopes`         | Requested permissions           |
| `framework`      | Active ADK or LangGraph runtime |
| `agent_name`     | Current agent                   |
| `invocation_id`  | Current run                     |
| `session_id`     | Current session                 |

Your provider decides whether to forward an incoming credential or exchange it for a narrower one. Harnest does not guess that policy.

## Use a credential

Trusted tools, graph nodes, and `lib/` functions resolve credentials inside an active invocation:

```python theme={null}
from harnest import context


async def execute(payload):
    credential = await context.credentials.resolve(
        "threadify-engine",
        scopes=("engine:execute",),
    )
    return await engine.execute(
        payload,
        headers={"Authorization": credential.reveal()},
    )
```

<Warning>
  Never accept an audience or token as a model-generated tool argument. Trusted code must choose the audience and scopes.
</Warning>

## Secret boundary

| Harnest does                                                              | Your code must do                                            |
| ------------------------------------------------------------------------- | ------------------------------------------------------------ |
| Keeps credentials out of prompts, sessions, checkpoints, events, and logs | Reveal a credential only while building the outbound request |
| Redacts credential representations and provider errors                    | Redact authorization headers in HTTP client logs             |
| Revokes private bindings after the invocation                             | Resolve a fresh credential for queued work                   |
| Scopes access to the current invocation                                   | Store authorization intent, not resolved secrets             |

After `Credential.reveal()`, the receiving code owns the value. Keep the reveal close to the network call.
