Skip to main content

What is a credential grant?

A credential grant is a named credential that belongs to one user. It has two parts:
  • Secret: the token, key, or password
  • Policy: the destinations that can receive the secret and the code that can use the grant
Your code calls fetch(grant, input, init) with the grant name. Standard Agents Builder adds the secret to the outgoing request and returns the response. Your code, the model, and the client receive no copy of the secret. Use a grant for a credential that authenticates one user to an external HTTPS API, such as that user’s GitHub token.

Creating a grant

An administrator creates or replaces a grant with one request:
A successful request returns HTTP 204 with an empty body. A second PUT to the same name replaces the secret and the policy.

Request body

A grant name starts with a lowercase letter or a digit. The remaining characters are lowercase letters, digits, ., _, and -. The maximum length is 128 characters. The request returns HTTP 400 with { "error": "Invalid credential grant" } when a field fails these rules or when the body has a field that this table does not list. It returns HTTP 404 when the user does not exist and HTTP 403 when the caller is not an administrator.

Listing grants

The response lists the policy of each grant, sorted by name. It contains no secret:
The list also contains the instance grants that apply to the user.

Deleting a grant

The request returns HTTP 204, including when the user has no grant with that name. It deletes the user’s own grant only.
  • When no instance grant has that name, fetch() with that grant name returns the grant_not_found error after the deletion.
  • When an instance grant has that name, it applies to the user again after the deletion. fetch() then uses the secret and the policy of the instance grant.
To revoke a credential that an instance grant supplies, remove the grant from the instance registry or change its allow policy.

The secret is write-only

The PUT request is the one operation that accepts a secret. No route returns it, and UserState has no member that reads it. To change a secret, send a new PUT request with the full grant. Standard Agents Builder stores each grant in the durable storage of the user it belongs to. The policy is stored next to the secret, and the runtime reads the secret only when it adds the credential to a fetch() request. The three grant routes accept administrators only. The policy, including metadata, is readable through GET /api/users/:id/grants. Code in agents/ cannot list grants or read their policy.

Kinds and injection

kind sets how the runtime adds the secret to the request. inject adjusts the location for the header and query kinds. For the basic kind, store the secret as username:password. A header grant for an API that expects X-Api-Key: <secret>:
A header grant for an API that expects Authorization: token <secret>:
The injected header or query parameter replaces a value with the same name that the caller supplied. Rules for inject:
  • header is a valid HTTP header name.
  • format has at most 1,024 characters and no line break.
  • query has 1 to 128 characters from letters, digits, ., _, ~, and -.
Standard Agents Builder does not support the signing kind. A PUT request with "kind": "signing" returns HTTP 400.

Origin matching

The runtime sends the secret only to a destination that an origins entry covers. An entry has one of two forms:
  • An exact origin uses https. Write it in lowercase with no path, no trailing slash, and no user name or password.
  • A wildcard entry does not match the bare domain. *.example.com matches api.example.com and does not match example.com. Add https://example.com as a second entry to cover it.
  • Each entry has at most 512 characters.
The runtime checks the origin before it checks the caller.

Redirects

The runtime follows redirects itself and checks each redirect destination against origins.
  • A redirect to a destination that no origins entry covers ends the fetch with the grant_origin_denied error. The runtime sends no request to that destination.
  • The runtime adds the credential again to each request that it sends, and it sends the headers from your code with each one.
  • The runtime follows at most 5 redirects. A sixth redirect ends the fetch with the grant_redirect_limit error.
  • A 303 response changes the method to GET, except for a GET or HEAD request. A 301 or 302 response does the same for a POST request. The runtime then removes the body and the headers that describe it, such as Content-Type. A 307 or 308 response keeps the method and the body.
  • The runtime does not forward a redirect option from your code.

Allow policy

allow names the code that can use the grant. Each field is a list of up to 128 names. A caller that no list names receives the grant_caller_denied error. A PUT request needs at least one list. Thread code means tools, hooks, handlers, effects, and thread endpoints. Standard Agents Builder identifies all of them by the agent of the thread, so allow.agents is the list that admits them. Two more callers use the userEndpoints list:
  • A user lifecycle hook matches its hook type, such as user_value_changed.
  • A user room matches the name in its endpoint option. A room without that option matches its path under agents/users/, such as my_package/room.
The PUT route also accepts allow.tools and allow.endpoints and stores them. No caller in Standard Agents Builder matches those two lists. The route rejects allow.userRooms.

Using a grant

fetch(grant, input, init) takes the grant name, a URL, and request options. It is a member of UserState and of the handle that state.user() returns.

From a user endpoint

This endpoint needs github/repos in allow.userEndpoints.

From a tool

This tool needs the thread’s agent name in allow.agents.

How the runtime handles the request

  1. The runtime reads the user’s grant with that name. When the user has none, it reads the instance grant with that name.
  2. It checks the destination against origins.
  3. It checks the caller against allow.
  4. It adds the secret to the request as kind and inject describe.
  5. It sends the request with redirects disabled.
  6. For a redirect response, it checks the new destination against origins, adds the secret again, and sends the next request. It repeats this step for at most 5 redirects.
  7. It returns the final response of the external API to your code.
The runtime forwards the method, headers, and body options. The default method is GET. body is a string or absent, and fetch() throws for any other body type.

Errors

When the runtime does not send the request, fetch() resolves with a JSON response that carries an error code: The external API can return the same status codes. Read the error field of the body to tell a grant error from an API response. The error bodies contain neither the secret nor the destination URL.

Instance grants

An instance grant applies to every user of the instance. Set the secret AGENT_BUILDER_CREDENTIAL_GRANTS to a JSON registry:
  • Each entry follows the rules of the PUT request body, and name is required.
  • The registry holds at most 64 grants and at most 65,536 characters.
  • When the registry is malformed, or two entries share a name, the runtime uses no instance grants.
  • A user’s own grant takes precedence over an instance grant with the same name. After the user’s grant is deleted, the instance grant applies again.

Grant or variable

state.env(name) and user.env(name) return the value of a variable to your code. Code that holds a value can log it, return it to the model, or send it to any destination. A grant also limits the credential to listed destinations and listed callers. A variable has no such limits.

Next steps

Users

Run endpoints for one user and push live events to that user

Tools

Read variables with state.env()

API

Create endpoints that operate on one thread

Hooks

Run code at thread lifecycle points