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:

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:

Step 2 — Save the credentials (shown once)

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 access →
Machine 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/listcall 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
| Error | Cause | Fix |
|---|---|---|
invalid_client: invalid client secret | Typo'd or rotated secret | Copy-paste from the dashboard, never retype |
access_denied: client not authorized for this resource server | Credential is valid but no grant exists | Step 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.