Join our Newsletter — 33% off our NHI Course
Home› Guides› Machine-to-Machine OAuth (Client Credentials) Guide: Securing Service Access
Guide Non-Human Identity (NHI)

Machine-to-Machine OAuth (Client Credentials) Guide: Securing Service Access

PDF for newsletter subscribers · free to read online

← All guides
By Lalit Choda, NHI Mgmt Group Updated 24 October 2026 15 min read
On this page

The OAuth client credentials grant gives a service its own access with no user in the loop, so whoever holds the client credential holds that access, often tenant-wide and long after anyone remembers the client exists. Machine-to-machine (M2M) OAuth is how backend services, scheduled jobs, integrations and increasingly AI agents get access tokens for APIs. It is simple to set up with a client ID and secret, and that simplicity is the problem: shared secrets leak, application permissions are broad, and tokens are usually bearer tokens anyone can replay. This guide covers how the grant works, which client authentication method to choose, scoping and audience restriction, sender-constrained tokens, resource server validation, platform specifics, rollout, detection and the common mistakes. It is for platform, API and identity engineers; for user-facing flows see the OAuth 2.0 and OpenID Connect Guide, and for acting on behalf of a user see the OAuth Token Exchange and Delegation Guide.

Key takeaways

  • The client is the identity. With no user, the client credential is the only thing standing between an attacker and every permission the client holds, so treat it as a privileged NHI credential.
  • Replace shared secrets with asymmetric authentication. RFC 9700 recommends mTLS or private_key_jwt over client secrets; better still, use workload identity federation so there is no long-lived credential at all.
  • Scope and audience every token. Request one resource and the minimum scopes, and make resource servers reject tokens not issued for them.
  • Sender-constrain high-value tokens. mTLS certificate binding (RFC 8705) or DPoP (RFC 9449) makes a stolen access token useless without the client's private key.
  • Resource servers must enforce, not assume. Some platforms issue app-only tokens with no roles at all; the API has to check issuer, audience, client and permissions itself.

How the client credentials grant works

RFC 6749 section 4.4 defines the client credentials grant: the client authenticates to the authorisation server's token endpoint and receives an access token for resources it controls, or that have been granted to it. The grant MUST only be used by confidential clients, meaning clients that can keep a credential secret, such as server-side services. It must never be used from browsers, mobile apps or desktop software distributed to users.

  1. The client sends a POST to the token endpoint with grant_type=client_credentials, an optional scope (or a resource indicator) and its client authentication.
  2. The authorisation server authenticates the client, checks which permissions the client has been granted and issues an access token, usually with token_type Bearer and an expires_in value.
  3. The client calls the API with the token. When it expires, the client simply authenticates again: no refresh token is needed, and RFC 6749 says one should not be issued for this grant. Microsoft Entra ID, for example, never issues refresh tokens in this flow.

If the server grants fewer scopes than requested it must return the granted scope in the response (RFC 6749 section 3.3), so clients should read it rather than assume. The client must cache tokens until shortly before expiry: fetching a token per request multiplies load, cost (Amazon Cognito bills M2M by active app clients and token request volume) and the number of credential presentations.

Client credentials is not the right grant when a service acts for a specific user; use the authorisation code grant for the user, then token exchange (RFC 8693) for downstream calls, so the user context is kept. See the Human vs Non-Human Identity guide for the distinction.

Choosing a client authentication method

MethodHow the client proves itselfCredential at restAssuranceUse when
client_secret_basicClient ID and secret in an HTTP Basic header (RFC 6749 section 2.3.1)Shared secret on client and serverLowOnly if nothing better is supported; rotate and vault it
client_secret_postSecret in the request body; RFC 6749 calls this NOT RECOMMENDEDShared secretLowAvoid; never put secrets in the URI
private_key_jwt (RFC 7523)JWT signed with the client's private key, sent as client_assertionPrivate key on client only; server holds public key or JWKS URIHighDefault choice for services that can hold a key
tls_client_auth / self_signed_tls_client_auth (RFC 8705)X.509 client certificate in the TLS handshakePrivate key on client onlyHighmTLS already in place end to end; FAPI-style APIs
Federated assertion (workload identity federation)Token from the workload's platform (Kubernetes, CI, cloud) presented as client_assertionNone long-lived; the platform issues short-lived tokensHighWorkloads running on platforms with their own identity provider

RFC 9700 section 2.5 recommends asymmetric methods such as mTLS or private_key_jwt over shared secrets. Microsoft states that certificates and federated credentials give higher assurance than secrets, and that the certificate flow differs from the secret flow only by replacing client_secret with client_assertion_type and client_assertion.

Building a private_key_jwt assertion correctly

  • Type: client_assertion_type is urn:ietf:params:oauth:client-assertion-type:jwt-bearer.
  • Issuer and subject: for client authentication, sub must be the client ID; iss identifies the issuer, normally also the client ID.
  • Audience: aud must identify the authorisation server (the token endpoint URL may be used), and the server must reject assertions not addressed to it.
  • Lifetime: exp is required; keep it to a few minutes. Servers may also reject an iat unreasonably far in the past.
  • Replay: include a unique jti. RFC 7523 makes jti tracking optional for servers, so check whether yours does; if not, very short lifetimes are your replay control.
  • Errors: an invalid client assertion returns invalid_client; log these, as they show broken clients or forged attempts.

Workload identity federation removes the stored key altogether. Microsoft Entra ID, for instance, accepts a JWT issued by another identity provider such as Kubernetes as the client_assertion once it is registered as a federated identity credential. See the Workload Identity Federation Guide and the Kubernetes Service Account Token Guide.

Scopes, audiences and least privilege

RFC 9700 section 2.3 says access tokens should be audience-restricted to a specific resource server and limited to the minimum privileges needed, using scope or authorization_details (RFC 9396 rich authorisation requests). Resource indicators (RFC 8707) let the client name the intended resource in the token request.

  • One resource per token: Microsoft Entra requires the scope to name a single resource with the /.default suffix (for example https://graph.microsoft.com/.default), which returns all application permissions granted for that resource. That makes the grant itself the control: what an admin consents to is what every token carries.
  • Application permissions are broad by design: in Entra, client credentials can only use application permissions (app roles), which need administrator consent and can reach data across the whole organisation. Where the API supports it, narrow them, for example by scoping Exchange Online application access to specific mailboxes.
  • Custom scopes per action: Amazon Cognito uses resource servers with scopes named resourceServerIdentifier/scopeName (for example orders-api/orders.read). Define read and write scopes separately and grant them per client.
  • Check platform gaps: Cognito documents that RFC 8707 resource binding is not available for client credentials grants, so audience separation there depends on separate resource servers and scopes.
  • One client per workload: never share a client ID across services or environments. Shared clients make attribution impossible and turn one leak into many compromises.
PlatformPermission model for M2MKey settingGotcha
Microsoft Entra IDApplication permissions (app roles) with admin consentAssignment required on the resource appTokens can be issued without a roles claim; the API must check permissions itself
Amazon CognitoCustom scopes on resource servers, linked to an app client with a secretALLOW_CLIENT_TOKEN_AUTH for GetClientTokenClient secret required; up to two active secrets for rotation; no RFC 8707 binding for M2M
Generic OAuth serverScopes, audiences and per-client policyRegistered token_endpoint_auth_method per clientDefaults often allow secrets and long token lifetimes

Sender-constrained tokens: mTLS and DPoP

A bearer token works for anyone who holds it. RFC 9700 section 2.2.1 says authorisation and resource servers should use sender-constraining mechanisms, naming mTLS (RFC 8705) and DPoP (RFC 9449).

AspectmTLS certificate-bound tokens (RFC 8705)DPoP (RFC 9449)
Bindingcnf claim with x5t#S256 certificate thumbprintcnf claim with jkt JWK thumbprint
Proof at the APISame client certificate in the TLS handshakeDPoP header: JWT with typ dpop+jwt, public jwk, claims jti, htm, htu, iat, ath
Token typeBearer token checked against the certificateAuthorization: DPoP with token_type DPoP
LayerTransportHTTP application layer
Works through TLS-terminating proxiesOnly if the proxy forwards the verified certificate securelyYes
Replay controlsInherent in the handshakeShort proof window, jti tracking, server nonces via DPoP-Nonce
Discoverytls_client_certificate_bound_access_tokensdpop_signing_alg_values_supported; clients can register dpop_bound_access_tokens

DPoP is not a client authentication method and combines with any of them, including private_key_jwt. Resource servers must reject a DPoP-bound token presented as a plain Bearer token, otherwise the binding is decorative. See the mTLS Guide for the transport side and the OAuth 2.1, GNAP and FAPI Guide for profiles that require these mechanisms.

Validating M2M tokens at the resource server

RFC 9068 defines a JWT profile for access tokens with the header typ at+jwt and required claims iss, exp, aud, sub, client_id, iat and jti. For grants with no resource owner, such as client credentials, sub identifies the client application.

  1. Reject any token whose typ is not at+jwt (or application/at+jwt); this stops OpenID Connect ID tokens being accepted as access tokens.
  2. Check iss exactly matches the expected issuer and fetch keys only from that issuer's metadata.
  3. Check aud contains this API's own identifier.
  4. Verify the signature with the expected algorithm, never none, and do not let the token's header choose keys from arbitrary locations.
  5. Check exp with no more than a few minutes of clock skew.
  6. Authorise: check scopes or roles for the operation and, where the platform allows role-less app tokens, check client_id (or Entra's appid) against an allow list.
  7. If the token is sender-constrained, verify the certificate thumbprint or DPoP proof; failures return invalid_token.

RFC 9068 also warns that authorisation servers should stop clients from influencing sub, for example by registering a client ID that collides with a privileged user identifier, so APIs should never treat sub alone as proof of a human. For opaque tokens, use token introspection (RFC 7662) and cache results briefly.

Implementation and rollout

  1. Inventory clients: list every client registration with grant types enabled, authentication method, granted permissions, owner, last token request and credential expiry. Include test tenants and old integrations.
  2. Remove what is unused: disable clients with no token requests in 90 days (after confirming with owners), and remove unused permissions.
  3. Split shared clients: one client per service per environment, with its own permissions.
  4. Migrate authentication: add a certificate, JWKS or federated credential alongside the existing secret, switch the client, monitor invalid_client errors, then delete the secret. Keeping the old secret registered during the change is the rollback path; deleting it on a fixed date ends it.
  5. Tighten tokens: shorten access token lifetimes where the platform allows, enforce audience and scopes, and enable assignment required or equivalent so unassigned clients cannot get tokens.
  6. Add sender constraint: pilot mTLS binding or DPoP on one high-value API, with the resource server accepting both bound and bearer tokens for a short, logged transition, then require binding.
  7. Harden resource servers: roll out the validation steps above in log-only mode first, review rejections, then enforce.
  8. Govern new clients: require an owner, a purpose, a permission review and an expiry date for every new M2M client, and review application permissions quarterly.

Decision guidance

  • Client credentials versus API keys: prefer client credentials when you need central revocation, scopes, short-lived tokens and audit by client. A static API key is a bearer credential with none of these. See the API Key Management Guide.
  • Client credentials versus token exchange: if the downstream call should be limited to what a particular user may do, client credentials is the wrong tool; use delegation.
  • private_key_jwt versus mTLS: private_key_jwt is easier when TLS is terminated by gateways or load balancers; mTLS fits estates that already run a mesh or private PKI.
  • Federation versus stored keys: where the workload runs on a platform with an identity provider (cloud, Kubernetes, CI), federation is the strongest option because nothing long-lived exists to steal.
  • Small organisations: start with one client per integration, secrets in a vault with rotation, short token lifetimes and a quarterly permission review, then move the most privileged clients to certificates.
  • Large organisations: enforce asymmetric or federated authentication by policy, automate client inventory, and require sender-constrained tokens for financial, data export and administrative APIs.
  • AI agents and commerce: agents calling APIs on their own account fit client credentials; agents acting for a person or paying on their behalf need delegation and per-transaction limits. See the Agentic AI Identity Guide and the Agentic Commerce Identity Guide.

Detection and measurement

  • Credential changes: alert on new secrets, certificates or federated credentials added to existing clients, which ATT&CK tracks as T1098.001 Additional Cloud Credentials.
  • Permission grants: alert on new application permissions and admin consent, especially for mail, files, directory and key vault APIs.
  • Token use anomalies: token requests from new IP ranges or networks for a client, sudden volume increases, or a client calling APIs outside its normal set. Stolen tokens used elsewhere map to T1550.001 Application Access Token and their theft to T1528 Steal Application Access Token.
  • Authentication failures: invalid_client and invalid_grant spikes at the token endpoint; rejected tokens at resource servers grouped by reason.
MetricTargetWhy it matters
M2M clients using asymmetric or federated authenticationAll privileged clients; rising overallShared secrets are the main leak path
Clients with a named owner and last-used date100%Orphaned clients keep working after everyone forgets them
Clients unused for 90 days still enabledZeroDormant credentials are pure risk
Clients holding tenant-wide application permissionsFalling, each with an approved exceptionBroad permissions turn one leak into a tenant compromise
High-value APIs requiring sender-constrained tokensAll in scopeBlocks replay of stolen tokens
Resource servers enforcing aud, iss and typ checks100%Prevents token reuse across APIs

Common failure patterns

  • Forgotten privileged clients: in the Microsoft Midnight Blizzard breach, attackers compromised a legacy test OAuth application with elevated access, created further OAuth apps and used the app-only Exchange Online full_access_as_app role to read mailboxes, access that survives user password resets. Inventory test tenants and alert on new credentials and consents.
  • Secrets in code and pipelines: client secrets committed to repositories, baked into images or pasted into tickets, with expiry set to the maximum. Move to certificates or federation, and scan for leaked secrets.
  • Using the grant from public clients: mobile, desktop or browser apps shipped with a client secret, which RFC 6749 forbids because the secret cannot be kept.
  • APIs that trust any valid token: a resource server checks only the signature, so tokens for another API, ID tokens or role-less app tokens are accepted.
  • One client for everything: a single client ID shared by many services and environments, so revocation breaks production and logs cannot say who did what.
  • Token per request and no caching: clients request a new token on every call, inflating cost and load and hiding real anomalies in noise.

Framework mapping

FrameworkReferenceHow M2M OAuth applies
NIST SP 800-53 Rev. 5IA-9 Service Identification and AuthenticationServices authenticate before access, ideally with asymmetric credentials
NIST SP 800-53 Rev. 5IA-5 Authenticator Management; IA-5(2) Public Key-based AuthenticationSecret rotation, key protection, certificate and JWKS management
NIST SP 800-53 Rev. 5AC-6 Least PrivilegeMinimum scopes and application permissions per client
NIST CSF 2.0PR.AA-01; PR.AA-03; PR.AA-05Service identities managed, authenticated and given least-privilege access
NIST CSF 2.0DE.CM-03Monitoring of client and token activity
ISO/IEC 27001:2022 Annex AA.5.17 Authentication information; A.8.5 Secure authenticationHandling of client secrets and keys, strong authentication methods
MITRE ATT&CK v20T1528; T1550.001; T1098.001; T1552.004 Private KeysToken theft and reuse, credential persistence on apps, theft of signing keys
MITRE D3FENDD3-TBA Token-based Authentication; D3-TB Token BindingDefensive techniques implemented by scoped, sender-constrained tokens

Practitioner checklist

  • Use client credentials only from confidential, server-side clients.
  • Inventory every M2M client with owner, permissions, authentication method and last use.
  • Give each workload and environment its own client ID.
  • Replace client secrets with private_key_jwt, mTLS or workload identity federation, starting with the most privileged clients.
  • Request one resource and minimum scopes; review application permissions quarterly.
  • Cache tokens until shortly before expiry and keep lifetimes short.
  • Validate typ, iss, aud, signature, expiry and permissions at every resource server.
  • Require sender-constrained tokens (RFC 8705 or DPoP) for high-value APIs.
  • Alert on new client credentials, new application permissions and unusual token use.
  • Disable clients unused for 90 days after confirming with owners.

Standards and references

Related NHI Mgmt Group resources: NHI Authentication Guide · OAuth 2.0 and OpenID Connect Guide · OAuth Token Exchange and Delegation Guide · Workload Identity Federation Guide · mTLS Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Written and reviewed by Lalit Choda, NHI Mgmt Group. Last updated 24 October 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.