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.

Who the call is for, as it travels

  1. 01End customer

    Authorises a partner to act for them

    Nothing in the credential records this

  2. 02Partner integration

    Calls with a shared key

    The key says who is calling

  3. 03Journey engine

    Calls the messaging API for its customer

    Still only who is calling

  4. 04Messaging 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.

How a shared secret becomes an incident

  1. 01One key, one integration

    Perfectly fine. It is scoped by accident, because there is only one caller.

  2. 02A second caller borrows it

    Nobody wants to run the provisioning process twice. The key is now shared.

  3. 03The key acquires scope

    It needs to do more, so it is granted more. Nothing revokes the parts it stopped needing.

  4. 04Rotation becomes impossible

    Rotating it means coordinating every holder, and nobody knows who holds it.

  5. 05A 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.

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 keyShort-lived delegated token
LifetimeMonths or yearsMinutes or hours
Says who is callingUsuallyYes
Says which customer it is forNoYes, as an explicit claim
ScopeOften "everything this account can do"One capability, one audience, one channel
Revoking one customerRotate the key for everyoneRevoke that one grant
If leakedWorks until someone noticesExpires on its own
Audit trailWhich key sent itWhich 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.

What a service credential has to be

  1. Verifiable

    The recipient can check it without calling the issuer. A signature over a known key, not a lookup.

  2. Scoped

    Names the actions and the resources. A credential that says "trusted" says nothing useful.

  3. Short-lived

    Minutes or hours. Expiry is the property that makes compromise survivable without coordination.

  4. Attributable

    Identifies the calling service, not just the fact of authorisation. Logs need a subject.

  5. Revocable

    Independently, by the issuer, without the holder's cooperation. Otherwise trust is permanent.

  6. 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.

Delegated identity, by the number of integrations

Over-engineeringVery hard to retrofit
123
  1. 1
    Three services

    We know who they are

  2. 2
    The fourth

    About right — and often a partner rather than a team

  3. 3
    The eleventh

    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#

Sources

Every claim worth checking, with somewhere to check it.

  1. RFC 8693: OAuth 2.0 Token ExchangeIETF
  2. RFC 7519: JSON Web Token (JWT)IETF
  3. RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access TokensIETF
  4. SPIFFE overviewSPIFFE (Cloud Native Computing Foundation)
  5. SP 800-207: Zero Trust ArchitectureNIST