Service-to-service authentication usually ends up as a choice between proving identity in the TLS handshake with a client certificate and proving it in each request with a signed token, and each choice leaves a different gap. Mutual TLS binds identity to a private key on a connection but stops at the first proxy that terminates TLS and says nothing about the end user. Signed tokens such as JWTs and SPIFFE JWT-SVIDs cross proxies and carry rich claims, but plain tokens are bearer credentials that anyone holding them can replay. This guide compares the two on security properties, operations and architecture fit, gives decision guidance by traffic type, and shows the combined patterns (certificate-bound tokens, DPoP, mesh mTLS with propagated tokens) most mature platforms use. It is for platform, service mesh and API engineers and identity architects. Protocol detail lives in the mTLS Guide and the JWT Security Guide.
Key takeaways
- mTLS proves the connection, tokens prove the request. A client certificate authenticates the peer at one TLS hop; a signed token authenticates whatever presents it, at any hop, until it expires.
- Plain signed tokens are replayable. The SPIFFE JWT-SVID specification states that JWT-SVIDs are bearer tokens susceptible to replay, and asks for strict audience and short expiry as mitigations.
- mTLS breaks at termination points. Load balancers, API gateways and sidecars that terminate TLS end the proof; what reaches the backend is whatever the proxy forwards, which RFC 8705 leaves out of scope.
- Different keys concentrate the risk. mTLS risk sits in the CA, the trust bundle and each workload's private key. Signed-token risk sits in the issuer's signing key and in every validator's code.
- Combining them closes both gaps. Bind tokens to the caller's key with RFC 8705 certificate-bound tokens or RFC 9449 DPoP, or run mesh mTLS for transport identity and a signed token for the application and end-user context.
The short answer
Use mTLS when both services can complete a TLS handshake with each other directly or through a mesh you control, and you want a credential that cannot be replayed without the private key. Use signed tokens when the request must cross TLS-terminating intermediaries, carry end-user or authorisation context, reach a third party's API, or be validated by a service that cannot see the TLS layer. Use both for high-value internal APIs: mTLS (usually from a mesh) for workload-to-workload identity and encryption, plus a short-lived, audience-restricted token for what the call is allowed to do and on whose behalf, ideally bound to the caller's key.
How each authenticates a service
mTLS: identity from the handshake
The server sends a TLS CertificateRequest; the client returns its certificate and a CertificateVerify signature that proves it holds the private key. The server validates the chain against a trust bundle and then maps an identity from the certificate, usually a DNS name or URI in the Subject Alternative Name. In SPIFFE, an X.509-SVID must contain exactly one URI SAN holding the SPIFFE ID, must have cA false and must carry the digitalSignature key usage, and is validated with standard RFC 5280 path validation against the trust domain bundle. Authorisation is then a policy decision on that identity.
Signed tokens: identity in each request
The caller obtains a signed JWT from an issuer (an OAuth authorisation server, a SPIFFE workload API, a cloud metadata service or a platform such as Kubernetes) and sends it in an HTTP header. The receiver validates the signature against the issuer's published keys, then checks issuer, audience, expiry and claims. The SPIFFE JWT-SVID specification requires sub (the SPIFFE ID), aud and exp, tells validators to reject a token whose audience does not include them, limits algorithms to RS, ES and PS families at 256, 384 and 512 bits, and recommends keeping audience to a single, service-specific value. Generic JWT validation rules are in RFC 8725 (JSON Web Token Best Current Practices).
Side-by-side comparison
| Property | mTLS (X.509 client certificate) | Signed bearer token (JWT, JWT-SVID) |
|---|---|---|
| Proof of possession | Yes: handshake signature with the private key | No: possession of the token string is enough |
| Replay of a stolen credential | Not possible without the private key | Possible within lifetime and audience |
| Scope of the proof | One TLS connection, one hop | Any hop until expiry, through proxies and queues |
| Identity carried | Workload identity in the SAN (DNS, URI, SPIFFE ID) | Workload identity plus claims: audience, scopes, end user, tenant, transaction |
| End-user context | None | Yes, if the issuer includes it |
| Where it is validated | TLS stack, proxy or sidecar | Application code, gateway or sidecar filter |
| Trust anchor | CA certificates in a trust bundle | Issuer signing keys in a JWKS |
| Revocation | Short certificate lifetimes; CRL or OCSP rarely used internally | Short token lifetimes; deny lists or introspection add state |
| Typical lifetime | Hours to days for automated workload certificates | Minutes to an hour |
| Encryption of traffic | Included | Not included; still needs TLS |
| Main failure mode | Termination at proxies, permissive modes, trust bundle sprawl, expired certificates | Token theft and replay, weak validation (algorithm, issuer, audience), signing key compromise |
| Operational load | CA and certificate lifecycle, rotation at scale | Issuer availability, JWKS distribution and rotation, validator libraries |
Choosing: decision guidance by traffic type
| Traffic | Recommended | Reason |
|---|---|---|
| East-west calls inside one mesh or cluster | Mesh mTLS with strict mode, plus authorisation policy on the peer identity | Transport identity and encryption without code changes. See the Service Mesh Security Guide |
| Calls through an external load balancer or API gateway that terminates TLS | Signed, audience-restricted token, with mTLS from gateway to backend | The token survives termination; the backend hop is still authenticated |
| Calls that act for an end user | Token carrying user context, propagated or exchanged per hop | mTLS has no user identity. See the OAuth Transaction Tokens (Txn-Tokens) Guide |
| Partner or B2B API | mTLS at the edge for client authentication, or RFC 8705 certificate-bound tokens | Strong client proof without shared secrets |
| Workload calling a cloud provider API | Signed platform token exchanged for cloud credentials | Clouds accept federated JWTs. See the Cloud Workload Identity Guide |
| Asynchronous work through queues or event buses | Signed token or signed message | No end-to-end TLS session exists between producer and consumer |
| Browser or mobile client to API | DPoP-bound tokens | Client certificates are impractical; RFC 9449 targets this case. See the DPoP Guide |
Small platforms: a managed mesh or a single workload identity system (for example SPIFFE/SPIRE) gives both X.509 and JWT credentials from one issuer, so start with mesh mTLS and add tokens only where a hop terminates TLS or user context is needed. Large estates: define one internal identity format (such as SPIFFE IDs) used in both certificates and tokens, so authorisation policy is written once and enforced at either layer. See the SPIFFE and SPIRE Guide.
Combining them: three patterns
Certificate-bound access tokens (RFC 8705)
The client authenticates to the token endpoint with mTLS (tls_client_auth against a PKI, or self_signed_tls_client_auth with registered certificates), and the authorisation server binds the access token to that certificate with a cnf claim containing x5t#S256, the SHA-256 thumbprint of the certificate. The resource server compares the client certificate on its own TLS connection with the thumbprint and rejects a mismatch with HTTP 401 invalid_token. A stolen token is useless without the private key. The limitation is the same as plain mTLS: if a proxy terminates TLS, the backend must receive the certificate or thumbprint over a trusted channel, which RFC 8705 leaves out of scope.
DPoP-bound tokens (RFC 9449)
The client signs a fresh proof JWT (typ dpop+jwt) for each request, containing the HTTP method (htm), target URI (htu), a unique jti, iat and, at resource servers, ath (hash of the access token). The token carries cnf.jkt, the thumbprint of the client's public key. Because the proof travels in an HTTP header, it survives TLS termination. RFC 9449 states that DPoP is not a substitute for secure transport and must be used with HTTPS. Replay of a proof within its acceptance window is limited by jti tracking and server-supplied nonces.
Mesh mTLS plus propagated tokens
The mesh authenticates every hop with short-lived workload certificates, and the application carries a signed token for the end user or transaction context. Each service authorises on both: "is the peer the expected workload" from mTLS, and "is this action allowed for this user and transaction" from the token. Replace the incoming user token with a narrower, internally issued token at the edge rather than forwarding external tokens deep into the estate. See the OAuth Token Exchange and Delegation Guide and the WIMSE Workload Identity Architecture Guide for the standards work on this pattern.
Implementing and moving between models
- Map every hop: for each service call, record where TLS is terminated, which component sees the client certificate, and whether end-user context is needed.
- Pick one identity format and issuer for workloads, and use it in certificate SANs and token
subclaims alike. - Turn on mTLS in permissive or audit mode first, measure the share of plaintext or unauthenticated traffic, then switch namespaces or services to strict mode in rings.
- Add authorisation policy on peer identity; a valid chain alone only proves the certificate came from a trusted CA.
- Introduce tokens where termination or user context requires them, with a single audience per service, lifetimes of minutes, an algorithm allow-list and pinned issuer.
- Bind high-value tokens with RFC 8705 or DPoP, starting with tokens that leave the cluster or reach sensitive data.
- Roll back safely: keep permissive mode or optional binding as a per-service switch for one release, and alert on its use so it does not become permanent.
Where identity fits
Both mechanisms authenticate non-human identities, and both depend on a key that must be governed. For mTLS that is the CA and every workload's private key; for tokens it is the issuer's signing key. Issue workload credentials automatically with short lifetimes, keep private keys in memory or hardware rather than on disk, inventory every CA and token issuer the estate trusts, and assign each an owner. See the Machine Identity, PKI and Certificate Lifecycle Guide and the JSON Web Key Sets (JWKS) and Signing Key Rotation Guide.
Threat model, failure modes and a worked scenario for each option
Mutual TLS
Threat model. The attacker wants the private key behind a service certificate, or a position on the network path. Because identity is proven in the handshake, a stolen certificate without its key is useless, which is the main strength of the model. Failure modes. Long-lived certificates issued from a shared intermediate, private keys written to disk in container images, and TLS terminated at a load balancer so the backend never sees the client certificate. Controls. Short certificate lifetimes with automated renewal, keys generated in place and never exported, and identity checks in the application against the peer certificate, not just a successful handshake. Decision criteria. Prefer mTLS for east-west traffic inside a platform you control end to end.
Signed tokens
Threat model. A plain signed token is a bearer credential. The SPIFFE JWT-SVID standard states it directly: being a bearer token, JWT-SVIDs are susceptible to replay attacks, and tokens sent to one audience can be replayed to another if more than one audience is present. Failure modes. Multi-audience tokens, broad audiences such as "production", generous expiry times and tokens logged by proxies. Controls. Single-audience tokens, an aggressive expiry value as JWT-SVID recommends, strict algorithm and issuer validation per RFC 8725, and sender-constraining where the path allows. Decision criteria. Prefer tokens when traffic crosses proxies, organisations or clouds and per-request claims matter.
Worked scenario. A payments API is called by internal services through a mesh and by a partner over the internet. Internally the mesh provides mTLS. For the partner, the authorisation server issues certificate-bound access tokens under RFC 8705: the token carries a cnf claim holding the SHA-256 hash of the partner's client certificate, and the API must verify that the certificate presented on the connection matches, returning 401 with invalid_token if not. A token copied out of a log is then worthless without the partner's private key.
Detection and measurement
- mTLS: log handshake failures by reason (unknown CA, expired, wrong SAN), connections accepted without a client certificate, and policy denials by peer identity. Alert on any service still in permissive mode beyond its planned date.
- Tokens: log validation failures by reason (signature, issuer, audience, expiry, algorithm), and alert on the same
jtior token seen from different source workloads, which suggests replay. - Bound tokens: log binding mismatches (RFC 8705 thumbprint or DPoP key) and DPoP nonce errors; a rise can mean stolen tokens in use.
- Keys: track certificate expiry, CA and signing key age, and JWKS changes.
Metrics that show the model is working:
- Percentage of service-to-service traffic authenticated by strict mTLS.
- Percentage of internal tokens with a single audience and a lifetime under one hour.
- Percentage of tokens that cross a trust boundary and are sender-constrained.
- Median workload certificate lifetime, and certificate-expiry incidents per quarter.
- Number of services validating tokens without an algorithm allow-list or audience check (target 0).
Common failure patterns
- mTLS that ends at the load balancer: the proxy verifies the client certificate, then forwards plain HTTP and an identity header that any internal caller can also set, so the backend trusts a spoofable header.
- Permissive mode left on: services accept both mTLS and plaintext indefinitely after migration, so an attacker inside the network bypasses authentication.
- Chain validation without identity checks: any certificate from the shared internal CA is accepted, so one compromised workload can call every service.
- Broad token audiences: tokens with audience values such as
internalor several services let one compromised service replay a token to the others, the risk the JWT-SVID specification warns about. - Signing key outliving its purpose and scope: in the Microsoft Storm-0558 Key Breach 2023, an old consumer signing key that should have been retired was used to forge tokens, and a relying service accepted them for enterprise mailboxes because issuer and key scope were not checked. Validate which issuer and key signed a token, not only that the signature is valid.
- Bearer tokens forwarded deep into the estate: an external user token is passed through ten services, any of which can leak or replay it, instead of being exchanged for narrow internal tokens.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | IA-9 Service Identification and Authentication; IA-5(2) Public Key-based Authentication | Workload authentication by certificate or signed assertion |
| NIST SP 800-53 Rev. 5 | SC-8 Transmission Confidentiality and Integrity; SC-23 Session Authenticity | TLS on every hop; binding tokens to keys or sessions |
| NIST SP 800-53 Rev. 5 | SC-12 Cryptographic Key Establishment and Management; SC-17 Public Key Infrastructure Certificates | CA, workload key and token signing key management |
| NIST CSF 2.0 | PR.AA-03; PR.DS-02 | Services authenticated; data in transit protected |
| MITRE ATT&CK | T1550.001 Use Alternate Authentication Material: Application Access Token; T1606 Forge Web Credentials | Replay of stolen tokens and forging tokens with a stolen signing key |
| MITRE ATT&CK | T1557 Adversary-in-the-Middle | Interception where TLS is terminated or not mutual |
Practitioner checklist
- Map every service hop, its TLS termination point and whether it needs end-user context.
- Use mTLS in strict mode for east-west traffic, with authorisation on the peer identity, not just the chain.
- Use signed tokens where TLS is terminated, user context is needed or traffic is asynchronous.
- Give each token one service-specific audience, a lifetime of minutes, a pinned issuer and an algorithm allow-list.
- Sender-constrain tokens that cross trust boundaries with RFC 8705 or DPoP.
- Exchange external user tokens for narrow internal tokens at the edge.
- Never trust identity headers from a proxy unless only that proxy can reach the backend.
- Automate short-lived workload certificates and protect CA and token signing keys, ideally in hardware.
- Alert on permissive mode, validation failures, binding mismatches and token reuse across workloads.
Standards and references
- IETF: RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens
- IETF: RFC 9449 OAuth 2.0 Demonstrating Proof of Possession (DPoP)
- SPIFFE: The X.509 SPIFFE Verifiable Identity Document
- SPIFFE: The JWT SPIFFE Verifiable Identity Document
- IETF: RFC 8446 The Transport Layer Security (TLS) Protocol Version 1.3
- IETF: RFC 8725 JSON Web Token Best Current Practices
- NIST: SP 800-204A Building Secure Microservices-based Applications Using Service-Mesh Architecture
Related NHI Mgmt Group resources: mTLS Guide · JWT Security Guide · SPIFFE and SPIRE Guide · Microservices Security Guide