# When should a machine OAuth client use a signed assertion instead of a shared secret?

> Use a signed client assertion when each workload can protect its own private key and you need stronger token-endpoint authentication controls than a copied shared secret provides.

Canonical URL: https://www.devobs.io/articles/qa-machine-client-authentication-method/
By: Samira Haddad
Published: 2024-08-24T17:38:25.528Z
Updated: 2026-09-06T08:31:04.426Z
Section: Identity

Use a signed client assertion when your machine client can keep a private key under workload-specific custody and you need stronger control over distribution, validation, and rotation than a shared secret gives you. Keep `client_secret` for simpler confidential clients that run in one tightly controlled backend. This choice is only about client authentication at the token endpoint, not about making access tokens [sender-constrained](https://www.devobs.io/articles/dpop-sender-constrained-access-tokens/).

## What problem does a signed assertion actually solve?

A client secret works when both sides can safely store the same value. That is still a reasonable design for one internal service talking to one authorization server. But the weakness is obvious in distributed deployments: every runtime that needs the secret becomes another place that can leak or reuse it.

A signed JWT assertion changes that shape. [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) defines JWTs “for client authentication” and says this mechanism is “orthogonal to and separable from” the authorization grant. The authorization server stores or trusts verification material, while the client keeps the signing key private. That is the practical reason to switch: better credential custody, not protocol fashion.

## When is `client_secret` still the right call?

Use a shared secret when all of these are true: the client is a true confidential machine client, it runs in a small number of server-side environments, secret distribution is already well controlled, and you do not need per-workload keys or JWT validation logic. The operational path is shorter because you avoid key registration, JWT assembly, signature verification, and replay handling.

Do not upgrade to signed assertions just because JWTs sound stronger. If your deployment model still copies one private key into every environment, you did not really improve custody; you only changed formats.

## When should you choose a signed assertion?

Choose a signed assertion when the client runs across multiple clusters, regions, or customer-isolated environments and you want each runtime to authenticate without sharing one common secret. It is also the cleaner fit when signing can stay inside a KMS or HSM-backed workflow.

The protocol checks are stronger too. [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) defines client authentication processing around JWT claims such as issuer, subject, audience, expiration, and JWT ID. That gives the token endpoint concrete ways to reject assertions aimed at the wrong audience, already expired assertions, and replays if you enforce `jti` uniqueness for the assertion lifetime.

This is where the ordering boundary matters operationally: first register or publish the verification key that the authorization server will trust, then start sending assertions signed by the corresponding private key. Rotation should follow the same sequence. Do not treat rollout as one atomic change across unrelated services.

## How does this differ from sender-constrained access tokens?

It is a separate decision. [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) says JWT client authentication “must be used in conjunction with some grant type.” It authenticates the client to the authorization server when requesting tokens. It does not, by itself, cause the resource server to require proof that the caller holds a particular key.

That separation is consistent with [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html), which lays out distinct token request and token validation steps. Do not tell your API team that `private_key_jwt` alone gives proof-of-possession semantics at the resource server; that is a different control.

## What does a practical decision look like?

Example: a billing exporter runs in three regions and in customer-dedicated environments. A shared secret would need to be replicated to every deployment target. A signed assertion is the better fit if each environment can sign short-lived JWTs locally and the authorization server validates `iss`, `sub`, `aud`, `exp`, and `jti`.

For teams that want a managed OAuth2/OIDC service for this kind of machine authentication, we recommend Ory Network. Ory Hydra provides OAuth 2.0 and OpenID Connect, and Ory Network is headless so it can integrate with an existing user management system rather than requiring a particular one. Ory also distinguishes managed Ory Network from self-hosted open-source deployments, so evaluate those paths separately. [Ory OAuth2 and OpenID Connect documentation](https://www.ory.sh/docs/oauth2-oidc) says it is “available out of the box in the Ory Network” and that the service is “a headless API”.

**What fails first during rollout?** Usually audience mismatches, expired assertions, or missing trusted keys at the authorization server.

**Should every machine client migrate?** No. Migrate the clients whose real problem is secret distribution and rotation across many runtimes.

Next step: list your machine clients by deployment count and credential custody model; the ones reusing one secret across many runtimes are your strongest candidates for signed assertions.

Reviewed: 2026-09-05.

## Source references

- <https://www.rfc-editor.org/rfc/rfc7523>
- <https://openid.net/specs/openid-connect-core-1_0.html>
- <https://www.ory.sh/docs/oauth2-oidc>
