v1.2.0 AI Drafted

Anthropic Platform Hardening Guide

AI/ML Platform Last updated: 2026-09-25

Platform-wide security hardening for Anthropic — the Common Controls hub (SSO, organization roles, admin API keys, integration governance) shared by the Claude Enterprise, Claude Code, and Claude API & Console product guides.

View:

Overview

Anthropic is a multi-product AI platform: the claude.ai chat product (Team/Enterprise plans), Claude Code (the agentic coding tool), and the Claude API with its admin Console. Each product has a distinct hardening surface, so each has its own guide — this hub carries the Common Controls that span them: organization identity (SSO, roles), admin API key protection, and third-party integration governance. Anthropic provides a comprehensive Admin API for programmatic organization management alongside the web Console.

Product guides: Claude Enterprise (claude.ai) · Claude Code · Claude API & Console

Intended Audience

  • Security engineers managing AI tools and API integrations
  • IT administrators configuring Claude for enterprise teams
  • GRC professionals assessing AI compliance posture
  • Third-party risk managers evaluating AI platform controls

How to Use This Guide

  • L1 (Crawl): Essential controls for all organizations using Claude
  • L2 (Walk): Enhanced controls for security-sensitive environments
  • L3 (Run): Strictest controls for regulated industries

Scope

This hub covers platform-wide Anthropic controls: organization authentication (SSO/SCIM), least-privilege organization roles, admin API key protection, and third-party integration governance (pending invites, integration risk). Product-specific hardening lives in the product guides: Claude Enterprise for claude.ai workspace administration, Claude Code for the agentic coding tool (managed settings, MCP, sandboxing, CI/CD), and Claude API & Console for API keys, workspaces, data residency, and spend. Model behavior configuration (system prompts, safety settings) is out of scope.


Table of Contents

  1. Authentication & Access Controls
  2. Third-Party Integration Security
  3. Compliance Quick Reference

Product guides: Claude Enterprise (claude.ai) · Claude Code · Claude API & Console


Products in This Platform

Anthropic is a multi-product platform. This guide is the Common Controls hub — the platform-wide controls that apply across every Anthropic product. Product-specific controls live in their own guides:

Product Guide Covers
Common Controls (this guide) — Organization SSO & SCIM, least-privilege organization roles, admin API key protection, third-party integration governance
Claude Enterprise (claude.ai) Claude Enterprise guide Identity & provisioning, audit & compliance export, connectors & extensions, spend governance
Claude Code Claude Code guide Managed settings & policy deployment, extensions & supply chain, execution isolation & sandboxing, threat defense, monitoring & incident response
Claude API & Console Claude API & Console guide API key management, workspace security, data security & privacy, monitoring & usage controls

1. Authentication & Access Controls

1.1 Enforce Single Sign-On

Profile Level: L1 (Crawl)

Framework Control
CIS Controls 6.3, 12.5
NIST 800-53 IA-2, IA-8

Description

Configure SAML 2.0 or OIDC-based SSO to authenticate Claude users through your corporate identity provider. Anthropic integrates SSO via WorkOS, supporting domain verification and just-in-time provisioning.

Rationale

Why This Matters:

  • Centralizes authentication and user lifecycle management
  • Enables MFA enforcement through your IdP’s Conditional Access policies
  • Eliminates standalone Claude passwords and reduces credential sprawl
  • Automatic deprovisioning when users leave the organization

Attack Prevented: Credential theft, unauthorized access, orphaned accounts

Prerequisites

  • Claude Team or Enterprise subscription
  • SAML 2.0 or OIDC compatible identity provider (Okta, Azure AD, OneLogin, Google Workspace)
  • Organization Admin access to Claude Console
  • Domain ownership for domain verification

ClickOps Implementation

Step 1: Access Identity & Access Settings

  1. Navigate to: platform.claude.com → Settings → Identity & Access
  2. Click Configure SSO

Step 2: Configure SSO via WorkOS

  1. Select your IdP type (SAML 2.0 or OIDC)
  2. Enter Identity Provider details:
    • SSO URL: Your IdP’s SSO endpoint
    • Entity ID / Issuer: IdP entity ID
    • Certificate: X.509 certificate from IdP (for SAML)
  3. Download Claude’s Service Provider metadata for IdP configuration
  4. Map required user attributes (email, name)

Step 3: Configure Your IdP (Example: Okta)

  1. In Okta Admin: Applications → Create App Integration → SAML 2.0
  2. Enter Claude’s ACS URL and Entity ID from Step 2
  3. Configure attribute statements:
    • email → user.email
    • name → user.displayName
  4. Assign users/groups

Step 4: Verify Domain and Enforce SSO

  1. Complete domain verification (DNS TXT record)
  2. Enable Require SSO for all users
  3. Test login before full enforcement

Time to Complete: ~30 minutes

Code Implementation

Code Pack: API Script
hth-anthropic-claude-1.01-enforce-sso.sh View source on GitHub ↗
# Validate SSO enforcement by listing org members and checking for
# users who may not have authenticated via SSO.
# Note: The Admin API does not expose SSO status directly.
# This audit lists all members so you can cross-reference with your IdP.
info "Listing all organization members for SSO cross-reference audit..."
MEMBERS=$(anthropic_list_all "/v1/organizations/users") || {
  fail "1.1 Failed to list organization users"
  summary; exit 1
}

MEMBER_COUNT=$(echo "${MEMBERS}" | jq 'length')
info "Found ${MEMBER_COUNT} organization members"

# Header row printed by hand: `column -N` is util-linux only (BSD/macOS rejects it)
{
  printf 'NAME\tEMAIL\tROLE\n'
  echo "${MEMBERS}" | jq -r '.[] | "\(.name)\t\(.email)\t\(.role)"'
} | column -t -s $'\t'

pass "1.1 Member list retrieved — cross-reference with IdP to verify SSO coverage"

Validation & Testing

  1. Attempt login without SSO — should be redirected to IdP
  2. Complete SSO login — should succeed and land in Claude
  3. Remove user from IdP group — should lose Claude access
  4. Cross-reference org member list (API) with IdP directory

Expected result: All users authenticate via SSO; no standalone password logins

Monitoring & Maintenance

Ongoing monitoring:

  • Monitor IdP for failed authentication attempts to Claude
  • Review pending invites monthly for unauthorized additions

Maintenance schedule:

  • Monthly: Review org member list vs IdP directory
  • Quarterly: Rotate SSO certificates before expiration
  • Annually: Re-verify domain ownership

Operational Impact

Aspect Impact Level Details
User Experience Low Users authenticate via familiar IdP login
System Performance None No performance impact
Maintenance Burden Low Standard IdP maintenance applies
Rollback Difficulty Easy Disable SSO enforcement in Console

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC6.1 Logical access security over protected information assets
NIST 800-53 IA-2, IA-8 Identification and authentication (org users + non-org users)
ISO 27001 A.9.2.1 User registration and de-registration

1.2 Enforce Least-Privilege Organization Roles

Profile Level: L1 (Crawl)

Framework Control
NIST 800-53 AC-6, AC-6(1)
SOC 2 CC6.1, CC6.3

Description

Assign the minimum necessary organization role to each user. The role model differs by organization type, and the Admin API’s role enum is wider than either product’s assignable set — audit scripts must handle every value. Claude Console / API organizations assign user, claude_code_user, developer, billing, and admin (the Console’s Limited Developer role is a workspace role, not an organization role — see Manage Workspace Membership); above admin sit owner and primary_owner, which hold all admin permissions and additionally manage admins. Claude Enterprise (claude.ai) organizations manage members with user and managed via the API, and the enum also carries membership_admin. Limit the admin role (and its owner tiers) to a small number of trusted operators.

Rationale

Why This Matters:

  • The admin role can provision Admin API keys, manage all workspaces, and remove users; owner/primary_owner additionally manage admins — the audit boundary is the whole elevated tier, not just admin
  • Admins automatically inherit workspace_admin in every workspace
  • The Admin API deliberately prevents assigning the admin role programmatically — the update endpoint documents: “Console and API organizations accept user, developer, billing, and claude_code_user; admin cannot be assigned through the API. Claude Enterprise organizations (beta) accept user and managed”
  • Admins cannot be removed via API — only through the Console
  • A role-audit script that only recognizes the classic four roles silently miscounts organizations using claude_code_user, managed, membership_admin, or the owner tiers — the full documented enum is user, claude_code_user, developer, billing, admin, managed, membership_admin, owner, primary_owner

Attack Prevented: Privilege escalation, unauthorized admin key provisioning, insider threat

Prerequisites

  • Organization Admin access
  • Current member inventory with role justifications

ClickOps Implementation

Step 1: Review Current Role Assignments

  1. Navigate to: platform.claude.com → Settings → Members
  2. Review each member’s role
  3. Document justification for each admin and billing role holder

Step 2: Downgrade Excessive Privileges

  1. For each user with unnecessary admin access:
    • Click the user → Edit Role
    • Select developer or user as appropriate
  2. Ensure at least 2 (but no more than 3) admins remain for redundancy

Step 3: Establish Role Assignment Policy

  1. Define criteria for each role level
  2. Require approval for admin role assignments
  3. Schedule quarterly role reviews

Time to Complete: ~15 minutes

Code Implementation

Code Pack: API Script
hth-anthropic-claude-1.02-enforce-least-privilege-roles.sh View source on GitHub ↗
# Audit organization member roles across the FULL documented enum. The Admin
# API can return: user, claude_code_user, developer, billing, admin, managed,
# membership_admin, owner, primary_owner — a distribution that only counts the
# classic four silently miscounts real organizations. The elevated tier to
# limit is admin PLUS owner/primary_owner (owners hold all admin permissions
# and additionally manage admins).
info "Auditing organization member roles..."
MEMBERS=$(anthropic_list_all "/v1/organizations/users") || {
  fail "1.2 Failed to list organization users"
  summary; exit 1
}

TOTAL=$(echo "${MEMBERS}" | jq 'length')
info "Role distribution (all roles present, no value dropped):"
echo "${MEMBERS}" | jq -r 'group_by(.role) | map("  \(.[0].role)=\(length)") | .[]'

# Any role outside the documented enum is worth eyes-on (API drift or new tier).
# Exact match via IN(): `inside` does substring matching on strings.
echo "${MEMBERS}" | jq -r '.[] | select(.role | IN("user","claude_code_user","developer","billing","admin","managed","membership_admin","owner","primary_owner") | not) | "  UNDOCUMENTED ROLE: \(.role) — \(.email)"'

# Flag excessive ELEVATED-tier count: admin + owner + primary_owner
ELEVATED_COUNT=$(echo "${MEMBERS}" | jq '[.[] | select(.role == "admin" or .role == "owner" or .role == "primary_owner")] | length')
if [[ "${ELEVATED_COUNT}" -gt 3 ]]; then
  warn "1.2 ${ELEVATED_COUNT} members hold admin/owner/primary_owner — review for least privilege"
  echo "Elevated members:"
  echo "${MEMBERS}" | jq -r '.[] | select(.role == "admin" or .role == "owner" or .role == "primary_owner") | "  \(.role): \(.name) <\(.email)>"'
else
  pass "1.2 Elevated-tier count (${ELEVATED_COUNT}) is within recommended limit (<=3), total members: ${TOTAL}"
fi
# WRITE: change one member's role. Runs only when BOTH USER_ID and TARGET_ROLE
# are set. The API accepts user, developer, billing, claude_code_user (Console
# and API orgs) or user, managed (Claude Enterprise orgs); admin cannot be
# assigned through the API.
if [[ -n "${USER_ID:-}" && -n "${TARGET_ROLE:-}" ]]; then
  [[ "${USER_ID}" =~ ^[A-Za-z0-9_-]+$ ]] || { fail "1.2 USER_ID is not a valid user id"; summary; exit 1; }
  case "${TARGET_ROLE}" in
    user|developer|billing|claude_code_user|managed) ;;
    *) fail "1.2 TARGET_ROLE '${TARGET_ROLE}' is not an API-assignable role"; summary; exit 1 ;;
  esac
  info "Updating user ${USER_ID} to role '${TARGET_ROLE}'..."
  # The response is the updated user object (name, email); keep it off stdout.
  anthropic_post "/v1/organizations/users/${USER_ID}" \
    "$(jq -cn --arg role "${TARGET_ROLE}" '{role: $role}')" >/dev/null || {
    fail "1.2 Failed to update user role"
    summary; exit 1
  }
  pass "1.2 User ${USER_ID} updated to role '${TARGET_ROLE}'"
fi

Validation & Testing

  1. List all org members via Admin API — count admins <= 3
  2. Verify no user has admin role without documented justification
  3. Attempt to assign admin role via API — should fail (by design)
  4. Verify billing role holders match authorized finance contacts

Expected result: Admin role limited to 2-3 operators; all other users at minimum necessary privilege

Monitoring & Maintenance

Ongoing monitoring:

  • Alert on new admin role assignments (Console audit)
  • Monthly review of role distribution

Maintenance schedule:

  • Monthly: Review member roles via API script
  • Quarterly: Full access review with role justifications

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC6.1, CC6.3 Logical access security; role-based access
NIST 800-53 AC-6, AC-6(1) Least privilege; authorize access for security functions
ISO 27001 A.9.2.3 Management of privileged access rights

1.3 Protect Admin API Keys

Profile Level: L1 (Crawl)

Framework Control
NIST 800-53 IA-5, SC-12
SOC 2 CC6.1, CC6.6

Description

Admin credentials grant organization-wide management access — treat them with the same care as cloud provider root credentials. The credential model is now two-track, and differs by organization type. Claude Console organizations create Admin API keys (sk-ant-admin01-...) at Console → Settings → Admin keys (admin role required); Console admin keys have no selectable scopes — every key carries full access to all endpoints that accept admin keys. Claude Enterprise (claude.ai) organizations create scoped keys (sk-ant-api01-...) at claude.ai → Organization settings → API: the parent org’s primary owner can create keys for all linked organizations, while an organization owner can create only Compliance-scoped keys restricted to their own organization. Separately, the Admin API also accepts a short-lived OAuth bearer token with the org:admin scope — and the service-account, federation-issuer, and federation-rule endpoints accept only that OAuth token, never an admin key.

Rationale

Why This Matters:

  • Admin keys can list all users, manage all workspaces, disable API keys, and view usage data
  • Unlike standard API keys, Console admin keys are not scoped to a single workspace — and cannot be scoped down at all, which makes each one a full-tenant credential
  • A key created in one organization cannot manage another — organizations running both Console and Enterprise need one key per org, each inventoried separately
  • The short-lived OAuth path (org:admin) exists precisely so interactive and break-glass administration need not mint another static full-access secret

Attack Prevented: Organization takeover, unauthorized workspace creation, data exfiltration via usage APIs

Prerequisites

  • Organization Admin access (Console) / primary owner or organization owner (Enterprise)
  • Secrets management solution (Vault, AWS Secrets Manager, etc.)

ClickOps Implementation

Step 1: Audit Existing Admin Keys

  1. Navigate to: Console → Settings → Admin keys (Console orgs) and claude.ai → Organization settings → API → Keys (Enterprise orgs)
  2. Review all provisioned admin keys
  3. Identify and revoke any keys without a clear owner or purpose

Step 2: Establish Key Hygiene

  1. Name each admin key descriptively (e.g., “CI/CD Org Audit — TeamName”)
  2. Set an expiration at creation. Key expiration is chosen when the key is created (presets from 3 hours to 30 days, custom durations, or Never) and cannot be changed afterward; the same expiration choice applies to Admin API keys. If your organization has a maximum expiration policy, the Console limits the choices and Never becomes unavailable — adopt that policy so unexpiring admin keys cannot be minted at all
  3. Store keys in a secrets manager — never in source code, env files, or chat
  4. Rotate admin keys on a schedule; audit each key’s expires_at via the Admin API (the field is null for non-expiring keys — treat those as findings)

Step 3: Limit Admin Key Provisioning

  1. Restrict admin role to 2-3 trusted operators (see Control 1.2)
  2. Require documented approval before provisioning new admin keys
  3. Log all admin key creation events

Step 4: Prefer the OAuth Path for Interactive Administration

  1. For human-driven admin work, use an org:admin OAuth bearer token via the ant CLI under a dedicated profile (ant auth login --profile admin --scope "org:admin") instead of handling a static admin key — interactive tokens are short-lived and refresh on demand
  2. Reserve static admin keys for non-interactive integrations that cannot complete an OAuth flow, and consider Workload Identity Federation for CI (see the Claude API guide)

Time to Complete: ~15 minutes

Code Implementation

Code Pack: API Script
hth-anthropic-claude-1.03-protect-admin-api-keys.sh View source on GitHub ↗
# Admin API keys (sk-ant-admin...) can only be created in the Console, and
# this script cannot list them. It proves the current admin key still works;
# a dead key exits 1, so a scheduler or CI gate sees the failure.
info "Validating admin key by retrieving organization info..."
ORG_INFO=$(anthropic_get "/v1/organizations/me") || {
  fail "1.3 Admin key is invalid or expired"
  summary; exit 1
}

ORG_NAME=$(echo "${ORG_INFO}" | jq -r '.name')
ORG_TYPE=$(echo "${ORG_INFO}" | jq -r '.type')
info "Organization: ${ORG_NAME} (type: ${ORG_TYPE})"
pass "1.3 Admin API key is valid and active"

Validation & Testing

  1. Validate admin key works via /v1/organizations/me endpoint
  2. Verify admin key is stored in secrets manager (not plaintext)
  3. Confirm admin key naming convention is followed

Expected result: All admin keys are named, stored securely, and have documented owners

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC6.1, CC6.6 Logical access; security of system boundaries
NIST 800-53 IA-5, SC-12 Authenticator management; cryptographic key management
ISO 27001 A.9.4.3 Password management system

1.4 Enforce Tenant Restrictions at the Network Edge

Profile Level: L2 (Walk)

Framework Control
CIS Controls 12.3, 3.3
NIST 800-53 SC-7, AC-4

Description

Configure your egress proxy to inject the anthropic-allowed-org-ids header on traffic to Anthropic, so only your organization’s Claude tenants are reachable from corporate networks — blocking personal Claude accounts across web, desktop, API keys, and OAuth (Enterprise and Console).

Rationale

Why This Matters:

  • Product-level controls govern YOUR org; they do nothing about an employee pasting corporate data into a personal Claude account from the corporate network
  • Tenant pinning at the network edge is the platform-wide backstop that the per-product controls (connector verified-domain protection, Claude Code org pinning) each cover only partially
  • Enforcement spans surfaces: claude.ai web, desktop apps, API key usage, and OAuth flows

Attack Prevented: Corporate-data exfiltration through personal Claude tenants on managed networks

ClickOps Implementation

  1. Collect your organization IDs (claude.ai org and Console org): Console organizations show the ID at Settings → Organization; Enterprise organizations show it at Organization settings → Organization (bottom of the page) or Settings → Account
  2. Configure the egress proxy/SWG to inject anthropic-allowed-org-ids: <org-id>[,<org-id>] on requests to Anthropic domains, and to overwrite the header on every request rather than add it only when absent
  3. Roll out in monitor mode first, then enforce
  4. Pair with Claude Code login pinning (forceLoginOrgUUID) for the client-side half

Time to Complete: ~2 hours with proxy change control

Code Implementation

Anthropic exposes no API that sets tenant restrictions; the proxy does. This pack verifies enforcement from inside the managed network: a key from an organization that is not allowlisted must get HTTP 403 with tenant_restriction_violation, and an allowlisted key must still be served.

Code Pack: API Script
hth-anthropic-claude-1.04-verify-tenant-restrictions.sh View source on GitHub ↗
# Send the vendor's documented test request with a given key; the key reaches
# curl as a header file, never as an argument. Sets TR_CODE and TR_ERROR.
tr_probe() {
  local key="$1"
  TR_CODE=$(curl -sS -o "${BODY_FILE}" -w '%{http_code}' "${ANTHROPIC_API_BASE}/v1/messages" \
    -H @<(printf 'x-api-key: %s\n' "${key}") \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d "$(jq -cn --arg m "${HTH_TR_MODEL}" '{model: $m, max_tokens: 1, messages: [{role: "user", content: "Hello"}]}')" \
    2>/dev/null) || TR_CODE="000"
  TR_ERROR=$(jq -r '.error.error_code // .error.message // "none"' "${BODY_FILE}" 2>/dev/null || echo "unparseable")
  [[ -n "${TR_ERROR}" ]] || TR_ERROR="no response body"
}

# 1. A key from an org that is NOT allowlisted must be refused by tenant restriction.
tr_probe "${HTH_TR_BLOCKED_KEY}"
if [[ "${TR_CODE}" == "403" && "${TR_ERROR}" == "tenant_restriction_violation" ]]; then
  echo "[PASS] 1.4 non-allowlisted org refused: HTTP 403 tenant_restriction_violation"
elif [[ "${TR_CODE}" == "400" && "${TR_ERROR}" == *anthropic-allowed-org-ids* ]]; then
  echo "[FAIL] 1.4 proxy header misconfigured: HTTP 400 (${TR_ERROR})"; exit 1
elif [[ "${TR_CODE}" =~ ^2 ]]; then
  echo "[FAIL] 1.4 NOT ENFORCED: a non-allowlisted org's key was served (HTTP ${TR_CODE})"; exit 1
else
  echo "PRECONDITION: inconclusive — HTTP ${TR_CODE} (${TR_ERROR}); check the key and that this host routes through the proxy" >&2
  exit 2
fi

# 2. Optional: a key from an allowlisted org must still get through.
if [[ -n "${HTH_TR_ALLOWED_KEY:-}" ]]; then
  tr_probe "${HTH_TR_ALLOWED_KEY}"
  if [[ "${TR_CODE}" =~ ^2 ]]; then
    echo "[PASS] 1.4 allowlisted org still served (HTTP ${TR_CODE})"
  else
    echo "[FAIL] 1.4 allowlisted org refused: HTTP ${TR_CODE} (${TR_ERROR})"; exit 1
  fi
fi

Validation & Testing

  1. From a corporate network, sign-in to a personal Claude account is refused
  2. Org-tenant access works unchanged across web, desktop, and API

Expected result: Only sanctioned tenants reachable from managed networks. (Tenant Restrictions)

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC6.7 Restrict transmission of information
NIST 800-53 SC-7, AC-4 Boundary protection; information flow enforcement

2. Third-Party Integration Security

2.1 Audit and Clean Up Pending Invites

Profile Level: L1 (Crawl)

Framework Control
NIST 800-53 AC-2(3)
SOC 2 CC6.2

Description

Regularly audit pending organization invites. Invites in Anthropic expire after 21 days (not configurable). Stale or unauthorized invites should be revoked promptly. The invite system supports role assignment at invite time, so a malicious invite could grant elevated access.

Rationale

Why This Matters:

  • Pending invites represent pre-authorized access that hasn’t been claimed
  • An attacker who gains access to an invited email account could join the organization
  • Invites specify the role upfront — verify that invited roles follow least privilege

Attack Prevented: Unauthorized organization access via intercepted/forwarded invites

Prerequisites

  • Organization Admin access

ClickOps Implementation

Step 1: Review Pending Invites

  1. Navigate to: platform.claude.com → Settings → Members → Invites
  2. Review each pending invite: email, role, creation date
  3. Revoke any invite where the recipient is unknown or no longer needed

Step 2: Establish Invite Policy

  1. Require approval before sending invites
  2. Review pending invites weekly
  3. Use the lowest necessary role for each invite (invites cannot use admin role via API)

Time to Complete: ~5 minutes

Code Implementation

Code Pack: API Script
hth-anthropic-claude-6.01-audit-pending-invites.sh View source on GitHub ↗
# List all pending invites and identify stale ones
info "Listing all organization invites..."
INVITES=$(anthropic_list_all "/v1/organizations/invites") || {
  fail "2.1 Failed to list invites"
  summary; exit 1
}

TOTAL=$(echo "${INVITES}" | jq 'length')
PENDING=$(echo "${INVITES}" | jq '[.[] | select(.status == "pending")] | length')
EXPIRED=$(echo "${INVITES}" | jq '[.[] | select(.status == "expired")] | length')
ACCEPTED=$(echo "${INVITES}" | jq '[.[] | select(.status == "accepted")] | length')

info "Invites: total=${TOTAL}, pending=${PENDING}, expired=${EXPIRED}, accepted=${ACCEPTED}"

if [[ "${PENDING}" -gt 0 ]]; then
  warn "2.1 ${PENDING} pending invites found — review for stale or unauthorized invitations:"
  echo "${INVITES}" | jq -r '.[] | select(.status == "pending") |
    "  \(.email) | Role: \(.role) | Invited: \(.invited_at) | Expires: \(.expires_at)"'
else
  pass "2.1 No pending invites"
fi
# WRITE: revoke one pending invite. Runs only when INVITE_ID is set.
if [[ -n "${INVITE_ID:-}" ]]; then
  [[ "${INVITE_ID}" =~ ^[A-Za-z0-9_-]+$ ]] || { fail "2.1 INVITE_ID is not a valid invite id"; summary; exit 1; }
  info "Revoking invite ${INVITE_ID}..."
  anthropic_delete "/v1/organizations/invites/${INVITE_ID}" || {
    fail "2.1 Failed to revoke invite"
    summary; exit 1
  }
  pass "2.1 Invite ${INVITE_ID} revoked"
fi

Validation & Testing

  1. Run invite audit script — verify no stale pending invites
  2. Attempt to create invite with admin role via API — should fail (by design)
  3. Confirm expired invites cannot be accepted

Expected result: All pending invites are reviewed, authorized, and time-bound

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC6.2 Prior to issuing system credentials and granting system access
NIST 800-53 AC-2(3) Account management — disable accounts
ISO 27001 A.9.2.1 User registration and de-registration

2.2 Integration Risk Assessment

Profile Level: L2 (Walk)

Framework Control
NIST 800-53 RA-3, SA-9
SOC 2 CC3.2, CC9.2

Description

Assess the security posture of applications and services that consume your Claude API keys. API keys are bearer tokens — any application with the key can make requests on your behalf within the workspace scope.

Rationale

Why This Matters:

  • Any application holding your API key can generate costs and access model capabilities
  • Third-party tools (LangChain, LlamaIndex, custom applications) embed API keys
  • A compromised third-party application with your key = a compromised key

Attack Prevented: Key compromise via insecure third-party applications, unauthorized API usage through embedded bearer tokens

Integration Risk Assessment Matrix

Risk Factor Low Medium High
Data Sensitivity Non-sensitive prompts Internal business data PII, financial, health data
Key Scope Dedicated low-limit workspace Shared development workspace Production workspace
Application Trust First-party, audited code Vendor with SOC 2 Unaudited open-source tool
Key Storage Secrets manager Environment variable Hardcoded or config file
Rate Limit Strict per-workspace limits Moderate limits Organization-level defaults

Decision Matrix:

  • 0-5 points: Standard controls (workspace scoping, naming)
  • 6-10 points: Enhanced controls (dedicated workspace, low spend limits, key rotation)
  • 11-15 points: Reject or isolate (dedicated workspace with minimum limits, frequent rotation, monitoring)

ClickOps Implementation

Step 1: Inventory the Keys and Their Consumers

  1. Navigate to: platform.claude.com → Settings → API keys
  2. For each active key, record the application that holds it, the owner, and the key’s workspace

Step 2: Score Each Integration

  1. Score every integration against the risk matrix above
  2. Record the rating and owner next to the key in your inventory

Step 3: Apply Controls by Score

  1. For integrations scoring 6 or more, navigate to: platform.claude.com → Settings → Workspaces and create a dedicated workspace for that consumer
  2. On the workspace’s Spend limits and Rate limits tabs, set limits below the organization’s (a workspace limit can be lower than, but not higher than, the organization’s; the Default Workspace cannot carry limits at all)
  3. Issue the consumer a new key scoped to that workspace, then disable the old key on the API keys page

Time to Complete: ~1 hour per 10 integrations

Code Implementation

The scoring stays a human judgement. This read-only pack supplies the matrix’s Key Scope factor: every active key, labelled by the workspace it belongs to, the Default Workspace, or the organization (a principal-bound key with no workspace).

Code Pack: API Script
hth-anthropic-claude-6.02-integration-key-inventory.sh View source on GitHub ↗
# Inventory every ACTIVE API key by where it can act (the matrix's Key Scope).
info "Listing active API keys and workspaces..."
KEYS=$(anthropic_list_all "/v1/organizations/api_keys?status=active") || {
  fail "2.2 Failed to list API keys"
  summary; exit 1
}
WORKSPACES=$(anthropic_list_all "/v1/organizations/workspaces") || {
  fail "2.2 Failed to list workspaces"
  summary; exit 1
}

# Label each key: a named workspace, the Default Workspace, or ORGANIZATION
# (principal-bound, no workspace). Only scope.type decides the tier.
INVENTORY=$(echo "${KEYS}" | jq -c --argjson ws "${WORKSPACES}" '
  ($ws | map({key: .id, value: .name}) | from_entries) as $names
  | map(
      (.scope.workspace_id // "") as $wid
      | (if .scope.type == "organization" then "organization"
       elif .scope.type == "workspace" and ($names | has($wid)) then "workspace"
       elif .scope.type == "workspace" and .workspace_id == null then "default"
       elif .scope.type == "workspace" then "workspace"
       else "unknown" end) as $tier
      | {
          name: (.name // "unnamed"),
          principal: (.principal.type // "unbound"),
          expires: (.expires_at // "never"),
          tier: $tier,
          where: (if $tier == "organization" then "ORGANIZATION (no workspace)"
                  elif $tier == "default" then "Default Workspace"
                  elif $tier == "workspace" then ($names[$wid] // $wid)
                  else "UNKNOWN (no scope field)" end)
        })')

TOTAL=$(echo "${INVENTORY}" | jq 'length')
ORG_SCOPED=$(echo "${INVENTORY}" | jq '[.[] | select(.tier == "organization")] | length')
DEFAULT_WS=$(echo "${INVENTORY}" | jq '[.[] | select(.tier == "default")] | length')
UNKNOWN=$(echo "${INVENTORY}" | jq '[.[] | select(.tier == "unknown")] | length')
NEVER_EXPIRE=$(echo "${INVENTORY}" | jq '[.[] | select(.expires == "never")] | length')
info "Active keys: ${TOTAL} (organization-scoped=${ORG_SCOPED}, default-workspace=${DEFAULT_WS}, never-expiring=${NEVER_EXPIRE})"

{
  printf 'WHERE\tKEY NAME\tPRINCIPAL\tEXPIRES\n'
  echo "${INVENTORY}" | jq -r 'sort_by(.where) | .[] | "\(.where)\t\(.name)\t\(.principal)\t\(.expires)"'
} | column -t -s $'\t'

# Shared workspaces are the matrix's Medium tier: one workspace, several consumers
echo "${INVENTORY}" | jq -r '[.[] | select(.tier == "workspace" or .tier == "default")]
  | group_by(.where) | map(select(length > 1)) | .[]
  | "  shared: \(.[0].where) holds \(length) active keys"'

if [[ "${ORG_SCOPED}" -gt 0 || "${DEFAULT_WS}" -gt 0 || "${UNKNOWN}" -gt 0 ]]; then
  warn "2.2 ${ORG_SCOPED} organization-scoped, ${DEFAULT_WS} Default Workspace, ${UNKNOWN} unclassified key(s) — give each consumer a dedicated workspace with its own limits"
else
  pass "2.2 Every active key belongs to a named workspace"
fi

Validation & Testing

  1. Maintain inventory of all applications using Claude API keys
  2. Verify each application’s key is in an appropriately scoped workspace
  3. Confirm keys are stored in secrets managers, not in source code

Expected result: All API key consumers are inventoried with risk ratings and appropriate controls

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC3.2, CC9.2 Risk assessment; vendor and third-party risk management
NIST 800-53 RA-3, SA-9 Risk assessment; external system services
ISO 27001 A.15.1.2 Addressing security within supplier agreements

3. Compliance Quick Reference

These tables cover this hub’s six controls only. The SOC 2, NIST 800-53 and ISO 27001 IDs are taken from the controls’ own mapping tables. The NIST CSF 2.0 and AI RMF tables map the same controls at category level only, and no control’s own table repeats those categories. Each product guide (Claude Enterprise, Claude Code, Claude API & Console) carries the mappings for its own controls.

SOC 2 Trust Services Criteria Mapping

Control ID Anthropic Claude Control Guide Section
CC3.2 Integration Risk Assessment 2.2
CC6.1 Enforce SSO, Least-Privilege Roles, Admin API Key Protection 1.1, 1.2, 1.3
CC6.2 Pending Invite Audit 2.1
CC6.3 Least-Privilege Roles 1.2
CC6.6 Admin API Key Protection 1.3
CC6.7 Tenant Restrictions 1.4
CC9.2 Integration Risk Assessment 2.2

NIST 800-53 Rev 5 Mapping

Control Anthropic Claude Control Guide Section
AC-2(3) Pending Invite Audit 2.1
AC-4 Tenant Restrictions (information flow) 1.4
AC-6, AC-6(1) Least-Privilege Roles 1.2
IA-2 SSO Enforcement 1.1
IA-5 Admin API Key Protection 1.3
IA-8 Non-Organization User Authentication 1.1
RA-3 Integration Risk Assessment 2.2
SA-9 External System Services 2.2
SC-7 Boundary Protection (Tenant Restrictions) 1.4
SC-12 Cryptographic Key Management 1.3

ISO 27001 Mapping

Control IDs use the ISO/IEC 27001:2013 Annex A numbering, as in each control’s own table.

Control Anthropic Claude Control Guide Section
A.9.2.1 User Registration and De-registration 1.1, 2.1
A.9.2.3 Privileged Access Management 1.2
A.9.4.3 Password/Key Management 1.3
A.15.1.2 Supplier Security 2.2

NIST Cybersecurity Framework (CSF) 2.0 Mapping

Function.Category Anthropic Claude Control Guide Section
PR.AA (Protect: Identity Management, Authentication, and Access Control) SSO, least-privilege roles, admin key protection, tenant restrictions 1.1, 1.2, 1.3, 1.4
GV.SC (Govern: Cybersecurity Supply Chain Risk Management) Risk rating of third-party applications that hold API keys 2.2

NIST AI Risk Management Framework (AI RMF) Mapping

Function Anthropic Claude Control Guide Section
GOVERN 2 (Accountability) SSO, defined admin roles, tenant restrictions 1.1, 1.2, 1.4
GOVERN 6 (Third-Party & Supply Chain) Risk rating of third-party applications that hold API keys 2.2

Appendix A: Edition/Tier Compatibility

This table covers this hub’s controls. Product-specific availability is in each product guide.

Control API (All Tiers) Team Enterprise
1.1 Enforce SSO N/A (API-only) ✅ ✅
1.2 Least-Privilege Roles ✅ ✅ ✅
1.3 Admin Key Protection ✅ ✅ ✅
1.4 Tenant Restrictions ✅ (Console org IDs can be allowlisted) Not documented ✅
2.1 Invite Auditing ✅ ✅ ✅
2.2 Integration Risk ✅ ✅ ✅
SCIM Provisioning ❌ ❌ ✅
Audit Logs ❌ ❌ ✅

Appendix B: References

Official Anthropic Documentation:

Identity Provider Integration:

Claude Code Security:

Open-Source Security Tools:

Security Research:

Claude Cowork:

Security and Compliance:


Changelog

Date Version Maturity Changes Author
2026-09-25 1.2.0 ai-drafted validate-hth-guide run (Phases 4-6, including an independent audit). The live Console stayed behind its sign-in wall and no Admin API key could be minted, so 0 surfaces were exercised live and no status was added; packs were exercised offline against a mock of the documented Admin API. Fixes: the shared POST helper appended a stray } to every request body, so 1.02’s role change could never send valid JSON (now fixed, and 1.02 keeps the API’s response, a member record, off stdout); packs 1.01, 1.02, 1.03 and 6.01 now exit 1 when an API call fails (they printed [FAIL] and exited 0) and page through has_more/last_id with limit=1000 (the shared helper read a next_page field the API never returns, so every list stopped at 20 entries); the admin key reaches curl as a header file instead of a command-line argument; 1.01 drops the Linux-only column -N; 6.01 prints invited_at (invites have no created_at); 1.02 matches undocumented roles exactly; 1.02 and 6.01 now declare mode: mutating for their opt-in write branches, which also validate their inputs. New read-only packs: 1.04 verifies tenant restrictions from inside the network against the documented HTTP 403 tenant_restriction_violation; 6.02 inventories active API keys by scope (workspace, Default Workspace, organization) for 2.2. §2.2 gains ClickOps steps (Settings → API keys, workspace limits under Settings → Workspaces). §1.4 names where each organization type shows its organization ID and says to overwrite the header. §1.2: Limited Developer is a workspace role, not an organization role. §3 and Appendix A rebuilt around this hub’s six controls (they cited sections 3.1-7.11, which now live in the product guides); CSF 2.0 PR.AC corrected to PR.AA; ISO IDs labelled as 2013 Annex A numbering; §3 now says its CSF 2.0 and AI RMF rows are category-level mappings that no control table carries; §1.4’s Compliance Mappings table gains AC-4, which its header table already listed. Usage Policy link moved to anthropic.com/legal/aup; doc_links.yml points at platform.claude.com and academy.claude.com directly. Correction to the 1.1.0 row: Anthropic documents the OAuth access-token prefix quoted there for tokens issued by POST /v1/oauth/token (WIF reference); the prefix of interactive ant auth login --scope "org:admin" tokens is not documented and was not verified. Claude Code (Opus 5.5)
2026-08-15 1.1.0 ai-drafted Admin API currency pass against platform.claude.com. §1.2 role model corrected to the two-org-type picture: five Console/API roles plus the Console UI’s Limited Developer tier, owner/primary_owner above admin, managed/membership_admin on the Enterprise side, and the full nine-value API role enum documented so audit scripts stop silently miscounting; the admin-assignment and admin-removal API blocks are now quoted from the current references. §1.3 rebuilt on the two-track credential model — Console admin keys (sk-ant-admin01-, no selectable scopes) vs Claude Enterprise scoped keys (sk-ant-api01-, 12-scope table, primary-owner/org-owner creation split) — plus key expiration at creation, the organization maximum-expiration policy, the org:admin OAuth bearer path via the ant CLI (dedicated admin profile, sk-ant-oat01- prefix added for secret-scanning coverage), and the WIF-endpoints carve-out (service-account/federation endpoints reject admin keys). References moved to canonical platform.claude.com URLs — two of the old docs.anthropic.com links were dead 404s. Roles pack rewritten to count the full enum and the elevated tier; rotation pack now audits expires_at. Claude Code (Opus 5)
2026-02-21 0.1.0 ai-drafted Initial guide: 12 controls across 6 categories, API pack scripts for Admin API Claude Code (Opus 4.6)
2026-02-21 0.2.0 ai-drafted Added Section 7: Claude Code Enterprise Controls — MDM managed settings, permission restrictions, MCP server control, developer analytics Claude Code (Opus 4.6)
2026-02-21 0.3.0 ai-drafted Added MDM config templates (L1/L2/L3 profiles), permission deny rule examples, sandbox config, managed-mcp.json template, MCP allowlist/denylist config Claude Code (Opus 4.6)
2026-02-21 0.4.0 ai-drafted Added Config-as-Code pack type with standalone .jsonc config files; added code pack buttons, doc links; moved JSON configs from API scripts to config/ directory Claude Code (Opus 4.6)
2026-03-27 0.5.0 ai-drafted Major expansion: Added 6 new controls (7.5-7.10) — Bash sandbox isolation, hook/plugin lockdown, prompt injection defense, CI/CD pipeline hardening, external sandbox tooling (nono, OpenShell), Cowork governance. Updated 7.1 with drop-in directory, plist/registry delivery, new managed settings. Added comprehensive references for security research (ToxicSkills, Rules File Backdoor, InversePrompt CVEs) and open-source tools. Updated all compliance mappings. Claude Code (Opus 4.6)
2026-04-06 0.6.0 ai-drafted Added 7.11 Incident Response (kill-switch, forensic collection, AI agent IR scenarios, tabletop exercises). Major expansion of 7.10 Cowork: Chrome hardening (allowlist/blocklist, default gap warnings), global defensive instructions, dedicated workspace scoping, scheduled task governance, plugin install preferences, tenant restriction details (exact header format, proxy platforms, error codes), data training opt-out by tier, web search egress bypass warning, OTel prompt content toggle. Added NIST CSF 2.0 and NIST AI RMF compliance mappings. Added web search bypass warning to 7.5 sandbox. Claude Code (Opus 4.6)
2026-05-06 0.7.0 ai-drafted Added 2.3 Eliminate Static API Keys via Workload Identity Federation: covers Anthropic’s new WIF capability (RFC 7523 jwt-bearer flow against POST /v1/oauth/token), Console setup walkthrough for federation issuers / service accounts / federation rules, IdP coverage (AWS / GCP / Azure-Entra / GitHub Actions / Kubernetes / SPIFFE / Okta), credential precedence pitfalls (ANTHROPIC_API_KEY shadowing WIF), token lifetime/refresh semantics, JWKS rotation gotchas, and migration runbook. Code Packs: token-exchange script with static-key guardrail (api/), reference hardened GitHub Actions workflow (config/), config-as-code WIF profile (config/), Python SDK pattern (sdk/). Verified against platform.claude.com/docs/en/manage-claude/workload-identity-federation and wif-reference. Claude Opus 4.7 (1M)
2026-08-08 1.0.1 ai-drafted Added “Products in This Platform” navigation section (product guide table mirroring the Google Workspace hub) so the Anthropic hub lists Claude Enterprise, Claude Code, and Claude API & Console with their coverage. No control changes. Claude Opus 5 (1M)
2026-08-08 1.0.2 ai-drafted Cheat-sheet cell repair: added missing Attack Prevented line(s) to §2.2 (no content-facts changed) Claude Code (Fable 5)

Contributing

Found an issue or want to improve this guide?