---
title: "The gap nobody fills: service-to-service identity"
summary: "Messaging platforms issue credentials to people and to applications. Few can express 'system A is acting on behalf of customer B, with these permissions, until this moment'."
canonical: https://cpaas.co/en/learn/service-to-service-identity
kind: whitepaper
depth: advanced
origin: original
published: 2026-07-17T16:26:18.899Z
updated: 2026-10-14T16:26:19.555Z
topics: ["Architecture", "Compliance", "CPaaS"]
language: en
---
# The gap nobody fills: service-to-service identity

## Why this matters

As soon as more than one system is involved in sending a message (a journey tool calling a messaging API, a support desk sending on behalf of a client, a partner integration acting for its customers), someone will eventually ask: *who authorised this message, for whom, and was that permission still valid?* If the only credential in the picture is a shared API key, nobody can answer. This piece explains the gap, why it appears, and the well-established patterns from the wider security world that close it. It is relevant to buyers as much as builders: how a provider handles this tells you a lot about how safely you can connect it to everything else.

Look at the credentials your messaging stack issues. There is probably an API key per account, and possibly scopes on it. That covers "this application may send".

It does not cover the question that actually arises in any integrated system: **on whose behalf, and for how long?**

## Why the gap exists

Because it did not matter when a platform was called by one backend belonging to one company. The key *was* the identity, and the answer to "on whose behalf" was "the account holder, obviously".

It stops being obvious the moment anything is layered. A journey engine calls a messaging API for one of its customers. An agent desk sends on behalf of the tenant whose queue it is serving. A partner integration acts for a client who can revoke that arrangement.

In each case the credential says who is calling. Nothing says who they are calling for.

User identity was solved first because the pressure was visible: there are many users, they leave, they get phished, and failures make headlines. That pressure produced OAuth, OpenID Connect and SAML. Service identity never had that pressure. Services are few and internal, and a shared secret works, until it is in eleven places, one of them a build pipeline nobody owns.

```diagram:path
# Who the call is for, as it travels
End customer | Authorises a partner to act for them | Nothing in the credential records this
Partner integration | Calls with a shared key | The key says who is calling
Journey engine | Calls the messaging API for its customer | Still only who is calling
Messaging API | Sees a valid key — and nothing about whom the message is for
> User identity had OAuth, OpenID Connect and SAML. The service-to-service hop had a shared secret.
```

## What goes wrong without it

**Revocation is all-or-nothing.** A partner's access is a shared key. Withdrawing one customer's authorisation means rotating a credential that everyone else is also using.

**Audit answers the wrong question.** Logs record which key sent a message. Reconstructing which end customer authorised it means joining application logs that may not have been retained.

**The blast radius is the whole account.** A leaked integration key can send as anybody the integration serves.

**Consent enforcement lands in the wrong place.** The system holding the consent ledger is often not the system holding the credential. Without a way to say "I am acting for this person", the check has to be trusted to the caller, which means it is not a check.

The absence of service identity does not announce itself. It shows up as a sequence of small compromises, each reasonable on its own.

```diagram:flow
# How a shared secret becomes an incident
One key, one integration | Perfectly fine. It is scoped by accident, because there is only one caller.
A second caller borrows it | Nobody wants to run the provisioning process twice. The key is now shared.
The key acquires scope | It needs to do more, so it is granted more. Nothing revokes the parts it stopped needing.
Rotation becomes impossible | Rotating it means coordinating every holder, and nobody knows who holds it.
A holder is compromised | Now everything the key can do is available, and you cannot tell which holder did what.
> No step here is a mistake anyone would flag in review. The vulnerability is the sequence, and the easiest place to break it is step two.
```

```diagram:checklist
# Four failures a shared key produces
[!] Revocation is all-or-nothing | Withdrawing one customer means rotating a key everyone else uses
[!] Audit answers the wrong question | It records which key, not which customer authorised the message
[!] The blast radius is the whole account | A leaked key sends as anybody the integration serves
[!] Consent enforcement lands in the wrong place | The check is trusted to the caller, so it is not a check
```

## The shape of the answer

The pattern is well established outside this category and less often applied inside it.

| | Long-lived shared API key | Short-lived delegated token |
|---|---|---|
| Lifetime | Months or years | Minutes or hours |
| Says who is calling | Usually | Yes |
| Says which customer it is for | No | Yes, as an explicit claim |
| Scope | Often "everything this account can do" | One capability, one audience, one channel |
| Revoking one customer | Rotate the key for everyone | Revoke that one grant |
| If leaked | Works until someone notices | Expires on its own |
| Audit trail | Which key sent it | Which grant, for whom, issued by whom |

In practice that means:

- **A short-lived token, not a long-lived key.** Minutes, obtained in exchange for a durable credential. A leaked token expires; a leaked key has to be discovered.
- **An explicit subject claim.** The token says which end customer this action is for. The receiving system enforces it rather than trusting an argument in the request body.
- **Scopes that name capability and audience.** "May send utility messages, for tenant X, on channel Y", not "may use the messaging API".
- **A revocation path that is not a rotation.** Withdrawing one delegation must not disturb any other.
- **An audit record carrying the delegation chain**, so "who authorised this message?" has a stored answer rather than requiring an investigation.

None of this needs inventing. OAuth 2.0 Token Exchange (RFC 8693) describes how one service obtains a token to act on behalf of another party; JSON Web Tokens (RFC 7519) carry signed, verifiable claims; mutual TLS with certificate-bound tokens (RFC 8705) ties a token to the client presenting it; and workload identity frameworks such as SPIFFE give each service a verifiable identity without shared secrets. Zero-trust guidance such as NIST SP 800-207 describes the same principle at the level of a whole architecture.

```diagram:blocks
# What a service credential has to be
Verifiable | The recipient can check it without calling the issuer. A signature over a known key, not a lookup.
Scoped | Names the actions and the resources. A credential that says "trusted" says nothing useful.
Short-lived | Minutes or hours. Expiry is the property that makes compromise survivable without coordination.
Attributable | Identifies the calling service, not just the fact of authorisation. Logs need a subject.
Revocable | Independently, by the issuer, without the holder's cooperation. Otherwise trust is permanent.
Delegable | A can act for B with proof, within B's scope and never beyond it. This is what federation needs.
> The first four are table stakes. The last two are what let you disconnect two systems later without rewriting either.
```

## What to ask a provider

If you are buying rather than building, these questions reveal how far a platform supports this:

1. Can I create credentials scoped to a single sub-account, channel or capability?
2. Do you support short-lived tokens, and can I revoke one without rotating the others?
3. When a partner or reseller acts on my behalf, does your audit log show which party acted and under what authority?
4. Can I see, export and keep those audit logs?

Many platforms support sub-accounts and scoped keys; fewer support delegation with per-customer revocation. Knowing which you are getting avoids surprises.

## Why this is worth doing before you need it

Because it is hard to retrofit. Once integrations exist that assume a long-lived key with implicit authority, every one of them has to change, and each is owned by a different team on a different schedule. The migration is not technically difficult. It is organisationally difficult, and it takes a long time.

The argument against is always the same: we have three services, we know who they are, this is over-engineering.

It may be over-engineering for three services. It is about right for the fourth, which will often be a partner rather than a team, and it is very hard to retrofit for the eleventh, because by then the shared secret is a load-bearing part of several deployment pipelines.

```diagram:scale
# Delegated identity, by the number of integrations
Over-engineering | Very hard to retrofit
Three services | 1 | We know who they are
The fourth | 5 | About right — and often a partner rather than a team
The eleventh | 9 | The shared secret is load-bearing in several deployment pipelines
```

## The trade-offs

Short-lived tokens add moving parts: a token service that must be available, clock synchronisation, and more failure modes to monitor. Fine-grained scopes need someone to design and maintain them. For a single company with one backend and one messaging provider, a well-protected, regularly rotated API key with restricted scope may be entirely adequate. The case for delegation grows with every additional party that acts on someone else's behalf.

## The federation connection

If you run several systems that call each other rather than one merged system, this is the layer that makes it safe. Published contracts let systems talk. Service-to-service identity is what makes it defensible when someone asks what a system was allowed to do, for whom, and when that permission ended. Federation without it is a set of shared secrets in configuration files: centralisation with extra steps and none of the isolation it was supposed to buy.

## What to take away

Give every service a verifiable identity. Issue scoped, short-lived, revocable credentials rather than long-lived shared secrets. Make delegation explicit and provable. Do it while the number of integrations is small, because the reason to do it arrives with the first partner and the ease of doing it leaves soon after.

## Where to go next

- [Federating systems without merging them](/en/learn/federating-without-merging): the architecture this identity layer supports.
- [Evaluating a CPaaS contract](/en/learn/evaluating-a-cpaas-contract): the contract questions to pair with the technical ones above.

## Sources

[^1]: [RFC 8693: OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693) — IETF
[^2]: [RFC 7519: JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519) — IETF
[^3]: [RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens](https://www.rfc-editor.org/rfc/rfc8705) — IETF
[^4]: [SPIFFE overview](https://spiffe.io/docs/latest/spiffe-about/overview/) — SPIFFE (Cloud Native Computing Foundation)
[^5]: [SP 800-207: Zero Trust Architecture](https://csrc.nist.gov/pubs/sp/800/207/final) — NIST

---

Published by the company behind five products in this category. Those products appear in this site's directory alongside competitors under the same published criteria and are labelled as its own. Editorial content does not recommend them.
