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

# Authentication

> Exchange an API client's credential for a bearer token, use private-key JWTs, and revoke access.

API clients authenticate with the OAuth 2.0 client-credentials grant. Create
the client and its credential in **Settings → API Clients** first (see
[Create an API client](/docs/conduit/use/api#create-an-api-client)).

## Get a token

Exchange the credential for a short-lived (1 hour) bearer token at the token
endpoint, `https://conduit.example.com/oauth/token` (your instance's exact URL is
shown in the client's **Manage credentials** dialog). Conduit publishes it, with
the rest of its OAuth configuration, in the standard
[authorization server metadata](https://www.rfc-editor.org/rfc/rfc8414)
document at `https://conduit.example.com/.well-known/oauth-authorization-server`:
`token_endpoint`, the client authentication methods it accepts
(`token_endpoint_auth_methods_supported` — for API clients, `client_secret_basic`
and `private_key_jwt`; `none` is for MCP clients), and the algorithms it accepts
for a signed assertion (`token_endpoint_auth_signing_alg_values_supported`:
`RS256`, `ES256`). OAuth
client libraries that support discovery read it given just the instance's base
URL, the document's `issuer`.

```bash theme={null}
# client_secret_basic
curl -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials \
  -d scope="connectors:read connectors:write" \
  https://conduit.example.com/oauth/token
```

`private_key_jwt` is the same grant with a signed client assertion
(`client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`),
which is the stronger option because no shared secret is transmitted. Standard
OAuth client libraries implement both and refresh the token for you.

Requesting `scope` down-selects from what the client was granted; omit it to get
all granted scopes. The response is a standard OAuth token:

```json theme={null}
{ "access_token": "…", "token_type": "bearer", "expires_in": 3600, "scope": "connectors:read connectors:write" }
```

There is no refresh token — re-run the grant when the token expires.

## Private-key JWT authentication

`private_key_jwt` (RFC 7523) is the recommended credential for unattended use.
Instead of sending a shared secret, your client signs a short-lived JWT
assertion with a private key that never leaves your infrastructure. You register
only the matching **public** key with Conduit, so no reusable secret is ever
transmitted to the token endpoint or stored in Conduit's database — a leaked
request log or a database dump can't be replayed to mint tokens. A client secret,
by contrast, is a bearer value: anyone who reads it once can use it until you
rotate it. Prefer `private_key_jwt` for CI and production.

### Generate a key pair

Conduit accepts an RSA (≥ 2048-bit) or EC P-256 public key, as a PEM or a JWK.
PEM is the simplest — two `openssl` commands, nothing else to install:

```bash theme={null}
# private key — keep this in your secrets manager; your client signs with it
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out priv.pem
# …or EC P-256: openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out priv.pem

# public key — this is what you register with Conduit
openssl pkey -in priv.pem -pubout -out pub.pem
```

Paste the contents of `pub.pem` (the `-----BEGIN PUBLIC KEY-----` block) into
**Settings → API Clients → Add credential → Public key**. Conduit derives a
`kid` for it (the RFC 7638 thumbprint) and shows it next to the credential.

Already have a JWK? Paste that instead — the same field takes either form.

### Request a token

Standard OAuth libraries build and sign the assertion for you. The raw exchange
is the same client-credentials grant, with a signed assertion in place of Basic
auth — the assertion's `iss` and `sub` are the client ID (copy it from the
API clients table or the client's **Manage credentials** dialog), `aud` is the
token endpoint URL (shown in the same dialog), and `exp` is a few minutes out with a unique `jti`:

```bash theme={null}
curl -d grant_type=client_credentials \
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  -d client_assertion="$SIGNED_JWT" \
  -d scope="connectors:read" \
  https://conduit.example.com/oauth/token
```

The `aud` must **exactly** match your instance — either its base URL or that URL
plus `/oauth/token` (the metadata document's `token_endpoint`). This binds the
assertion to one instance so it can't be replayed against another, and a
mismatch is the most common setup error: the token endpoint returns
`invalid_client` (deliberately without saying why). Set `aud` to the token
endpoint you're POSTing to and they always agree.

## Revoking access

* **Disable or delete a client**, or **revoke a single credential**, in
  Settings → API Clients — it takes effect on the next request. Revoking one
  credential leaves the client's other credentials working, so it is the clean
  way to retire a leaked secret mid-rotation.

**Instance-wide kill switch:** an operator can set
`CONDUIT_DISABLE_API_CLIENTS=true` and restart Conduit to refuse token minting
and reject every existing API-client token instance-wide (break-glass for a
suspected compromise).

Machine actions are attributed to `api-client:<name>`, and every audit detail
includes the immutable client ID plus the credential ID that minted the token.
Known-client authentication failures are included in the workspace's
`audit:read` export, coalesced to one row per client/source-IP/minute; repeats
and unknown client IDs remain available through telemetry without amplifying
audit-log writes.

Each audit entry also carries `actor_user_id` (the acting user's id, or the
API client's backing service user), `trace_id` and `span_id`. Telemetry
identifies people by `user.id`, never by email, so these are how a SIEM joins
an event from your telemetry backend to the audit entry, and its email,
behind it. See [What telemetry contains](/docs/conduit/configure/telemetry#what-telemetry-contains).
