Every multi-app ecosystem eventually hits the same wall. App A, a frontend the user actually logged into, needs to call App B, some backend microservice, as that user. And there are exactly three ways teams usually do it, all of them wrong.
One: App A forwards its own access token to App B. The aud claim says App A. App B either rejects it — correct — or validates it anyway, which means you’ve trained a service to accept tokens minted for someone else. That’s not delegation, that’s a confused deputy waiting for a researcher to write it up.
Two: App A stashes a refresh token so it can mint tokens for App B on demand. Now you have a long-lived, high-value credential sitting in a frontend’s reach, crossing a trust boundary it was never scoped for. Refresh tokens are the crown jewels. Spreading them across apps to solve a plumbing problem is how you turn one compromised service into ten.
Three: App A redirects the user back through the IdP to consent again, scoped to App B. Protocol-correct, UX-fatal. Nobody ships a product that bounces users through an authorization screen every time a backend needs to talk to another backend.
ID-JAG — Identity Assertion JWT Authorization Grant, explained in Auth0’s October 20 writeup — kills the first two cleanly and sidesteps the third. It’s not a new protocol so much as a disciplined use of an old one: RFC 7523’s jwt-bearer grant, pointed at the cross-app problem specifically.
The shape is three parties, not two. The requesting app (App A), the resource app (App B), and the enterprise IdP that both trust. App A doesn’t forge anything. It asks the IdP for a signed assertion — a short-lived JWT that says this is User X, they consented, and I’m asking on their behalf. The IdP signs it with its own key. App A then takes that assertion straight back to the token endpoint and exchanges it for an access token whose aud is App B and nothing else.
The exchange itself is unremarkable, which is the point:
Authenticate the client with a client_assertion — a private-key JWT — not a shared secret you’ll find in a log six months from now. The response is a normal access token, correctly scoped, short-lived, with no refresh token for App A to lose. If App A gets popped tomorrow, the blast radius is one audience and one expiry window, not a standing key to User X’s account across the estate.
Where ID-JAG sits relative to its neighbours matters, because people will reach for the wrong one. Use client credentials when there’s no user — service-to-service, App B acting as itself. Use RFC 8693 token exchange when you already hold a token and need to swap it for another, including impersonation and broader on-behalf-of patterns; it’s the bigger, more general toolbox. Use ID-JAG for the narrow, common case the general tools make clumsy: a user is present, App A has their session, and App A needs a token for App B inside the same organization without holding long-lived credentials. It doesn’t replace OBO everywhere. It makes the frontend-to-backend hop boring, which is what you want.
One thing to keep straight: this is not the SAML bearer assertion flow with a new coat of paint. Different signing, different format, different trust plumbing. The assertion is a JWT the IdP signed, validated by the IdP’s own keys at exchange time — don’t let anyone wire it up as if it were SAML.
Support is early. Auth0 is pushing it, and the Auth0 post points at Okta and Entra ID moving in the same direction. Read that as architect for it now, pilot before you bet a production estate on it. The idea is sound and overdue. The surprise is that we spent a decade leaking refresh tokens across apps because the clean answer hadn’t been named yet.
