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_accessplus 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
- The user signs in to the portal through PF (authorization code + PKCE). The PF token stays server-side.
- The portal calls
POST /v1/link-intents. The broker creates a one-use, five-minute intent. - The browser goes through the PF SP journey, which logs in to Entra upstream.
- The Reference ID adapter Form POSTs
REFandTargetResourceto the portal callback. - The portal calls
POST /v1/link-intents/{id}/completewith{"reference":"<REF>"}. - The broker performs the authenticated pickup from PF.
- The broker checks that
subjectequals thesubof the portal's token. - 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)