8 min read

HTTP Authentication for AI Agents: Safe Credential Patterns

HTTP authentication for AI agents explained: compare bearer tokens, Basic auth, and custom headers, then keep credentials out of agent context.

HTTP Authentication for AI Agents: Safe Credential Patterns

An AI agent should be able to ask for an HTTP action without ever holding the credential that makes the action possible. That rule matters more than whether the request uses a bearer token, Basic authentication, or a vendor header. If a secret enters the agent's context, it can leak through a prompt, a tool trace, a generated shell command, a repository file, or a later summary that nobody intended to preserve.

HTTP authentication patterns still matter because each pattern shapes what can be stolen, replayed, accidentally forwarded, and audited. The right design starts with the API's required scheme, then confines the credential to a trusted executor that sends a narrowly defined request on the agent's behalf.

Agents turn ordinary credentials into copied data

An unattended agent changes the risk of an otherwise normal API credential because the agent reads and writes so many forms of text. A developer might keep a token in a local credential store and paste it into one request. An agent may inspect environment variables, write debug output, compose a curl command, create configuration files, and report its work to a human. Every one of those operations creates another place where a reusable secret can land.

The dangerous path often looks harmless at first:

  1. A task runner puts PAYMENTS_TOKEN in the agent process environment.
  2. The agent runs a diagnostic command that prints its environment or writes a shell script.
  3. The script reaches a repository, CI artifact, terminal scrollback, or another agent tool call.
  4. Someone finds the token later and sends valid requests until it expires or an operator revokes it.

The token did not need a sophisticated attacker. It only needed to become text in a place that was built to copy text.

Do not confuse access control for the agent with confidentiality for credentials. A sandbox may stop an agent from opening files outside a directory. It does not help if the secret already appears in the model context, command arguments, or a tool result. Likewise, an approval prompt that asks whether the agent may run curl says little if the agent can supply any host, path, body, and inherited authorization header.

This is why HTTP authentication for AI agents needs two separate boundaries. The agent needs permission to propose a request. A trusted component needs custody of the credential and authority to send the request. Combining those boundaries gives the agent a copyable secret and makes later controls depend on the agent handling it perfectly. That is an unreasonable design assumption.

A useful test is simple: ask whether you could paste the full agent transcript into an issue tracker without revoking the credential. If the answer is no, the secret has crossed the wrong boundary.

Bearer tokens are simple to send and easy to replay

A bearer token grants access to whoever presents it, so an agent must never receive one unless you accept that a transcript leak can become an API access leak. RFC 6750 defines bearer token use in the Authorization request header:

GET /v1/projects/alpha/releases HTTP/1.1
Host: api.example.test
Authorization: Bearer eyJhbGciOi...
Accept: application/json

The server does not need proof that the sender is the intended agent, original user, or original machine. It checks whether the presented token is valid and authorized. That property keeps HTTP clients simple, but it also makes copied tokens useful to anybody who has them.

RFC 6750 permits sending bearer tokens in a form body under restricted conditions and describes URI query use for legacy cases. Do not put them in query strings. URLs reach browser history, proxy logs, analytics systems, referrer headers, support tickets, and application logs more readily than most teams expect. The standard itself warns that URI transport has a high likelihood of disclosure. There is no good reason to make an agent's secret part of a URL.

Bearer tokens fit agents well only when the surrounding credential design limits damage. Prefer tokens that have an intended API audience, narrow permissions, short expiration, and a distinct identity for the action runner. An API token that can administer every project, read every customer record, and never expires is a production master credential with a friendlier name.

The usual argument for handing a bearer token to an agent is speed: one environment variable, one HTTP client, no extra component. It is popular because it works in a demo. It fails once the agent needs debugging, delegation, long-running work, or access to more than one service. A token copied into context becomes harder to revoke selectively because you no longer know every place it has traveled.

There is another trap: a token with a restrictive name may still have broad effective authority. Read the provider documentation for the actual scope model. Some services use endpoint-specific scopes. Others grant rights at an organization, project, repository, or account level. Some API tokens silently inherit the full privileges of the user who created them. The token label is not evidence of its limits.

A mediator can hold the bearer token and construct the header only after it validates the requested destination. The agent should submit intent and request data, such as “create a release in project alpha with this body,” rather than the literal Authorization header. The executor adds the secret after validation and removes it before returning any record to the agent.

Basic authentication needs a separate service identity

Basic authentication is acceptable for a narrow service account over TLS, but it is a poor way to give an agent a human username and password. RFC 7617 specifies the wire format as a base64 encoding of user-id:password placed in the Authorization header:

Authorization: Basic YWdlbnQtcmVsZWFzZXI6czNjcjN0LXZhbHVl

Anyone who can read that value can decode it. Base64 changes representation; it does not protect the value. TLS protects the connection between client and server, but it does not protect the credential after an agent, local process, debug log, or proxy has copied the header.

Many API providers use Basic authentication with an API token as the password and a fixed or ignored username. That does not turn the scheme into a weaker version of bearer authentication. It creates similar replay exposure, plus a few practical risks. A client or logger might record the decoded username, the raw header, or both. A developer might reuse a real account password because the protocol calls the second field a password. That is exactly the credential you should not delegate to an autonomous process.

When Basic authentication is unavoidable, create a dedicated account for the executor. Give it only the permissions needed for the request family. Do not use a personal account, an administrator account, or credentials shared among unrelated automations. A service identity makes revocation and investigation possible without locking out a person or breaking every job at once.

Handle character encoding deliberately. RFC 7617 describes a compatibility issue around the username and password character set and allows servers to advertise UTF-8. If a provider supports only ordinary ASCII values, keep machine credentials within that set. Do not invent a homegrown encoding step in an agent prompt, because it creates another inconsistent place where a secret can be transformed and logged.

The request executor should build the Basic header itself from protected fields. The agent can choose an approved operation and provide non-secret parameters. It should not construct the base64 value, and it should never see a decoded credential in an error message. A safe error says that authentication failed for the selected credential reference. It does not echo the header or tell the agent which part of the password matched.

Custom headers need exact vendor semantics

A custom authentication header works only as safely as the API's documented verification rules and your handling of its value. Common examples include X-API-Key, Api-Key, or a provider-specific header. Some providers expect a static API key. Others expect a signed request with a timestamp, nonce, canonical path, and body digest. Treating all custom headers as interchangeable is how teams break authentication and, worse, accidentally broaden where a credential gets sent.

First, follow the provider's specification exactly. Header names are case-insensitive under HTTP, but header values and signature inputs may not be. A signature scheme may require a particular canonicalization order, exact body bytes, and a bounded timestamp window. If the executor parses JSON and reserializes it before signing, it may produce valid-looking JSON with a different byte sequence. The provider then rejects the request, and people often respond by disabling signature checks or adding broad retry logic. Fix the byte handling instead.

Second, distinguish a header used for authentication from a header that identifies a client. User-Agent, request IDs, and application identifiers can help a provider observe traffic, but they usually do not prove authority. Conversely, an X-API-Key header may be every bit as replayable as Authorization: Bearer. Do not rate its sensitivity by whether its name contains the word authorization.

Third, prevent header smuggling by the agent. The agent should not get a free-form map of outbound headers if a protected executor also injects credentials. A free-form map lets it add a second Authorization header, override an expected content type, attach an unapproved identity header, or influence a downstream proxy in ways the reviewer did not see.

Use a request contract with named, typed fields. For example:

{
  "credential_ref": "release-service",
  "method": "POST",
  "url": "https://api.example.test/v1/projects/alpha/releases",
  "headers": {
    "accept": "application/json"
  },
  "body": {
    "version": "2025.06.0",
    "notes": "Fix parser crash on empty input"
  }
}

The executor, not the agent, maps credential_ref to the vendor's custom header or signing procedure. It should reject attempts to provide authorization, cookie, the provider's credential header, host, or a duplicate form of those names. It should also own Content-Length, because the HTTP client must calculate that from the final bytes.

The response shown to the agent needs the same discipline. An HTTP response can include Set-Cookie, diagnostics, or echoed request details. Return the status, selected safe response headers, and the body required for the task. Keep raw headers and traces in protected audit storage when they are needed for operators.

Pick the scheme the provider requires, then limit the blast radius

Keep a record of calls
Sessions and Activity journals are projected from one encrypted, hash-chained audit log.

You rarely choose the authentication scheme for a third-party API. The provider has already chosen it. You do choose whether the credential is broad or narrow, where it lives, which requests may use it, and what happens when the agent behaves strangely.

Use this comparison when designing the boundary:

PatternWhat the client sendsMain exposure if copiedSensible agent handling
Bearer tokenA token in AuthorizationDirect replay by the recipientKeep in the executor and limit scopes and lifetime
Basic authenticationBase64 of username and password or tokenDecoding followed by direct replayUse a dedicated service identity in the executor
Static custom headerA provider-defined secret headerUsually direct replayInject only for approved hosts and paths
Signed custom headerSignature plus timestamp and request dataReuse may fail, but signing material remains sensitiveKeep signing secret and canonicalization in the executor

Signed requests deserve a careful qualification. A timestamp and nonce can reduce straightforward replay at the API boundary, but they do not make the signing secret safe for agent context. An agent that can access the secret can sign a fresh malicious request. If the signing implementation accepts arbitrary method, host, path, and body from the agent, it will faithfully sign actions you did not mean to authorize.

Scope should match the action, not an imagined future use. A release publishing agent might need permission to create a release in one project. It does not need permission to delete projects, alter billing, read every artifact, or invite users. If a provider cannot issue an appropriately limited credential, place a narrower service you control in front of the provider API or retain human approval for the dangerous calls.

Do not solve poor scopes with a long allowlist written in natural language. “Use the token only for releases” is advice, not an enforcement point. Put the restriction where the request is assembled: expected origin, allowed method, path pattern, header set, body schema, and maximum response size. Those constraints make a credential less useful outside its intended job.

Mediation keeps secrets out of the agent context

Mediated HTTP calls work when the secret holder executes the request, rather than returning a secret for the agent to execute it. The distinction is easy to blur because both designs can present the agent with a tool named http_request. The data flow tells you which design you built.

In the unsafe design, the tool obtains a token and gives it to the agent, perhaps as an environment variable, a placeholder expansion, or a “temporary” credential. The agent's next action sends the request. The token has already crossed into a system designed to reason over, transform, and repeat text.

In the mediated design, the agent sends a structured request to an executor. The executor checks the request against its permitted shape, obtains the selected credential from protected storage, injects the correct authentication material, sends the request, records the action, and returns a bounded result. The agent never gets the secret value, an encoded form of it, or a shell command containing it.

That difference also changes incident response. If you suspect an agent session has gone wrong, you can stop its ability to request actions without rotating every credential immediately. If a credential itself may have escaped, you still rotate it. Session revocation and credential rotation solve different problems, and teams lose time when they treat them as the same button.

A practical executor should reject several request forms by default:

  • Absolute URLs that point to an unapproved origin, including lookalike subdomains.
  • Requests with user-supplied Authorization, Cookie, proxy, or credential-specific headers.
  • Redirects that could carry an authenticated request to another origin.
  • Methods outside the credential's intended use, especially destructive methods.
  • Bodies that exceed the expected size or do not match the endpoint's expected format.

Sallyport takes this custody model on macOS: its encrypted vault holds API and SSH credentials, while an MCP agent asks the app to perform HTTP calls rather than receiving credential values.

Do not mistake mediation for a general-purpose policy engine. It cannot infer whether “delete stale test resources” is correct in a particular production account. It can ensure that a request stays within a defined technical boundary and that the credential remains outside agent context. Human review, scoped accounts, and application-specific safeguards still decide whether the action itself deserves approval.

The request boundary must include redirects, DNS, and responses

Use Basic auth without exposure
Basic authentication credentials stay in the vault while the agent receives only the call result.

Approving https://api.example.test alone is too broad because a credentialed request contains more than a hostname. The executor needs to control each place an agent can change the effective destination or the meaning of the request.

Start with an exact origin: scheme, hostname, and port. Require HTTPS for normal internet API credentials. RFC 9110 defines the HTTP request target and authority rules, but application code still needs to enforce its own destination rules. Do not approve hosts by suffix alone. A check such as “hostname ends with example.test” may accept notexample.test; a loose substring check is worse. Compare parsed hostnames against an exact allowlist or an intentionally designed subdomain rule.

Then restrict methods and paths. If the agent should create releases, allow the precise POST path family it needs. Do not add DELETE because it may be useful during cleanup. Do not allow arbitrary versioned paths without deciding whether a later API version exposes different behavior. Path normalization matters too. Parse the URL before comparison and reject surprising encodings, dot segments, or duplicate separators if your matching code does not handle them predictably.

Redirect behavior deserves explicit treatment. HTTP libraries differ, and a library upgrade can change defaults. For credentialed calls, the safest default is to reject redirects and report the location to the agent. If a provider truly requires a redirect, allow only the specific expected target origin and rebuild the request under the same restrictions. Never assume a client strips credentials consistently enough to make an open redirect harmless.

DNS creates a second destination check. A trusted hostname can resolve to changing addresses, and internal systems may have names that point into sensitive networks. If your executor runs on a developer laptop, arbitrary outbound HTTP can become a route to local administration services or cloud metadata endpoints. Restrict approved origins before connection, avoid accepting agent-controlled proxy settings, and do not let the agent select a network interface or resolver.

Finally, cap and filter the response. An agent does not need a multi-megabyte download to learn that a release creation succeeded. Large bodies waste context and can carry instructions copied from an untrusted service into the agent's reasoning. Return only the fields needed for the next action when you can. Mark remote text as data in the tool contract, and never let response content rewrite the executor's authorization rules.

Approval should name the calling process and the concrete action

A human approval click has value only when it tells the human enough to make a decision. “Allow agent access to API” is a blanket permission disguised as a prompt. It encourages approval fatigue because the person cannot tell which local process asked, what credential it seeks to use, or what it will send.

Identify the calling process in a way that helps an operator recognize it. On macOS, code-signing authority is often more useful than a mutable process name. A process called agent can be a legitimate developer tool or an unrelated binary that chose the same name. Process lineage, executable path, and signing information provide better evidence, though none replaces a bounded action request.

Session approval and request approval solve different tradeoffs. Session approval reduces repeated interruptions for a known agent run. It works for low-risk, repetitive operations where the credential and request boundary remain narrow. Request approval fits irreversible or sensitive operations, such as publishing externally, changing access settings, or writing data that will trigger another system.

Avoid an approval design that asks a person to review a dense raw HTTP dump for every call. People will approve it without reading after the third interruption. Show a concise action summary: the service identity, method, destination, path, meaningful body fields, and any side effect the API documents. Preserve the raw request details in the audit record for later investigation.

You should also distinguish permission to start a session from permission to keep one alive. If an agent process exits, a replacement process should not inherit approval merely because it has the same name. If a user revokes a session, the executor must stop accepting calls from it immediately. A UI label that says “revoked” while an already-authorized client continues to send requests is worse than no revocation feature because it creates false confidence.

Audit records must answer what happened after the agent exits

One vault for HTTP and SSH
Keep HTTP API and SSH credentials in one encrypted vault instead of passing either to an agent.

A useful audit trail lets an operator answer who requested a call, which credential reference the executor used, where the request went, what the executor allowed, and what the remote service returned. You do not need to record the raw secret to answer those questions. In fact, recording it would create a second secret store masquerading as observability.

For each call, retain the session or process identity, timestamp, selected credential reference, HTTP method, approved origin, path, a protected representation of relevant request data, response status, and the decision that allowed or denied it. Record the actual final destination after any permitted redirect. Record failures too. Repeated denied attempts against an unusual path often reveal a broken agent instruction or an attempted boundary escape.

Tamper evidence changes how much trust you can place in the record. A hash-chained log links each entry to prior entries, so later modification or deletion becomes detectable during verification. It does not prove that the original executor made a wise authorization choice, and it does not make a compromised host trustworthy. It does make quiet editing of history harder, which is exactly the property incident review needs.

Keep the audit record separate from the agent's ordinary working context. The agent may receive a summary such as 201 Created, release id r-4821. An operator may need a richer record that includes the path and decision metadata. Neither party should receive the raw authorization header simply because logging exists.

With Sallyport, the Sessions and Activity journals project from an encrypted hash-chained audit log, and sp audit verify can verify that chain offline without unlocking the vault. That is a sensible design for review because verification does not require exposing the credentials that authorized the calls.

Test failure paths before granting production access

A credential boundary that only succeeds on the happy path has not earned production access. Build a small test API or use a non-production account, then make the executor prove that it refuses the situations that would leak or misuse credentials.

Use a test sequence like this:

  1. Request an approved GET endpoint and confirm the agent receives the expected body but no credential-bearing header.
  2. Supply an Authorization header from the agent and confirm the executor rejects it rather than merging or replacing headers silently.
  3. Change the URL to an unapproved host and to a deceptive near-match, then confirm both fail before any network request.
  4. Return a cross-origin 302 response from the test API and confirm the executor stops instead of forwarding authentication.
  5. Revoke the agent session during a run and confirm later calls fail while audit verification still succeeds.

Inspect process environments, temporary directories, shell history, generated files, crash reports, and test logs after the run. Search for a known test credential string. This exercise catches an embarrassing number of leaks in wrapper scripts and debug modes that request-level tests never see.

Also test provider errors. An API may return a body that echoes invalid header content, a trace identifier, or advice to retry with a different endpoint. Confirm your result filter does not hand credential-like material back to the agent and that retry logic cannot turn one denied request into a flood of attempts. Limit retries to errors where the API documentation says a retry is appropriate, and keep the method and destination unchanged.

The first production credential should be narrow enough that a failed test would be inconvenient, not catastrophic. If your team cannot explain exactly which request shapes it authorizes and how to revoke an active agent run, the credential is still too broad for autonomous use.

FAQ

Are bearer tokens safe for autonomous AI agents?

Bearer tokens are easier to use because the client sends one value in the Authorization header. That convenience makes possession the whole security boundary: anyone who gets a usable token can usually replay it. Give agents bearer tokens only when the token has narrow scope, a short lifetime, and a recovery path you can operate quickly.

Is HTTP Basic authentication encrypted?

Basic authentication sends a base64-encoded username and password or access token. Base64 is encoding, not encryption, so TLS is mandatory and any process that sees the header can recover the credential. It can be acceptable for a tightly limited service account, but it is a poor fit for broad human credentials.

When should an API agent use a custom authentication header?

Use a custom header only when the provider explicitly documents it, such as X-API-Key or a vendor-specific signature header. A header name does not add security; the credential's scope, lifetime, storage, and exposure paths do. Treat a custom header value with the same care as a bearer token unless the protocol says otherwise.

What does mediated HTTP authentication mean?

A mediated call lets the agent request an action without receiving the credential that authorizes it. A separate trusted executor retrieves the secret, builds the authenticated request, sends it, and returns a filtered result. This removes the secret from prompts, tool arguments, shell history, and most agent-readable logs.

Does a credential gateway make AI agent actions safe?

No. A gateway limits credential exposure, but it cannot decide whether an agent's intended business action is wise. You still need narrow credentials, explicit endpoint boundaries, human approval where consequences justify it, and review of returned data.

What should an AI agent be allowed to send through an API gateway?

Authorize a concrete request shape, not an entire hostname. Include the method, allowed path family, destination host and port, required header names, body type, and redirect behavior. If the agent may alter any of those fields freely, the credential can become useful in places you did not intend.

Where should API credentials be stored for coding agents?

Do not pass API tokens through environment variables, prompt files, chat messages, command arguments, or request templates that the agent can read. Each of those locations is easy to echo, commit, copy into logs, or expose through a tool result. Store secrets in a protected vault and let a trusted component inject them at send time.

What should be logged for agent API calls?

Redact credentials from activity logs, but retain enough immutable context to reconstruct the action. Record the agent process or session, time, request method, destination, path, status, and a digest or protected representation of the request where appropriate. A log that records only "request succeeded" is too weak for incident work.

Should an agent follow HTTP redirects with credentials?

Do not forward Authorization headers to a different origin after a redirect. Most HTTP clients strip sensitive headers on cross-origin redirects, but you should test the actual library or executor you use. A safer default is to reject redirects for credentialed requests unless the destination is explicitly approved.

What should I do if an AI agent exposes an API token?

Rotate the credential, revoke the agent session, preserve the relevant audit records, and identify every request made during the exposure window. Then determine how the secret leaked: agent context, tool output, repository files, process environment, or a permissive request rule. Fixing only the token leaves the same leak path ready for the replacement.

Sallyport

Sallyport runs API calls and SSH commands for your AI agent. The keys stay in a local vault on your Mac; you approve each run and every action lands in a sealed journal.

© 2026 Sallyport · Open source under Apache-2.0 · Oleg Sotnikov