Prerequisites
- An Authdog project and environment.
- A machine-to-machine application created in the Authdog console.
- Client ID and client secret captured at creation, the secret is shown once.
- A trusted server, worker, job, or agent runtime. Client credentials must never be placed in browser code.
- An API or MCP server that validates bearer tokens and enforces authorization.
Use a separate M2M application per workload and environment, so one compromised workload can be replaced without rotating unrelated automation.
Implementation
1. Request a token
Discover the token endpoint from OIDC metadata (/oauth2/token) rather than hard-coding an origin. Send client credentials with HTTP Basic auth:
curl -X POST "$TOKEN_ENDPOINT" \
-u "$AUTHDOG_CLIENT_ID:$AUTHDOG_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials"The response contains access_token, token_type, and expires_in. There's no refresh token, repeat the exchange before expiry.
2. Send the token to the protected service
Authorization: Bearer <access_token>For an MCP service, the repository's examples/mcp-auth-sample authenticates before the transport runs; its packages/mcp-sdk verifies JWT signatures through JWKS and can check issuer, audience, and scopes.
3. Layer the boundary
Authenticate every request before dispatch, reject the wrong issuer or audience, require a baseline scope for the service, then apply narrower scope checks again at sensitive tool boundaries. Treat authenticated identity as input to authorization, not blanket permission.
4. Cache carefully
Keep an access token only in trusted process memory until shortly before expires_in. When many agent instances refresh concurrently, add jitter so they don't all request tokens at once.
Security considerations
- Store client secrets in a secret manager, never in prompts, command history, or logs.
- Use least privilege: separate a service-wide scope (
mcp:invoke) from tool-specific scopes (mcp:admin). - Don't give unattended automation a human user's cookie or personal token, M2M exists specifically for non-human workloads.
- Record client ID and workload name in audit context, but redact secrets and complete access tokens.
- On suspected exposure, replace the client's credentials rather than waiting for issued tokens to expire.
Validation checklist
- Agent obtains a token with
client_credentials, without a browser or user session. - Invalid client credentials are rejected by the token endpoint.
- Protected service rejects missing, expired, or wrong-audience tokens.
- Baseline scope failure prevents request dispatch.
- Sensitive operations enforce their own narrower scope.
- Development token does not grant production access.
- Logs contain workload identifiers but no secrets or complete tokens.
Troubleshooting
Token exchange fails: confirm the client is active, uses the expected auth method, and is authorized for client_credentials.
Protected service returns 401: inspect issuer, JWKS URI, audience, and bearer-header formatting. A 403 means authentication succeeded but a required scope is missing.
Failures occur only across environments: verify the agent uses the client and endpoint for the same environment as the protected service.
Next steps
- Read Machine identity for M2M applications, service accounts, and personal access tokens.
- Read Backend requests for trusted server validation.
- Read Authorization before assigning agent permissions.
- Use
examples/mcp-auth-sampleandpackages/mcp-sdkwhen the protected agent interface is MCP.