{
  "question": "How do I give an AI agent a scoped, expiring credential instead of a long-lived API key?",
  "slug": "how-to-give-an-ai-agent-a-scoped-expiring-credential-instead-of-a-long-lived-api-key",
  "url": "https://www.rightshold.com/answers/how-to-give-an-ai-agent-a-scoped-expiring-credential-instead-of-a-long-lived-api-key",
  "short_answer": "Keep the long-lived secret with a small broker you control, and hand the agent only a credential minted for the job that expires on its own. The major platforms already do this. AWS STS AssumeRole returns temporary keys that last from 900 seconds (15 minutes) up to the role's maximum session duration (1 to 12 hours; default 3,600 seconds), and a session policy passed with the call can only narrow what the role allows. Google Cloud's generateAccessToken mints a service-account access token with a lifetime you choose (for example 300s), capped at 1 hour unless an organization policy extends it to 12, and it comes without a refresh token. A GitHub App installation token expires after 1 hour and can be limited to listed repositories and permissions. Name each session after the agent and task so logs show who acted, and plan revocation before you need it: AWS temporary credentials can't be deleted, only denied, either every session of a role issued before a point in time or one named session through a deny policy.",
  "checklist": [
    "Find every long-lived key the agent holds today (cloud access keys, personal access tokens, service-account JSON keys, API keys in .env files) and decide which service will mint a short-lived replacement.",
    "AWS: give the broker permission to call sts:AssumeRole on a role for the agent's work. Set DurationSeconds close to the task length (minimum 900), pass a session policy for this task (inline, or up to 10 managed policy ARNs, 2,048 characters combined), and set RoleSessionName to the agent and task. Role chaining is capped at 1 hour.",
    "Record the human behind the session: AWS SourceIdentity persists across chained role sessions and can be required with the sts:SourceIdentity condition in the role's trust policy.",
    "Google Cloud: grant the broker roles/iam.serviceAccountTokenCreator on a service account that holds only the task's roles, and call generateAccessToken with the scopes and a short lifetime. The gcloud --impersonate-service-account flag can't set a lifetime, so use the API or a client library when you want minutes. Don't create downloadable service-account keys for the agent.",
    "GitHub: authenticate as a GitHub App and mint an installation token per task with repositories (or repository_ids, up to 500) and permissions set, so the token can't reach everything the installation can. It expires after 1 hour. Newer tokens use a stateless ghs_ format rolled out from April 27, 2026, so don't assume a 40-character token.",
    "CI: let workflows get cloud credentials through GitHub Actions OIDC instead of storing cloud keys as secrets; the cloud provider issues a short-lived token per run based on a trust policy.",
    "Keep the root of trust out of the agent's reach: the role trust, token-creator grant or app private key belongs to the broker, never to the agent's environment, prompt or tool results.",
    "Know your kill switch: on AWS, Revoke active sessions attaches an AWSRevokeOlderSessions deny policy to the role, cutting off every session issued before then (and about 30 seconds after) for every user of that role. To stop one agent's session only, deny it by its role session name with the aws:userid condition key (role-id:session-name), which is another reason to name sessions per agent and task. Short lifetimes are the revocation you get for free."
  ],
  "sections": [
    {
      "heading": "Why expiry and scope matter more for agents",
      "points": [
        "Agents copy what they see into transcripts, logs, commits and tool calls. A credential that expires in minutes, and only works for one task's resources, limits both how long and where a leak matters.",
        "A long-lived key also hides who acted. A per-task session name (AWS), a dedicated service account (Google Cloud) or a GitHub App identity puts the agent and the task in the provider's own logs."
      ]
    },
    {
      "heading": "What the platforms enforce",
      "points": [
        "AWS: the session's permissions are the intersection of the role's identity-based policy and the session policies; session policies can't grant more than the role allows. DurationSeconds above the role's maximum fails the call.",
        "Google Cloud: tokens from generateAccessToken carry an expireTime; when it passes, the broker has to impersonate again, because no refresh token is issued. Lifetimes above 1 hour need the constraints/iam.allowServiceAccountCredentialLifetimeExtension organization policy.",
        "GitHub: an installation token can't be granted repositories the installation wasn't granted or permissions the app wasn't granted. The response lists the token's expiry, permissions and repositories, so the broker can check it got what it asked for."
      ]
    },
    {
      "heading": "What can still go wrong",
      "points": [
        "A broker that mints whatever the agent asks for is a long-lived key with extra steps. Fix the scopes per task type in the broker, not in the agent's request.",
        "Revocation is coarse unless you planned it: AWS's Revoke active sessions hits every current user of the role, and a per-session deny only works if the session name identifies the agent. Separate roles per agent or task type, and unique session names, keep the blast radius small. Session names can change during role chaining.",
        "Refresh loops: an agent that can call the broker again indefinitely has, in effect, a credential that never expires. Bound how many tokens a task can mint and end the task's access when it finishes."
      ]
    }
  ],
  "checks": [],
  "ask_rightshold": {
    "start": "https://www.rightshold.com/start?ref=seo",
    "mcp_endpoint": "https://www.rightshold.com/mcp",
    "ask": "Give my sub-agent a Rightshold key that can only run license checks and stops working when this job's intent expires.",
    "mcp_call": {
      "tool": "intent_key",
      "arguments": {
        "intent_id": "<int_... from declare_intent, declared with operations [\"check_license\", \"check_rights_batch\"] and ttl_hours 8>",
        "label": "nightly license scan"
      }
    },
    "also": [
      "Needs your account API key. declare_intent sets the purpose, operations and lifetime (ttl_hours 1 to 2,160; 30 days if you leave it out). intent_key then issues a key bound to that intent: requests outside its declared operations are refused, it can't change account settings, budgets or keys, and once the intent expires or is revoked the key gets 403 intent_inactive. The key is shown once.",
      "revoke_intent ends the intent, and every key bound to it, at once. POST /v1/me/api-keys/<key_id>/revoke revokes one key.",
      "issue_agent_credential issues a signed rs-credential/0.1 credential for an agent's exact configuration with a lifetime of 1 to 168 hours (24 by default); verify_agent_credential checks it for free."
    ],
    "scope": "Rightshold issues and enforces its own intent-bound keys and agent credentials. It doesn't mint AWS, Google Cloud or GitHub credentials or hold your cloud keys; those come from your broker and each provider."
  },
  "sources": [
    {
      "title": "AWS STS API Reference: AssumeRole (DurationSeconds, session policies, SourceIdentity)",
      "url": "https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html"
    },
    {
      "title": "AWS IAM: Revoke IAM role temporary security credentials",
      "url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_revoke-sessions.html"
    },
    {
      "title": "AWS IAM: Disabling permissions for temporary security credentials (aws:userid)",
      "url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_disable-perms.html"
    },
    {
      "title": "Google Cloud IAM: Create short-lived credentials for a service account",
      "url": "https://cloud.google.com/iam/docs/create-short-lived-credentials-direct"
    },
    {
      "title": "GitHub Docs: Generating an installation access token for a GitHub App",
      "url": "https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app"
    },
    {
      "title": "GitHub Docs: OpenID Connect (Actions)",
      "url": "https://docs.github.com/en/actions/concepts/security/openid-connect"
    }
  ],
  "related": [
    {
      "title": "How to scope OAuth tokens per task for AI agents",
      "url": "https://www.rightshold.com/answers/how-to-scope-oauth-tokens-per-task-for-ai-agents"
    },
    {
      "title": "How to keep .env secrets and API keys away from an AI coding agent",
      "url": "https://www.rightshold.com/answers/how-to-keep-env-secrets-and-api-keys-away-from-ai-coding-agents"
    },
    {
      "title": "How to rotate API keys and tokens after a malicious VS Code or Cursor extension",
      "url": "https://www.rightshold.com/answers/how-to-rotate-api-keys-and-tokens-after-a-malicious-vs-code-or-cursor-extension"
    }
  ],
  "computed_from": "Curated guidance; license verdicts computed from Rightshold's license profiles at render time. Not legal advice.",
  "updated": "2026-10-30"
}