Skip to content
API Reference
Application identity

Authenticate deployed workloads

Let a deployed service prove who it is to Keycard with the OIDC token its platform already issues. No client secret to store or rotate.

Your service authenticates to Keycard as an Application. Locally that means a client ID and secret. In production you want the same service to prove who it is without a long-lived secret in the host’s environment, because anything that can read that environment can act as your Application, from anywhere, until someone rotates the secret.

If your platform issues each running service a short-lived OIDC token, you don’t need the secret. Register the platform as a Provider, pin a Workload Identity credential on your Application to the token’s issuer and subject, and the SDK presents the platform’s token instead. The steps are the same on every platform that publishes an OpenID Connect discovery document for its tokens. Render and Fly.io have walkthroughs.

flowchart LR
    A[Your platform] -->|"Short-lived OIDC token"| B[Your service]
    B -->|"Token exchange + client assertion"| C[Keycard]
    C -->|"Match issuer + subject, verify signature"| C
    C -->|"Credential for the Resource"| B
    B -->|"Bearer token"| D[Resource]
  1. The platform issues your service a short-lived token and rotates it. The token’s iss claim names the platform’s issuer; its sub claim names your exact service.
  2. The SDK reads the token and presents it to Keycard as the Application’s client assertion on each token exchange.
  3. Keycard looks up the Workload Identity credential registered for that issuer and subject, verifies the token’s signature against the issuer’s published keys, and authenticates the request as that Application.
  4. Token exchange proceeds as it does locally: your service receives credentials for the Resources it depends on.

Because the credential is pinned to the subject, another service on the same platform presents a different sub and does not match.

Work out your token’s issuer and subject

Section titled “Work out your token’s issuer and subject”

Keycard matches the token’s iss and sub claims exactly. Each platform builds them from IDs its dashboard or CLI shows, so you can register the credential before you deploy:

Platform Issuer (iss) Subject (sub) Where the IDs come from
Render https://oidc.render.com/<workspace-id> workspace:<workspace-id>:environment:default:service:<service-id> The workspace ID (tea-…) is on the workspace’s Settings page. The service ID (srv-…) is returned when you create the service and is in its dashboard URL. The environment segment is default unless the service belongs to a project environment, whose ID starts with evm-.
Fly.io https://oidc.fly.io/<org-slug> <org-slug>:<app-name>:<machine-name> The org slug is in fly orgs list and the app name in fly.toml. The last segment is each Machine’s name, which differs from one Machine to the next, so register <org-slug>:<app-name>:*, which matches every Machine in the app.
Anything else The platform’s OIDC issuer Check the platform’s documentation

Create a separate Application for the deployment rather than adding the credential to your local one. Each environment then has its own identity, its own dependencies and its own audit trail, and removing one’s credential never touches the other.

  1. Register the platform as a Provider

    In Keycard Console, navigate to Providers → Add Provider:

    Field Value
    Name A label, for example Render OIDC
    Issuer URL The issuer from the table above

    Leave Client ID and Client Secret empty. Keycard discovers the issuer’s signing keys from its OpenID Connect discovery document.

  2. Create the production Application

    Navigate to Applications → Add Application and name it. On its details page, give it what the deployed service provides and depends on, mirroring your local Application:

    • On the Provides tab, add the Resource for the deployed server’s public URL. Register that Resource first under Resources → Add Resource → Add Manually, with Zone Provider as its credential provider.
    • On the Dependencies tab, add the same external Resources your local Application depends on.

    Don’t generate a client ID and secret for this Application.

  3. Add a Workload Identity credential

    On the Application’s Application Credentials tab, click Add credential and choose Workload Identity:

    Field Value
    Provider The Provider from step 1
    Subject The subject from the table above

    The subject must match the token’s sub claim character for character. Console marks the field optional; set it anyway, because a credential with no subject matches no token. A trailing * matches every subject that starts with the text before it, so workspace:<workspace-id>:* trusts every service in a Render workspace at the cost of looser pinning. When several credentials match one token, an exact subject wins over a wildcard and a longer prefix wins over a shorter one.

One codebase runs locally with a client secret and in production with the platform’s token. How the SDK picks the credential differs by language.

When you don’t pass an application_credential, AuthProvider discovers one from the environment (keycardai-fastmcp 0.7 and later). Exactly one of these may be configured:

  • KEYCARD_CLIENT_ID and KEYCARD_CLIENT_SECRET: a client secret
  • A token file named by KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_WEB_IDENTITY_TOKEN_FILE or AZURE_FEDERATED_TOKEN_FILE: a workload identity
  • KEYCARD_WEB_IDENTITY_KEY_STORAGE_DIR: a key pair

If the environment can build more than one, startup fails with Ambiguous application credential configuration rather than guessing. KEYCARD_APPLICATION_CREDENTIAL_TYPE (client_secret, workload_identity or web_identity) picks one when you need both present. Versions before 0.7 picked the client secret silently.

On the deployed service, don’t pass a credential, don’t set KEYCARD_CLIENT_ID or KEYCARD_CLIENT_SECRET, and name the token file in one of the variables above if the platform mounts it. Render sets AWS_WEB_IDENTITY_TOKEN_FILE itself once AWS_ROLE_ARN is present on the service.

import os
from keycardai.fastmcp import AuthProvider
# No application_credential argument: the SDK selects it from the environment.
auth_provider = AuthProvider(
zone_url=os.environ["KEYCARD_ISSUER"],
mcp_server_url=os.environ["MCP_SERVER_URL"],
)

zone_url / zoneUrl is your Zone’s Issuer URL, shown in Keycard Console under Settings → Connection. Both SDKs fetch the token on every token exchange, so the platform’s rotation needs no handling in your code.

Deploy, connect a client, and call a tool that reaches an external API. Then open the production Application in Keycard Console and select its Activity tab: each credential the deployed service was issued is listed there. If the call fails instead, see Troubleshooting.

invalid_client: No token application credential configured for provider ‘…’ and subject ‘…’

Section titled “invalid_client: No token application credential configured for provider ‘…’ and subject ‘…’”

No Workload Identity credential matches the token. The message includes the issuer and subject exactly as Keycard read them from the token; compare them with the Provider’s Issuer URL and the credential’s Subject in Console. Usual causes: a subject that names a different environment or service ID, a Provider whose Issuer URL differs from iss by a character, or a credential with no subject.

Startup fails with Ambiguous application credential configuration

Section titled “Startup fails with Ambiguous application credential configuration”

The Python SDK found a client secret and a token file (or key directory) at once and won’t guess. Remove KEYCARD_CLIENT_ID and KEYCARD_CLIENT_SECRET from the service, or set KEYCARD_APPLICATION_CREDENTIAL_TYPE=workload_identity, and redeploy.

The deployed service authenticates as the local Application

Section titled “The deployed service authenticates as the local Application”

KEYCARD_CLIENT_ID and KEYCARD_CLIENT_SECRET are set on the service and your code prefers them, as the TypeScript example above does and as keycardai-fastmcp before 0.7 did. Remove them and redeploy.

Token exchange fails for a Resource that works locally

Section titled “Token exchange fails for a Resource that works locally”

The production Application is missing that Resource under Dependencies. Dependencies belong to the Application, so the local one’s don’t carry over.