Accept federation clients on the authorization server
A hosted authorization server can onboard relying parties that nobody registered in advance, as long as they belong to a federation the server trusts. Instead of a registration record created by an administrator, the client proves who it is with its Trust Chain, and its keys and metadata come from that chain. The EDK implements both methods of OpenID Federation for OpenID Connect 1.1: Automatic Registration (§12.1), where the client registers implicitly with its first request, and Explicit Registration (§12.2), where it registers first and gets a signed registration response.
This guide is written for the operator of the hosted authorization server, and describes what the client has to send so you can support relying parties that integrate with it.
Some boundaries up front. This is not RFC 7591 dynamic client registration, which the hosted server does not offer. It is unrelated to the upstream sign-in bindings in Hosted sign-in and external federation. And it works in one direction only: the hosted authorization server accepts federation clients. The platform does not act as a federation relying party that registers itself at external OpenID Providers.
Enable federation clients
Two things have to be in place: a Trust Domain decision about which federations the server accepts, and a federation entity for the server so relying parties can find and verify it.
Choose the accepted Trust Anchors
The accepted federations are decided by a Trust Domain, through the
Trust Domain API under /api/trust-domain/v1:
- Create a domain for the federations you accept (
createTrustDomain,POST /domains). - Add each federation's Trust Anchor as an anchor with evidence mechanism
OIDFED_ENTITY, whose public identifier is the anchor's Entity Identifier (createTrustAnchor,POST /domains/{domainId}/anchors). - Admit each anchor with the admission class
CLIENT(putTrustAnchorAdmission,PUT /domains/{domainId}/anchors/{anchorId}/admissions/CLIENT). This admission class accepts onlyOIDFED_ENTITYanchors. - Attach the domain to the authorization server for the usage
CLIENT_REGISTRATION_TRUST(putTrustAttachment,PUT /attachments/AS/{authorizationServerId}/CLIENT_REGISTRATION_TRUST), whereauthorizationServerIdis the server's resource UUID.
The anchors' keys are never configured here. Whenever a client's chain is verified, each anchor's keys are read from the anchor's own Entity Configuration. Without an attachment, or with an attachment whose domains hold no admitted federation anchors, the server accepts no federation clients at all. Trust domains explains anchors, admissions and attachments in general.
Publish the server as a federation entity
Relying parties discover an OpenID Provider through its Entity Configuration, and they need a Trust Chain for it too. Give the server a federation entity as described in Join an existing federation:
- use the server's issuer URL as the Entity Identifier. Request Objects must name exactly the server's issuer as audience, and relying parties address the audience by the provider's Entity Identifier, so the two have to be the same;
- bind the server with an
AUTHORIZATION_SERVERbinding and declare theopenid_providerrole for an OpenID Provider, oroauth_authorization_serverfor a plain OAuth 2.0 server; - create and select a Federation Entity Key, have the federation's superior admit the entity, and publish it.
When the CLIENT_REGISTRATION_TRUST attachment exists, the published Entity Configuration adds three
claims to the server's openid_provider or oauth_authorization_server metadata (OpenID Federation for
OpenID Connect 1.1 §5.1.1):
client_registration_types_supportedwithautomaticandexplicit;federation_registration_endpoint, which is<server Entity Identifier>/federation_registration;request_authentication_methods_supported, stating that the Pushed Authorization Request endpoint takesprivate_key_jwt.
The claims follow the attachment at the moment of publication. After you create or delete the attachment, publish the server's entity again so its Entity Configuration says what the server does.
Automatic Registration
With Automatic Registration the relying party skips a registration step and starts with an authorization request. End to end:
- The relying party resolves the server's Entity Configuration and Trust Chain, and learns that
automatic registration is supported and that the Pushed Authorization Request endpoint expects
private_key_jwt. - It builds a Request Object with its own Entity Identifier as
client_id, signed with a key from thejwksof itsopenid_relying_partymetadata as published in the federation. The Request Object names only the server's issuer inaud, carries a freshjtiand anexp, and contains nosub. It includes a PKCEcode_challenge. - It sends the Request Object to the server's Pushed Authorization Request endpoint, authenticating with
a
private_key_jwtclient assertion signed with the same key set. - The server sees an
httpsclient_idit has no record of. It reads the anchors admitted forCLIENT_REGISTRATION_TRUST, resolves the client's Trust Chain from the client's Entity Configuration through itsauthority_hintsto one of those anchors, verifies it, and applies the metadata policies of every superior in the chain. The client's registration is taken from the resolvedopenid_relying_partymetadata and the signing keys it publishes. - With that registration the server verifies the client assertion and the Request Object exactly as for
any other client. A Request Object whose
jtiwas already used is refused. - The server returns a
request_uri. The flow continues with the authorization request, and the token request authenticates withprivate_key_jwtagain and presents the PKCE verifier.
A client registered this way is confidential and never receives a secret. It authenticates only with
private_key_jwt, must use Pushed Authorization Requests and PKCE, and each Request Object is accepted
once. Its resolved metadata must contain at least one redirect_uri; grant_types defaults to
authorization_code and response_types to code. Automatic Registration always uses the OpenID
Connect profile, so the client's metadata has to be openid_relying_party.
There is no stored registration record. The server keeps the resolved registration until the Trust Chain expires and resolves it again on the next request after that. A change to the accepted anchors takes effect for the next request.
When resolution fails, the client is treated as an unknown client and the Pushed Authorization Request is
refused with the server's usual client error. The federation reason is not returned to the client; it is
written to the server log with the client's Entity Identifier. Typical reasons are a chain that ends at
an anchor that is not admitted, an expired statement somewhere in the chain, metadata that a superior's
policy rejects, and missing redirect_uris.
Explicit Registration
With Explicit Registration the relying party registers before it sends any authorization request.
The request
The client sends POST <server Entity Identifier>/federation_registration with one of two bodies:
- its Entity Configuration, as
application/entity-statement+jwt. It may carry its Trust Chain in atrust_chainJWS header, and then optionally the server's chain in apeer_trust_chainheader; - its Trust Chain, as
application/trust-chain+json: a JSON array whose first element is its Entity Configuration. In this form neither header may be used.
The Entity Configuration used as a request differs from the one the client serves at its well-known URL
in one way: its aud is exactly the server's Entity Identifier. Its iss and sub are both the client's
https Entity Identifier, it lists authority_hints, its metadata contains openid_relying_party (or
oauth_client when the server's binding declares no openid_provider role), and it is signed with a key
from its own jwks.
What the server verifies
The server identifies itself from the host and path the request was sent to, and refuses the request when
no federation entity bound to a hosted authorization server lives at that URL. It then verifies the
request against the anchors admitted for CLIENT_REGISTRATION_TRUST:
- the request is a well-formed, unexpired Entity Statement with the claims above, signed with a key from
its own
jwks; - the Trust Chain, taken from the body or header or otherwise resolved from the
authority_hints, verifies to one of the admitted anchors; - the chain continues at one of the request's
authority_hints, and that Immediate Superior's statement lists the key that signed the request, so the request verifies with a key the superior vouches for; - after applying the metadata policies of the chain, client metadata of the expected Entity Type remains;
- a
peer_trust_chain, when sent, verifies and ends at the same anchor as the client's chain.
The registration itself is then checked. The client must authenticate with private_key_jwt, which is
assumed when token_endpoint_auth_method is absent, so its metadata needs jwks or jwks_uri. An
authorization_code client needs redirect_uris. Unknown grant or response types are refused.
The response
The server stores the registration with the client's Entity Identifier as client_id and answers with a
registration response signed by its selected Federation Entity Key, as
application/explicit-registration-response+jwt. The response repeats the registered metadata and the
client_id, and its expiry is the registration's expiry: the earlier of the Trust Chain's expiry and the
request's exp. The registered client is confidential, has no secret and must use PKCE. Pushed
Authorization Requests are not forced for explicitly registered clients unless the server requires them
for every client.
A new registration from the same client replaces the earlier one, so a client renews by registering again before its registration expires. After expiry the registration can no longer be used. If the response cannot be signed, for example because the server's entity has no selected key, the registration is withdrawn again and the client receives a server error.
Errors
Errors are returned as JSON with error and error_description:
| Status | error | Cause |
|---|---|---|
404 | not_found | No federation entity bound to a hosted authorization server at this URL. |
403 | invalid_trust_anchor | The server accepts no federation clients: no admitted anchors for CLIENT_REGISTRATION_TRUST. |
400 | invalid_request | Wrong content type, empty body, or a request that is malformed, not addressed to this server, or not signed with a key the superior vouches for. |
400 | invalid_trust_anchor, invalid_trust_chain | The chain does not reach an admitted anchor, does not verify, or has expired. |
400 | invalid_metadata | No client metadata of the expected Entity Type remains after the chain's policies. |
400 | invalid_client_metadata | The metadata cannot be registered: another authentication method, no keys, no redirect_uris, an unknown grant or response type. |
500 | server_error | The registration could not be stored or the response could not be signed. |
Expiry, renewal and key rollover
A federation client's registration never outlives its Trust Chain, and a chain is only as long-lived as
its shortest statement. When the client's superior stops renewing its statement, or an anchor loses its
CLIENT admission, the client loses access within one statement lifetime: Automatic clients at the next
resolution, Explicit clients at the latest when their registration expires. To cut an explicitly
registered client off earlier, remove its client registration in the server's client administration.
Clients rotate keys the federation way. The client publishes the new key next to the old one, its superior signs a statement that includes the new key, and only then does the client start signing with it. An Automatic client's resolved keys are refreshed when its chain is resolved again, so a client that switches keys too early is refused until then. An Explicit client registers again after the rollover.
The server's own keys follow the same rule. Explicit responses are signed with the key currently selected for the server's entity; create a new key, have the superior include it, select it and publish the entity again, then revoke the old key once no published statement depends on it.
Where it runs
Federation registration runs in the enterprise tenant authorization server and platform services, which both connect to the OpenID Federation database. The monolith does not include it. See Deploy OpenID Federation.