Skip to main content

Service accounts: client secret

Time: ~5 minutes. The simplest machine credential: the service account holds an ID and a shared secret. Right for quick starts and simple deployments — if the caller runs in Kubernetes or your security posture forbids shared secrets, start at the comparison instead.

Step 1 — Create the service account

Open WORKSPACE → Service Accounts in the sidebar. Each service account is a machine principal holding one credential, granted access to specific MCP servers independently of any user session. Click + Create service account:

Service Accounts page

Name it after the thing that will run it (reporting-pipeline, ci-deployer), keep Client secret selected — it's the default — and click Create service account:

Create service account — client secret

Step 2 — Save the credentials (shown once)

Client secret credentials

Copy both values into the caller's environment — the secret is not shown again. It's 64 hex characters: copy-paste it, never retype it.

AUTHSEC_ISSUER=https://mcpauthz.com
SA_CLIENT_ID=<client id from the dialog>
SA_CLIENT_SECRET=<the secret you copied>
MCP_URL=https://your-mcp-server.example.com/mcp

Step 3 — Grant access (identity ≠ permission)

Creating the account gives it an identity, not permissions. Until you grant it a role, every token request fails with access_denied: client not authorized for this resource server.

Open your application → Access tab → Add accessMachine credential (secret/key) → pick this service account and the role it should hold (e.g. Readonly). The account appears in the Who has access list with its role and effective scopes.

Step 4 — Get a token and call the server

The caller trades its credential for a short-lived, scoped access token. With the Python SDK:

from authsec_sdk import AgentIdentity, ClientSecretAuth

agent = AgentIdentity(ISSUER, SA_CLIENT_ID, auth=ClientSecretAuth(SA_CLIENT_SECRET))
async with agent:
token = await agent.access_for(MCP_URL, requested_scopes=["my_mcp:read"])
# → Authorization: Bearer {token} on MCP requests

Full runnable program, verification helper, and every error we hit while building it: Python SDK — client secret.

Step 5 — Verify

  • The application's Connections tab shows the account as an active (m2m) connection with the role and scopes it was granted through.
  • MONITOR → M2M Logs shows each token grant as it happens.
  • A tools/list call with the token returns only the tools the granted scopes allow.

Revoking

Application → Access tab → Who has access → the account's ⋯ menu → Revoke access. This kills its access to this application only; other grants are untouched.

Troubleshooting

ErrorCauseFix
invalid_client: invalid client secretTypo'd or rotated secretCopy-paste from the dashboard, never retype
access_denied: client not authorized for this resource serverCredential is valid but no grant existsStep 3 — assign a role on the target application

Next

A shared secret is the floor, not the ceiling:

→ Upgrade to Private-key JWT — no secret ever crosses the wire.