DEV Community

DarkEdges
DarkEdges

Posted on

PingFederate as a Token Courier: Linking Entra with the Reference ID Adapter

Repo: darkedges/pingfederate-graph-broker*

In part 1 we said Entra tokens stay on the backend. But someone has to obtain them. Here the broker uses PingFederate as the courier: PF runs the upstream OIDC login to Entra, and the broker picks up the result server-to-server.

The moving parts in PingFederate

  • An upstream Entra OIDC IdP connection (authorization code with PKCE) requesting offline_access plus the two Graph read scopes.
  • The Reference ID SP Adapter from the Agentless Integration Kit. It hands the portal an opaque reference instead of the attributes themselves.
  • A pickup endpoint the broker calls with HTTP Basic auth to exchange that reference for attributes.

The attributes in the pickup contract:

Attribute Meaning
subject Canonical PF user ID
entra_tid Entra tenant ID
entra_oid Entra object ID
entra_token_response Token endpoint response (Context → Token Endpoint Response)

The connection flow

  1. The user signs in to the portal through PF (authorization code + PKCE). The PF token stays server-side.
  2. The portal calls POST /v1/link-intents. The broker creates a one-use, five-minute intent.
  3. The browser goes through the PF SP journey, which logs in to Entra upstream.
  4. The Reference ID adapter Form POSTs REF and TargetResource to the portal callback.
  5. The portal calls POST /v1/link-intents/{id}/complete with {"reference":"<REF>"}.
  6. The broker performs the authenticated pickup from PF.
  7. The broker checks that subject equals the sub of the portal's token.
  8. The broker redeems the refresh token immediately to prove it works, then saves the connection encrypted.

The browser only ever carries an opaque reference. The Microsoft token response travels PF to broker, never through the portal or the user agent.

Binding by subject, not by email

The most important check is step 7. A pickup whose subject differs from the calling user's sub is rejected. Connections are bound to the authenticated canonical subject and never to an email address or UPN, which are mutable and attacker-influenced in many directories.

Link intents add more guardrails:

  • one-use and owner-bound
  • a new intent cancels the previous one
  • completion consumes the intent before pickup, so a failed or replayed attempt can't be retried

Scope allowlisting at import

When a connection is imported (and on every refresh), the broker inspects the granted scopes. Both read scopes must be present. OIDC scopes and User.Read are tolerated. Anything else, including broader reads or any write scope, is rejected, and a scope escalation marks the connection reconnect_required with its tokens cleared.

A note on honesty

The Terraform in the repo provisions most of this, but the PingFederate provider doesn't yet expose an SP token-generator resource, so part of the handoff is manual. The live PF/Entra path is also not verified end to end. Part 5 covers exactly what is and isn't proven.

Next: how the agent side works, with PingFederate introspection and the delegation model.

Top comments (0)