R. Hedberg, Ed. | |
independent | |
A. Solberg | |
Uninett | |
S. Gulliksson | |
Schibsted | |
M. Jones | |
Microsoft | |
J. Bradley | |
Ping Identity | |
December 3, 2018 |
OpenID Connect Federation 1.0 - draft 06
openid-connect-federation-1_0
The OpenID Connect standard specifies how a Relying Party (RP) can discover metadata about an OpenID Provider (OP), and then register to obtain relying party credentials. The discovery and registration process does not involve any mechanisms of dynamically establishing trust in the exchanged information, but instead rely on-out-of band trust establishment.
In an identity federation context, this is not sufficient. The participants of the federation must be able to trust information provided about other participants in the federation. OpenID Connect Federations specifies how trust can be dynamically obtained from resolving trust from a common trusted third party.
While this specification is primarily targeting OpenID Connect, it is designed in order to allow for re-use by other protocols and in other use cases.
This specification describes how two entities that would like to interact can dynamically fetch and and resolve trust and metadata for a given protocol, by the use of third party trust issuers.
An identity federation can be realized using this specification by the use of one or more levels of trust issuers. A trust issuer is an entity, which main purpose is to issue trust statements about entities, such as OpenID relying party and providers. This specification does not mandates a specific way or restrict how a federation may be built. Instead the specification provides the basic technical trust infrastructure building blocks needed to build a dynamic and distributed trust network such as a federation.
An entity will typically configure a local trust root to include the identifier and the certificate of a trusted third party – the federation. All entities involved in OpenID Connect Federation, including the trust issuers, will have their own unique identifier. This identifier is used to dynamically fetch entity statements. As a complete chain of entity statements is obtained, connecting the local trust root to the target entity, the entity may resolve the resulting trusted metadata, by flattening the metadata found in the trust chain.
Note that a real-world entity like an organisation, a company may be represented by more than one entity in a federation.
The OpenID Connect Federation trust chains are relying on cryptographically signed JWT documents, and the trust chain does not at all rely on TLS in order to establish trust.
OpenID Connect Federation may very well be used for other purposes than building traditional identity federations. One of them could be to build an OpenID Connect deployment where the key rollover process does not fall back to TLS. Another could be allowing traditionally public/native clients, such as medical devices, to generate its own key pair, and use asymmetric crypto to increase the overall security.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
An entity statement is always a signed JWT. An entity statement is issued by the iss, and the statement considers the subject entity, the sub. To be able to resolve trust and metadata, one need to know the identifier of the target entity – we refer to this as the leaf entity. The leaf entity will always sign a statement about itself, and give some hints to other entities that may want to issue statements about itself. All other entities in a trust chain we refer to as intermediate entities. The local configured trust root, we refer to as the trust anchor.
The entity statement is signed using the private key of the issuer entity, in the form of a JSON Web Signature (JWS).
Non-normative example of a entity statement, before serialization and adding a signature.
{ "iss": "https://feide.no", "sub": "https://ntnu.no", "iat": 1516239022, "exp": 1516298022, "metadata": { "openid_provider": { "issuer": "https://ntnu.no", "organization": "NTNU", "id_token_signing_alg_values_supported": ["RS256", "RS384", "RS512"] }, "openid_client": { "organization": "NTNU", "grant_types_supported": ["authorization_code", "implicit"], "scopes": ["openid", "profile", "email", "phone"] } }, "jwks": { "keys": [ { "alg": "RS256", "e": "AQAB", "ext": true, "key_ops": ["verify"], "kid": "key1", "kty": "RSA", "n": "pnXBOusEANuug6ewezb9J_...", "use": "sig" } ] }, "authority_hints": { "https://edugain.org/federation": [] } }
(postamble)
In order to configure trust when deploying a software component, it is recommended to align that configuration with the semantics of an entity statement. How the configuration is stored and the exact format is out of the scope of this specification, but it is recommended to allow the user to configure a list of entries containing sub, jwks and metadata. When the compound metadata from the trust chain is resolved, metadata from the local trust root can be applied in the metadata flattening process. This allows the configuration of a provider to put trust limitations applied to all metadata resolved for the various trust roots. For example, a provider may trust a large federation with a metadata limitation of only releasing the name and userid, and no other scopes or claims. The provider may add other trust roots with a more limited target group to allow for more scopes and claims.
The OpenID Connect Federations specification does allow new metadata types to be defined, to support use cases outside OpenID Connect. The metadata type identifier will uniquely identify which metadata specification to interpret.
The metadata document MUST be a JSON document. Beyond that there is no restriction.
Metadata used in OpenID Connect Federations typically re-uses existing metadata standards. If needed, the metadata schema is extended with additional properties relevant in a federated context.
The metadata type identifier is openid_client.
All parameters defined in section 2 of OpenID Connect Dynamic Client Registration 1.0 are allowed in a metadata statement.
To that list is added:
The metadata type identifier is an openid_provider.
All parameters defined in section 3 of OpenID Connect Discovery 1.0
In addition the following properties are allowed:
The metadata type identifier is openid_discovery.
The metadata type identifier is oauth_as.
The metadata type identifier is oauth_client.
The metadata type identifier is oauth_api.
Given the entity identifier, the endpoint of the federation API endpoint of the entity is easily derived. All entities that are expected to expose entity statements about themselves or other entities, MUST implement a Federation API.
The federation API endpoint of an entity is resolved from the entity identifier. The Federation API endpoint is found using the Well known URIs specification, with the suffix openid-federation. The scheme, host and port is taken directly from the entity identifier combined with the following path: /.well-known/openid-federation.
The Federation API is an HTTP API that may support multiple operations. Fetching entities is one of the operations, and the only one that all entities are REQUIRED to support. All the other operations is OPTIONAL. The op (operation) parameter may be used to define new operations in a future version of the specification or a local deployment which have agreed upon additional functionality.
While all operations in the specification make use of a GET request, other operations may choose to use other HTTP methods. If the op parameter is left out, it is considered to be a fetch entity statements request. Unless otherwise mentioned or agreed upon, requests to the federation API does not need to be authenticated.
Fetching entity statement is used to collect entity statements and a valid trust chain in order to establish trust with a remote peer.
In order to fetch an entity statement, an entity needs to know the identifier of the entity to ask, and the identifier of the entity that you want the statement to be about.
The request MUST be an HTTP request using the GET method to a resolved federation API endpoint with the following query string parameters:
The following is a non-normative example of an API request for entity statement:
GET /.well-known/openid-federation? iss=https%3A%2F%2Fopenid.sunet.se%2Ffederation HTTP/1.1 Host: openid.sunet.se
(postamble)
As long as the request is correct, understood and accepted, the response is a signed entity statements. The content type MUST be in that case be set to application/jose. If the issuing entity does not recognize the subject or the issuer, it should return an error response as a JSON object. More about error responses below
A non-normative example of a response:
200 OK Last-Modified: Wed, 22 Jul 2018 19:15:56 GMT Content-Type: application/jose eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL3Nlc...
(the signed JWT is truncated)
An entity may use the trust negotiation operation in order to fetch a resolved metadata of it self as seen/trusted by a remote peer. The remote peer will fetch the metadata and necessary trust chain to the requester, and perform metadata flattening. The remote peer should include it own trust root configuration when generating the resulting metadata. The metadata returned should be the same as the one used in the software.
Resolving metadata for a specific type of metadata, for a given peer. The relying party may ask a specific provider to resolve the relying party openid_client metadata with its own configured trust root. The result may tell what operations, scopes and claims the relying party is allowed to use.
The following is a non-normative example of an API request for trust negotiation:
GET /.well-known/openid-federation? op=resolve_metadata& iss=https%3A%2F%2Fopenid.sunet.se%2Ffederation& type=openid_provider& peer=https%3A%2F%2Fidp.umu.se%2Fopenid HTTP/1.1 Host: openid.sunet.se
The response is a generated flattened metadata of the type specified in the request.
A non-normative example of a response:
200 OK Last-Modified: Wed, 22 Jul 2018 19:15:56 GMT Content-Type: application/json { "organization": "University of Ume?", "contacts": ["legal@umu.se", "technical@umu.se"], "logo_uri": "https://www.umu.se/SRWStatic/img/umu-logo-left-neg-SE.svg", "policy_uri": "https://www.umu.se/en/about-the-website/legal-information/", "authorization_endpoint": "https://idp.umu.se/openid/authorization", "token_endpoint": "https://idp.umu.se/openid/token", "response_types_supported": ["code", "code id_token", "token"], "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer" ], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "LoaMax": "http://swamid.se/LoA/substantial", "expires": 8776635 }
An entity may query another entity for a list of all the entities that that entity is prepared to issue statements about.
The following request parameters are allowed in the query part:
The following is a non-normative example of an API request for trust negotiation:
GET /.well-known/openid-federation? op=listing& iss=https%3A%2F%2Fopenid.sunet.se%2Ffederation& type=openid_provider& claims=client_name HTTP/1.1 Host: openid.sunet.se
The response MUST contain an JSON object where the known entity identifiers are the property keys, and a JSON object with the requested claims is the property value. Requested claims that the responder is not able to provide should be left out.
A non-normative example of a response:
200 OK Last-Modified: Wed, 22 Jul 2018 19:15:56 GMT Content-Type: application/json { "https://ntnu.andreas.labs.uninett.no/": { "client_name": "NTNU Labs" }, "https://blackboard.ntnu.no/openid/callback": { "client_name": "Blackboard" }, "https://serviceprovider.andreas.labs.uninett.no/application17": { "client_name": "Test application" } }
TBD.
If the request was malformed, or some error occurred during processing of the request, the following standardized error format should be used regardless of the operation specified.
The HTTP response code MUST be something else than 200, giving an indication of the type of error. The response body MUST be a JSON object conatining the claims below and the content type MUST be set to application/json.:
A non-normative example of an error response:
400 Bad request Last-Modified: Wed, 22 Jul 2018 19:15:56 GMT Content-Type: application/json { "op": "fetch", "error": "generic_error", "error_description": "Required request parameter [iss] was missing." }
An entity (the Consumer) that wants to establish trust with a remote peer, must have the remote peer's entity identifier and a list of entity ID's of trusted trust anchors together with the public version of their signing keys. The Consumer will first have to fetch sufficient entity statements to establish at least one chain of trust from the remote peer to one or more of the configured trust anchors. After that the entity MUST validate the trust chains independently, and -- if there are multiple valid trust chains and if the application demands it -- choose one.
Depending on the circumstances the Consumer may either be handed the remote peer's self-issued entity statement or it may have to fetch it by itself. If it needs to fetch it, it will use the process described in Fetch Entity Statements with both *iss* and *sub* set to the entity ID of the remote peer.
The next step is to iterate through the list of intermediates listed in authority_hints, ignoring the authority hints that end in an unknown trust anchor, requesting an entity statement about the remote peer from the intermediate. If the received entity statement contains an authority hint this process is repeated. This time with the *iss* set to the intermediates entity ID and the *sub* to be the *iss* of the previous query. The Consumer should never attempt to fetch entity statements it already has fetched during this process (loop prevention).
A successful operation will return one or more lists of entity statements. Each of the lists terminating in an entity statement issued by a trusted trust anchor.
If there is no path from the remote peer to at least one of the trusted trust anchors then the list will be empty and there is no way of establishing trust in the remote peer's information.
A trust chain consists of an ordered list of entity statements that starts with the self issued entity statement (ES[0]) of the remote peer and ends with an entity statement signed by a trusted trust anchor (ES[i]).
Except for the self-signed entity statement (ES[0]) each entity statement refers back to the one preceding it, such that the *sub* in one entity statement (ES[j]) is the *iss* in the immediate preceding (ES[j-1]).
A trust chain without any intermediate entities is also valid.
To validate the chain, the following steps are done for each entity statement ES[j] j=i,..,0 :
If multiple valid trust chains are found, the Consumer will need to decide on which one to use.
One simple rule would be to prefer a shorter chain over a longer one.
Each entity statement in a trust chain is signed and MUST have a expiration time (exp) set. The expiration time of the whole trust chain is the expiration time that is in the future and closest in time to the present time.
This specification allows for a smooth process of updating metadata and public keys.
As described above in lifetime calculation each trust chain has a expiration time. A consumer of metadata using this specification MUST support refreshing a trust chain when it expires. How often a consumer SHOULD re-evaluate the trust chain depends on how quickly the consumer wants to find out that something has changed in the trust chain.
If a leaf entity publishes its public keys in the metadata part using *jwks* setting an expiration time on the self-signed entity statement can be used to control how often the remote party is fetching an updated version of the public key.
A trust anchor must publish a self-signed entity statement about itself. Such an entity statement may be part of a trust cahin or it might not. The trust anchor SHOULD set a reasonable expiration time on that statement, such that the consumers will re-fetch the entity statement at reasonable intervals. If the trust root wants to roll over its signing keys it would have to:
It has to be taken into considerations that clients may have manually configured pubic keys as part of configuration.
Since we expect the consumers to check the trust chain at regular reasonable frequent times we feel that we do not need to specify a standard revocation process. Specific federations may make a different choice and will then have to add such a process.
The metadata for a specific entity can be constructed by starting with the information in ES[i] and then adding the information from the statements ES[i-1] to ES[0] using the following rules:
A subset is defined as:
The following is a non-normative example of a set of relying party-specific metadata statements that together form the metadata for an entity:
ms[2]
{ "contacts": ["helpdesk@example.com"], "logo_uri": "https://example.com/logo.jpg", "policy_uri": "https://example.com/policy.html", "tos_uri": "https://example.com/tos.html" }
ms[1]
{ "rp_scopes": ["openid", "eduperson"], "response_types": ["code", "code id_token"], }
ms[0]
{ "contacts": ["rp_admins@cs.example.com"], "redirect_uris": ["https://cs.example.com/rp1"], "response_types: ["code"] }
sum(ms[n]), (n=0...2)
{ "contacts": ["helpdesk@example.com"], "logo_uri": "https://example.com/logo.jpg", "policy_uri": "https://example.com/policy.html", "tos_uri": "https://example.com/tos.html" "rp_scopes": ["openid", "eduperson"], "response_types": ["code"], "redirect_uris": ["https://cs.example.com/rp1"], }
This section describes how the trust framework in this specification is used to establish trust between an OpenID Relying Party and an OpenID Provider that has no explicit configuration or registration in advance.
There is two alternative approaches to establish trust between a Relying Party and a Provider, what we call implicit and explicit registration. Members of a federation or a community should agree upon which one to use. While implementations should support both methods, deployments may choose to disable the use of one of them.
The trust between the entities is established using the above described extensions in the first two steps of the communication between an RP and an OP. How the RP found the OP in the first place is out of scope for this document.
------ ------ | | <--- 1) Discovery ------------------> | | | RP | ---- 2) Authentication request -----> | OP | | | | | ------ ------
After the discovery and registration is completed a first time, those steps SHOULD only be repeated if any changes occur (see notes in respective sections below).
The client_id of the RP MUST be set identical to the RP entity identifier.
Without a registration process, the RP does not have a client_secret. Instead the implicit registration model requires the RP to make use of asymmetric cryptography.
The RP MUST host a Federation API that allows the OP to fetch the entity statements.
The authentication request as specified in OpenID Connect Core.
When the OP receives an incoming authentication request, the OP supports OpenID Connect Federation and the incoming client_id is a valid URL, the OP should try to resolve and fetch the entity statement as described in fetching entity statements.
The OP should validate the possible trust chains, and resolve the RP metadata with type openid_client.
The OP should consider the resolved metadata of the RP, and verify that it complies with the client metadata specification in OpenID Connect Dynamic Client Registration 1.0.
If the OP fails to establish trust with the RP, it should use the error_description error code, and an error_description that aids the RP to fix what is wrong.
The RP will have to use asymmetric crypto to authenticate to the token endpoint. The RP MUST authenticate the request by including the private_key_jwt parameter, as described in Section 9 of OpenID Connect Core.
This method involves performing an explicit registration of a new client the first time a Relying Party interacts with an OpenID Provider using something that basically foolws the steps in OpenID Connect Dynamic Client Registration 1.0 but where the client registration request is a signed entity statement.
The RP will start by gathering the OPs metadata using the process specified in Resolving trust chain and metadata above.
The OP MUST support dynamic relying party registration. That it does so is signaled by having the claim federation_registration_endpoint in the metadata.
Given that the OP supports dynamic registration the RP progresses as follows:
A client registration using this specification is not expected to be valid for ever. The entity statements exchanged all have expiration times. Which means that the registration will eventually time out. An OP can also for some reason decided that a client registration is not valid anymore. To this can be added that the entities in the federation, for a number of reasons, over time may change how fast their signature will expire, thereby increasing or decreasing the lifetime of a trust chain.
At regular intervals the RP MUST:
At regular intervals the OP MUST:
There are a number of expiration times that MUST considered:
Taking this into consideration, an OP MUST NOT assign a expiration time to a relying party registration that is after the expiration time of the metadata statement signatures.
TBD
TBD
A service Foodle would like to offer its services to all identity providers in eduGAIN. Foodle is managed and registered by the university NTNU. NTNU is part of the Norwegian Feide federation. Foodle is also directly trusted in the Swedish SWAMID federation. Both Feide and SWAMID are part of the international eduGAIN federation.
The Foodle service chooses to use the entity identifier https://foodl.org/. And upon deployment, Foodle is setup with an RSA key pair, with the following public key:
{ "kid": "key1", "use": "sig", "kty": "RSA", "alg": "RS256", "n": "pnXBOusEANuug6ewezb9J_XbxbSGEISyA75wBGkerPNg6WTXmmxJ-DV1U4sCu RqhSdo3Uncmw6-01bZKCtAyRHT_TOZN2TMfNPRsfLkOstVofyfxg5oIWViLX9IDG_iZVd q6_T6yOuufOIvqpaeBMwSKuDXHNa_DU0aUu_3kOAc5_2hD4Dq-XXtum-oix2EPkNSbFfP qFIp5n4gS1XrzGzuNQiDw82k-H6mWN0wlVWfqLxJA9DZikAX7x9feipn36wxDH-XUlzDD Ui3nfnC8GSkT-CYII3oZPsIgMV527iQGVsehIV9KqTF2FnaP83cqV9YgvMfhs1wrx4L3Z -3B8Q", "e": "AQAB", "key_ops": ["verify"], "ext": true }
Foodle offers a WebFinger interface and a metadata API according to this specification, with the ability to issue entity statements about itself.
How trust is established and entities becomes part of a federation is out of scope of this specification. It could involve some kind of non-technical contract, agreement or term of use that is established, followed by a federation or trust issuer that registers an entity identifier, public key and a set of metadata that restricts the delegated trust that is represented in the entity statement about the joining party.
The following example, assumes the following trust relations are established, and the following entities are able to issue entity statements:
Foodle has a local trust root configuration that looks like this (notice that the exact format and content on the trust root configuration is out of scope of this specification):
[ { "sub": "https://www.sunet.se/swamid", "sub_meta": {"name": "SWAMID"}, "metadata": { "openid_provider": { "loa_max": "http://swamid.se/LoA/substantial"} }, "jwks": { "keys": [ { "kty": "RSA", "alg": "RS256", "n": "v6xydqciFKGfvQaqYGmk9A7etbfvNY[...]", "e": "AQAB", "key_ops": ["verify"], "ext": true, "kid": "swamid", "use": "sig" } ] } } ]
Let us assume a student from Umeå would like to login at Foodle. Some sort of discovery process involves the end user choosing an OpenID provider. OpenID Discovery using the e-mail address is one option. Foodle presenting a list of available providers for the user to choose from is another.
After the discovery process, Foodle knows that the user would like to login using the OpenID provider with entity identifier https://www.umu.se/openid.
Foodle normalizes the entity identifier of the OpenID Provider, and performs a request to fetch the self-issued entity statement using the Federation API of the OpenID provider.
GET /.well-known/openid-federation? iss=https%3A%2F%2Fumu.se%2Fopenid HTTP/1.1 Host: umu.se
HTTP/1.1 200 OK Content-Type: application/json ["eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InVtdSJ9.eyJpYXQi..."]
The API endpoint returns a list of signed entity statements. In this case we look for a self-issued statement from the Umeå university. We then decode and inspect the content:
{ "iat": 1539174048, "exp": 1539177648, "metadata": { "openid_provider": { "authorization_endpoint": "https://idp.umu.se/openid/authorization", "token_endpoint": "https://idp.umu.se/openid/token", "response_types_supported": ["code", "code id_token", "token"], "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer" ], "subject_types_supported": ["pairwise", "public"], "id_token_signing_alg_values_supported": ["RS256"], "logo_uri": "https://www.umu.se/img/umu-logo-left-neg-SE.svg", "policy_uri": "https://www.umu.se/en/website/legal-information/", "loa_max": "http://eidas.europa.eu/LoA/high" } }, "iss": "https://umu.se/openid", "sub": "https://umu.se/openid", "authority_hints": { "https://www.sunet.se/swamid": ["https://edugain.org/oidc"], "https://kalmar2.org/openid": [] }, "jwks": { "keys": [ { "kty": "RSA", "alg": "RS256", "n": "z1V1kyi6qwmXfKsfhVqKUMmQH3AixN[...]", "e": "AQAB", "key_ops": ["verify"], "ext": true, "kid": "umu", "use": "sig" } ] } }
In order to establish trust with this provider, the Foodle service provider would need to fetch sufficient entity statements to represent a complete chain from the self-issued statement to the locally configured trust root, which contains SWAMID.
The information found in the authority_hints is critical in order to dynamically discover the trust chain. If such hints are not present, the relying party may resume to fixed configured trust roots to ask for trust statements.
In this example, Foodle now fetches an entity statement from SWAMID using the Federation API endpoint of SWAMID, discovered in the authority_hints claim.
GET /.well-known/openid-federation? iss=https%3A%2F%2Fwww.sunet.se%2Fswamid& sub=https%3A%2F%2Fumu.se%2Fopenid HTTP/1.1 Host: www.sunet.se
HTTP/1.1 200 OK Content-Type: application/json ["eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZlaWRlIn0.eyJpY..."]
The decoded version of the entity statement is:
{ "iat": 1539174048, "exp": 1539177648, "metadata": { "openid_provider": { "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256", "RS512"], "organization": "University of Ume?", "contacts": ["legal@umu.se", "technical@umu.se"] }, "openid_client": {} }, "iss": "https://www.sunet.se/swamid", "sub": "https://umu.se/openid", "sub_meta": {"name": "University of Ume?"}, "jwks": { "keys": [ { "kty": "RSA", "alg": "RS256", "n": "v6xydqciFKGfvQaqYGmk9A7etbfvNY[...]", "e": "AQAB", "key_ops": ["verify"], "ext": true, "kid": "swamid", "use": "sig" } ] } }
Notice that the entity statement about University of Umeå also contains an entry for openid_client metadata. This indicates that SWAMID expresses this university to be trusted to issue its own OpenID clients without the need for registering these directly in SWAMID.
These two entity statements are sufficient to establish a path from the local configured trust anchor which trust SWAMID, to the self-issued statement from the University of Umeå. Here are the steps performed to validate the trust chain:
The output from the trust chain validation is an ordered list of entity statements. In order to extract the needed metadata, we need to look at the metadata type relevant in the given context. In this case, we are establishing trust with an OpenID Provider, and we take a look at the openid_provider metadata object of the trust chain:
[ {"loa_max": "http://swamid.se/LoA/substantial"}, { "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256", "RS512"], "organization": "University of Ume?", "contacts": ["legal@umu.se", "technical@umu.se"] }, { "authorization_endpoint": "https://idp.umu.se/openid/authorization", "token_endpoint": "https://idp.umu.se/openid/token", "response_types_supported": ["code", "code id_token", "token"], "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer" ], "subject_types_supported": ["pairwise", "public"], "id_token_signing_alg_values_supported": ["RS256"], "logo_uri": "https://www.umu.se/SRWStatic/img/umu-logo-left-neg-SE.svg", "policy_uri": "https://www.umu.se/en/about-the-website/legal-information/", "loa_max": "http://eidas.europa.eu/LoA/high" } ]
The metadata flattening process converts this to a single metadata object. The resulting metadata in this case would be:
{ "organization": "University of Ume?", "contacts": ["legal@umu.se", "technical@umu.se"], "logo_uri": "https://www.umu.se/SRWStatic/img/umu-logo-left-neg-SE.svg", "policy_uri": "https://www.umu.se/en/about-the-website/legal-information/", "authorization_endpoint": "https://idp.umu.se/openid/authorization", "token_endpoint": "https://idp.umu.se/openid/token", "response_types_supported": ["code", "code id_token", "token"], "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer" ], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "loa_max": "http://swamid.se/LoA/substantial" }
Foodle after establishing trust with the University of Umeå and extracted a resulting set of metadata, will send an authentication request to the OpenID provider. This example involves the implicit registration.
Here is an example of an authentication request:
GET /authorize? response_type=code &scope=openid%20profile%20email &client_id=https%3A%2F%2Ffoodl.org%2F &state=2ff7e589-3848-46da-a3d2-949e1235e671 &redirect_uri=https%3A%2F%2Ffoodl.org%2Fopenid%2Fcallback HTTP/1.1 Host: idp.umu.se
The provider receiving this authentication request will, unless the RP is cached or statically configured, start to dynamically fetch and establish trust with the RP.
The provider needs to establish a trust chain for the RP from which a authentication was received. The provider in this example has the following configured trust root:
[ { "sub": "https://edugain.org/oidc", "metadata": { "openid_client": { "rp_scopes": ["openid", "userid-targetedid", "eduperson"], "response_types": ["code", "code id_token"] } }, "jwks": { "keys": [ { "kty": "RSA", "use": "sig", "alg": "RS256", "n": "qnd5_krrHKzuJzb5_YEt4sP-YOGSbf[...]", "e": "AQAB", "key_ops": ["verify"], "ext": true, "kid": "edugain" } ] } }, { "sub": "https://www.sunet.se/swamid", "metadata": { "openid_client": { "rp_scopes": ["openid", "userid-persistent", "fs"], "response_types": ["code", "code id_token"] } }, "jwks": { "keys": [ { "kty": "RSA", "alg": "RS256", "n": "v6xydqciFKGfvQaqYGmk9A7etbfvNY[...]", "e": "AQAB", "key_ops": ["verify"], "ext": true, "kid": "swamid", "use": "sig" } ] } } ]
The provider starts to resolve metadata for the client identifier https://foodl.org/ by fetching the self-issued entity statement using the Federation API.
uses WebFinger, the metadata API endpoints and the authority_hints in order to establish a full trust chain to the trust root.
In this case there are two possible trust chains:
A research project has pooled resources and bought an extremely rare and expensive equipment (EREE) that MUST be accessible by all project participants disregarding which university/research organization/company they belong for. To that end the research project have created its own federation (EREE) and are expecting the participants to get their organizations OP to register with the EREE federation. These OPs are of course expected to be members in one or more other federations. So we have to EREE service and an EREE federation. Since the EREE equipment is placed in Sweden the EREE service is also member of the SWAMID federation.
The EREE service choose to use the entity identifier https://srv.eree.example.org/. And upon deployment, EREE is setup with a Elliptic Curve key pair, with the following public key:
{ "keys": [ { "kty": "EC", "use": "sig", "kid": "bmRkVmk0QUY3UUdnM3NDekI4VGptRUIxVk5lRXIyVE9rRUZpMUpNbGJ...", "crv": "P-256", "x": "ypFDCBLLT7lRP8UPo12ycnIkyFjeL1yco_Iu7VZoeDk", "y": "1sO4UIY1Iil0_PYobPKhuhs5ocQqVWYCujXcfo47epg" } ] }
{"https://eree.example.org":["https://eree.example.org"]
The EPEE service is provided files containing authority_hints by its superiors. From the EPEE federation it gets:
{"https://swamid.se":["https://swamid.se"]
from SWAMID:
{"https://uninett.no":["https://uninett.no"]
and from UNINETT:
and so on
On the federations side:
And finally, from the federations the EPEE service also receives the public part of the federations signing keys.
A researcher from Umeå wants to access the EPEE service. The EPEE service provides a discovery service which allows the researcher to chose which identity provider to use. In this case https://op.umu.se/ .
Using the entity ID (issuer ID) of the identity provider the service performs a fetch entity statement request as described in Fetch Entity Statement
GET /.well-known/openid-federation?iss=https%3A%2F%2Fop.umu.se HTTP/1.1 Host: op.umu.se
HTTP/1.1 200 OK Content-Type: application/json eyJhbGciOiJFUzI1NiIsImtpZCI6IlFVOUxUbkpzTjJ4VVRYQkZSM040T1Z...
{ "authority_hints": { "https://epee.example.org": [ "https://epee.example.org" ], "https://swamid.se": [ "https://swamid.se" ] }, "exp": 1543851936, "iat": 1543247136, "iss": "https://op.umu.se", "jwks": { "keys": [ { "crv": "P-256", "kid": "QU9LTnJsN2xUTXBFR3N4OVZOeTlyejFrWWthYWlaTllYMDR...", "kty": "EC", "use": "sig", "x": "DU6e1SjvW3Gqcd7up-n8s1N6Zlm2cNlZjYqL3O36v1A", "y": "pEtk0_fSKN56V-2hDnzFUbaw8-v0QBjNoT2KaZ7pqIc" } ] }, "metadata": { "openid_provider": { "authorization_endpoint": "https://op.umu.se/authorization", "federation_registration_endpoint": "https://op.umu.se/fedreg", "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer" ], "id_token_signing_alg_values_supported": [ "RS256" ], "logo_uri": "https://www.umu.se/img/umu-logo-left-neg-SE.svg", "policy_uri": "https://www.umu.se/en/website/legal-information/", "response_types_supported": [ "code", "code id_token", "token" ], "subject_types_supported": [ "pairwise", "public" ], "token_endpoint": "https://op.umu.se/token", "userinfo_endpoint": "https://op.umu.se/user" } }, "sub": "https://op.umu.se" }
The decoded version of the entity statement is:
In order to establish trust with this provider, the EEPE service provider would need to fetch sufficient entity statements to represent a complete chain from the self issued statement to the trust anchor that represents the EPEE federations.
GET /.well-known/openid-federation? iss=https%3A%2F%2Feree.example.org& sub=https%3A%2F%2Fop.umu.se HTTP/1.1 Host: eree.example.org
The authority_hints in the self-signed entity statement points to 2 trust anchors "https://epee.example.org" and "https://swamid.se" of these only the *EPEE* one is interesting. The RP therefor chooses to only follow that trust path. The next step being fetching an entity statement about "https://op.umu.se" signed by the EPEE federation. This is done by doing a fetch entity statement:
HTTP/1.1 200 OK Content-Type: application/json eyJhbGciOiJFUzI1NiIsImtpZCI6IlFuRlJWMEZ6YjE5NVdW...
{ "exp": 1543852816, "iat": 1543248016, "iss": "https://eree.example.org/", "jwks": { "keys": [ { "crv": "P-256", "kid": "QU9LTnJsN2xUTXBFR3N4OVZOeTlyejFrWWthYWlaTllYMDRXSk", "kty": "EC", "use": "sig", "x": "DU6e1SjvW3Gqcd7up-n8s1N6Zlm2cNlZjYqL3O36v1A", "y": "pEtk0_fSKN56V-2hDnzFUbaw8-v0QBjNoT2KaZ7pqIc" } ] }, "metadata": { "openid_provider": {} }, "sub": "https://op.umu.se", "sub_is_leaf": true }
The decoded version of the returned entity statement is:
A couple of things worth noting about the response:
These two entity statements are sufficient to establish a path from the local configured trust anchor which trust the EPEE federation, to the self-issued statement from the Identity provider at the University of Umeå. Here are the steps performed to validate the trust chain as described in validating the trust chain. We start with the signed entity statement issued by EPEE about the OP at the University of Umeå
Now, we can work on the self-signed entity statement published by the OP@UmU.
[ {}, { "authorization_endpoint": "https://op.umu.se/authorization", "federation_registration_endpoint": "https://op.umu.se/fedreg", "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer" ], "id_token_signing_alg_values_supported": [ "RS256" ], "logo_uri": "https://www.umu.se/img/umu-logo-left-neg-SE.svg", "policy_uri": "https://www.umu.se/en/website/legal-information/", "response_types_supported": [ "code", "code id_token", "token" ], "subject_types_supported": [ "pairwise", "public" ], "token_endpoint": "https://op.umu.se/token", "userinfo_endpoint": "https://op.umu.se/user" } ]
The output from the trust chain validation is an ordered list of entity statements. In order to extract the needed metadata, we need to look at the metadata type relevant in the given context. In this case, we are establishing trust with an OpenID Provider, and we take a look at the *openid_provider* metadata object of the trust chain:
Since the first item in this list is empty the flattening process will return the second item as the result.
{ "authority_hints": { "https://eree.example.org": [ "https://eree.example.org" ] }, "jwks": { "keys": [ { "crv": "P-256", "kid": "bmRkVmk0QUY3UUdnM3NDekI4VGptRUIxVk5lRXIyVE9rRUZpMUpNb...", "kty": "EC", "use": "sig", "x": "ypFDCBLLT7lRP8UPo12ycnIkyFjeL1yco_Iu7VZoeDk", "y": "1sO4UIY1Iil0_PYobPKhuhs5ocQqVWYCujXcfo47epg" } ] }, "metadata": { "openid_client": { "application_name": "rphandler", "application_type": "web", "contacts": [ "ops@example.org" ], "jwks_uri": "https://rp.eree.example.org/static/jwks.json", "redirect_uris": [ "https://rp.eree.example.org/authz_cb" ], "response_types": [ "code" ], "scope": [ "openid", "profile", "email",Extracting the provider metadata "address", "phone" ], "token_endpoint_auth_method": "private_key_jwt" } }, "sub": "https://rp.eree.example.org" }
Now when the RP has trusted information about the OP it can do a dynamic client registration. To that end it collects information about itself that it wants to register. This should be no different from what a normal OIDC RP does. To this it adds information about the RP federation signing keys, the *sub* and *authority_hints*. Ones it has all that information it creates an entity statement. The result of all this work may look something like this:
Next it self-signs this statement and sends it as a client registration request to the *federation_registration_endpoint* of the OP.
To collect the trust chains the OP uses the *authority_hints* in the self-signed entity statement it received from the RP (the client registration request).
GET /.well-known/openid-federation? iss=https%3A%2F%2Feree.example.org& sub=https%3A%2F%2Frp.epee.example.org HTTP/1.1 Host: eree.example.org
In this case it is only one which points to *https://eree.example.org* . So the OP fetches the entity statement that the EPEE federation publishes on the EPEE RP.
HTTP/1.1 200 OK Content-Type: application/json eyJhbGciOiJFUzI1NiIsImtpZCI6IlFuRlJWMEZ6YjE5NVd...
With the response:
{ "exp": 1543865440, "iat": 1543260640, "iss": "https://eree.example.org", "jwks": { "keys": [ { "crv": "P-256", "kid": "bmRkVmk0QUY3UUdnM3NDekI4VGptRUIxVk5lRXIyVE9rRUZpM...", "kty": "EC", "use": "sig", "x": "ypFDCBLLT7lRP8UPo12ycnIkyFjeL1yco_Iu7VZoeDk", "y": "1sO4UIY1Iil0_PYobPKhuhs5ocQqVWYCujXcfo47epg" } ] }, "metadata": { "openid_client": { "application_type": "web", "request_object_signing_alg": "ES256", "response_types": [ "code" ], "scope": [ "openid", "email" ], "token_endpoint_auth_method": "private_key_jwt", "token_endpoint_auth_signing_alg": "ES256", "userinfo_signed_response_alg": "ES256" } }, "sub": "https://rp.eree.example.org" }
Unpacked this becomes:
The process here is the one described in validating the trust chain.
{ "application_type": "web", "application_name": "EPEE", "contacts": ["ops@epee.example.org"], "jwks_uri": "https://rp.eree.example.org/static/jwks.json", "redirect_uris": [ "https://rp.eree.example.org/authz_cb" ], "request_object_signing_alg": "ES256", "response_types": [ "code" ], "scope": [ "openid", "email" ], "token_endpoint_auth_method": "private_key_jwt", "token_endpoint_auth_signing_alg": "ES256", "userinfo_signed_response_alg": "ES256" }
The OP does flattening on the information in the trust chain and comes up with:
{ "authority_hints": { "https://eree.example.org": [ "https://eree.example.org" ] }, "exp": 1543931097, "iat": 1543326297, "iss": "https://op.umu.se", "kid": "QU9LTnJsN2xUTXBFR3N4OVZOeTlyejFrWWthYWlaTllYMDRXSkN...", "metadata": { "openid_client": { "application_name": "EPEE", "application_type": "web", "contacts": [ "ops@epee.example.org" ], "jwks_uri": "https://rp.eree.example.org/static/jwks.json", "redirect_uris": [ "https://rp.eree.example.org/authz_cb" ], "request_object_signing_alg": "ES256", "response_types": [ "code" ], "scope": [ "openid", "email" ], "token_endpoint_auth_method": "private_key_jwt", "token_endpoint_auth_signing_alg": "ES256", "userinfo_signed_response_alg": "ES256" } }, "sub": "https://rp.eree.example.org" }
Happy with the information in the client registration request the OP takes the flattened metadata and creates an entity statement by add *sub* and *authority_hints*:
The RP MUST collect the trust chain ending in the EPEE trust anchor and verify the correctness of the trust chain but refrain from flattening the metadata. This since the entity statement issued by the EPEE federation about the UmU OP are only valid for that entity and not for the EPEE RP.
If the RP is OK with what the OP decided on regarding the RPs metadata then it will store this to be used in the following OIDC protocol exchange with the OP.
In this example the RP decided on one specific trust anchor before sending the registration request. If that was not the case but the RP chose to send a registration request with more than one authority_hint then this is the time when then RP can flatten and store the provider metadata. This since the OP MUST choose one and only one authority (=trust anchor) for the registration response.
Copyright (c) 2018 The OpenID Foundation.
The OpenID Foundation (OIDF) grants to any Contributor, developer, implementer, or other interested party a non-exclusive, royalty free, worldwide copyright license to reproduce, prepare derivative works from, distribute, perform and display, this Implementers Draft or Final Specification solely for the purposes of (i) developing specifications, and (ii) implementing Implementers Drafts and Final Specifications based on such documents, provided that attribution be made to the OIDF as the source of the material, but that such attribution does not indicate an endorsement by the OIDF.
The technology described in this specification was made available from contributions from various sources, including members of the OpenID Foundation and others. Although the OpenID Foundation has taken steps to help ensure that the technology is available for distribution, it takes no position regarding the validity or scope of any intellectual property or other rights that might be claimed to pertain to the implementation or use of the technology described in this specification or the extent to which any license under such rights might or might not be available; neither does it represent that it has made any independent effort to identify any such rights. The OpenID Foundation and the contributors to this specification make no (and hereby expressly disclaim any) warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to this specification, and the entire risk as to implementing this specification is assumed by the implementer. The OpenID Intellectual Property Rights policy requires contributors to offer a patent promise not to assert certain patent claims against other contributors and against implementers. The OpenID Foundation invites any interested party to bring to its attention any copyrights, patents, patent applications, or other proprietary rights that may cover technology that may be required to practice this specification.
[[ To be removed from the final specification ]]
-06
-05
-04