From ff3d76c355bdabfab134225b5c9aab639ffb19e6 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 2 Aug 2021 08:46:06 -0500 Subject: [PATCH 01/14] update diagram to introduce AS and token exchange --- sequence.mmd | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/sequence.mmd b/sequence.mmd index e98849b..87bb417 100644 --- a/sequence.mmd +++ b/sequence.mmd @@ -1,17 +1,31 @@ sequenceDiagram + autonumber + participant WebID as User's WebID Document + participant OP as OpenID Provider participant RC as Resource Client participant RS as Resource Server - participant OP as OpenID Provider - participant WebID as User's WebID Document - RC ->> RS: 1. unauthenticated request - RS ->> RC: 2. 401 with a WWW-Authenticate HTTP header - RC ->> OP: 3. start Authorization Code grant + participant AS as Authorization Server + RC ->> RS: unauthenticated request + RS ->> RC: 401 with a WWW-Authenticate HTTP header + RC ->> OP: start Authorization Code grant OP ->> RC: return Authorization Code - RC ->> OP: 4. present Authorization Code and DPoP proof - OP ->> RC: 5. return DPoP bound Access Token and OIDC ID Token - RC ->> RS: 6. Access Token + DPoP proof - RS ->> WebID: 7. get WebID document to verify Issuer - WebID ->> RS: WebID document - RS ->> OP: 8. get OP's public key to verify Access Token (JWS) - OP ->> RS: JWKS - RS ->> RC: 9. if all checks pass respond with representation + RC ->> OP: present Authorization Code and DPoP proof + OP ->> RC: return DPoP bound OIDC ID Token + alt + RC ->> AS: request exchange of ID Token for Access Token + else + RC ->> AS: request exchange of ID Token for chainable Ticket + AS ->> RC: chainable Ticket + loop for each claim + RC ->> AS: exchange claim for chainable Ticket + AS ->> RC: chainable Ticket + end + RC ->> AS: exchange chainable Ticket for Access Token + end + AS ->> WebID: get WebID document to verify Issuer + WebID ->> AS: WebID document + AS ->> OP: get OP's public key to verify ID Token (JWS) + OP ->> AS: JWKS + AS ->> RC: if all checks issue Access Token + RC ->> RS: request with Access Token + RS ->> RC: representation From a27a9053279ab4bd1113f2d934483e4859578cc9 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 15 Nov 2021 07:46:49 -0600 Subject: [PATCH 02/14] updated sequence diagram to use UMA claims pushing --- sequence.mmd | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/sequence.mmd b/sequence.mmd index 87bb417..1536694 100644 --- a/sequence.mmd +++ b/sequence.mmd @@ -1,31 +1,21 @@ sequenceDiagram autonumber - participant WebID as User's WebID Document + participant WebID as End-User's WebID Document participant OP as OpenID Provider - participant RC as Resource Client + participant C as Client participant RS as Resource Server participant AS as Authorization Server - RC ->> RS: unauthenticated request - RS ->> RC: 401 with a WWW-Authenticate HTTP header - RC ->> OP: start Authorization Code grant - OP ->> RC: return Authorization Code - RC ->> OP: present Authorization Code and DPoP proof - OP ->> RC: return DPoP bound OIDC ID Token - alt - RC ->> AS: request exchange of ID Token for Access Token - else - RC ->> AS: request exchange of ID Token for chainable Ticket - AS ->> RC: chainable Ticket - loop for each claim - RC ->> AS: exchange claim for chainable Ticket - AS ->> RC: chainable Ticket - end - RC ->> AS: exchange chainable Ticket for Access Token - end - AS ->> WebID: get WebID document to verify Issuer + C ->> RS: unauthenticated request + RS ->> C: 401 with a WWW-Authenticate HTTP header + C ->> OP: start Authorization Code grant + OP ->> C: return Authorization Code + C ->> OP: present Authorization Code and DPoP proof + OP ->> C: return DPoP bound OIDC ID Token + C ->> AS: request Access Token and push solid Claim Token (including ID Token) + AS ->> WebID: get WebID document to verify OIDC Issuer WebID ->> AS: WebID document AS ->> OP: get OP's public key to verify ID Token (JWS) OP ->> AS: JWKS - AS ->> RC: if all checks issue Access Token - RC ->> RS: request with Access Token - RS ->> RC: representation + AS ->> C: if all checks pass issue Access Token + C ->> RS: request with Access Token + RS ->> C: representation From df7f59837457a2a82d80ba98e9689c2e689a5f5b Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 15 Nov 2021 08:17:50 -0600 Subject: [PATCH 03/14] more detailed sequence diagram --- sequence.mmd | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/sequence.mmd b/sequence.mmd index 1536694..8ac3944 100644 --- a/sequence.mmd +++ b/sequence.mmd @@ -1,5 +1,4 @@ sequenceDiagram - autonumber participant WebID as End-User's WebID Document participant OP as OpenID Provider participant C as Client @@ -7,15 +6,27 @@ sequenceDiagram participant AS as Authorization Server C ->> RS: unauthenticated request RS ->> C: 401 with a WWW-Authenticate HTTP header + Note over C: 👩 User provides their WebID ⌨️ + C ->> WebID: get WebID document to discover OpenID Provider + WebID ->> C: WebID document C ->> OP: start Authorization Code grant OP ->> C: return Authorization Code C ->> OP: present Authorization Code and DPoP proof OP ->> C: return DPoP bound OIDC ID Token + Note over C: 👩 User is authenticated ✅ C ->> AS: request Access Token and push solid Claim Token (including ID Token) - AS ->> WebID: get WebID document to verify OIDC Issuer + AS ->> WebID: get WebID document to verify OpenID Provider WebID ->> AS: WebID document AS ->> OP: get OP's public key to verify ID Token (JWS) OP ->> AS: JWKS - AS ->> C: if all checks pass issue Access Token + Note over AS: 👩 User is authenticated ✅ + AS ->> C: provide Access Token C ->> RS: request with Access Token + alt Token introspection + RS ->> AS: token introspection request + AS ->> RS: token introspection response + else signed JWT + Note over RS, AS: Verify AS signature + end + Note over RS: 👩 User is authenticated ✅ RS ->> C: representation From 37fe9d570ad43b96445d0d7e936e12c3fda800f4 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Sun, 21 Nov 2021 14:45:12 -0600 Subject: [PATCH 04/14] added Client ID Document to sequence diagram --- sequence.mmd | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sequence.mmd b/sequence.mmd index 8ac3944..97c0b36 100644 --- a/sequence.mmd +++ b/sequence.mmd @@ -1,17 +1,22 @@ sequenceDiagram - participant WebID as End-User's WebID Document - participant OP as OpenID Provider - participant C as Client - participant RS as Resource Server - participant AS as Authorization Server + participant WebID as 👩 End-User's WebID Document + participant OP as 👩 OpenID Provider + participant ClientID as ⚙️ Client's ID Document + participant C as ⚙️ Client + participant RS as ☁️ Resource Server + participant AS as ☁️ Authorization Server C ->> RS: unauthenticated request RS ->> C: 401 with a WWW-Authenticate HTTP header Note over C: 👩 User provides their WebID ⌨️ C ->> WebID: get WebID document to discover OpenID Provider WebID ->> C: WebID document C ->> OP: start Authorization Code grant + OP->> ClientID: get Client ID document + ClientID->> OP: ClientID document + Note over OP: compare redirect_uri OP ->> C: return Authorization Code C ->> OP: present Authorization Code and DPoP proof + Note over OP: ⚙️ Client is authenticated ✅ OP ->> C: return DPoP bound OIDC ID Token Note over C: 👩 User is authenticated ✅ C ->> AS: request Access Token and push solid Claim Token (including ID Token) @@ -19,7 +24,7 @@ sequenceDiagram WebID ->> AS: WebID document AS ->> OP: get OP's public key to verify ID Token (JWS) OP ->> AS: JWKS - Note over AS: 👩 User is authenticated ✅ + Note over AS: 👩 User and ⚙️ Client are authenticated ✅ AS ->> C: provide Access Token C ->> RS: request with Access Token alt Token introspection @@ -28,5 +33,5 @@ sequenceDiagram else signed JWT Note over RS, AS: Verify AS signature end - Note over RS: 👩 User is authenticated ✅ + Note over RS: 👩 User and ⚙️ Client are authenticated ✅ RS ->> C: representation From d07100df6085f5b41ebfe4b6baa0c1350be1228d Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Sun, 21 Nov 2021 14:54:05 -0600 Subject: [PATCH 05/14] replaced basic flow details with reference to the primer --- index.bs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/index.bs b/index.bs index 6a8403c..4247598 100644 --- a/index.bs +++ b/index.bs @@ -142,23 +142,11 @@ Solid and are used as a primary identifier for Users in this specification. *This section is non-normative* -The basic authentication and authorization flow is as follows: - -1. The Client requests a non-public resource from the RS. -2. The RS returns a 401 with a `WWW-Authenticate` HTTP header containing parameters that inform the - Client that a DPoP-bound Access Token is required. -3. The Client presents its Client Identifier and the associated Secret, if any, to the IdP and requests an - Authorization Code. -4. If granted, the Client presents the Authorization Code and a DPoP proof, to the Token Endpoint. -5. The Token Endpoint returns a DPoP-bound Access Token and OIDC ID Token, to the Client. -6. The Client presents the DPoP-bound Access Token and DPoP proof, to the RS. -7. The RS gets user's WebID Document and check for designated OIDC issuers -8. The RS gets the public key from the IdP and uses it to validate the signature on the DPoP-bound Access Token (JWS). -9. If IdP is designated by the user and Access Token is valid, then the RS returns the requested resource. +Details of the flow are available in [[!Solid.OIDC.Primer]]
-
Basic sequence of authentication and authorization as described above.
+
Basic sequence of authenticating the user and the client.
# Client Identifiers # {#clientids} @@ -559,6 +547,16 @@ Verborgh, Ricky White, Paul Worrall, Dmitri Zagidulin. "title": "Solid Protocol", "publisher": "W3C Solid Community Group" }, + "Solid.OIDC.Primer": { + "authors": [ + "Jackson Morgan", + "Aaron Coburn", + "Matthieu Bosquet" + ], + "href": "https://solid.github.io/solid-oidc/primer/", + "title": "Solid-OIDC Primer", + "publisher": "W3C Solid Community Group" + }, "WebID": { "authors": [ "Andrei Sambra", From 7997d64bd181559c855514d3b2b19dc0169a5d1a Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Sun, 21 Nov 2021 15:14:23 -0600 Subject: [PATCH 06/14] removed Access Token --- index.bs | 100 ++++++++++++++++++++++--------------------------------- 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/index.bs b/index.bs index 4247598..c7622f6 100644 --- a/index.bs +++ b/index.bs @@ -60,7 +60,7 @@ that additional functionality is required. The additional functionality documented herein aims to address: -1. Resource servers having no existing trust relationship with identity providers. +1. Resource servers and their Authorization servers having no existing trust relationship with identity providers. 2. Ephemeral Clients as a first-order use-case. ## Out of Scope ## {#intro-out-of-scope} @@ -302,58 +302,35 @@ Assuming one of the following options - Client ID and Secret, and valid DPoP Proof (for dynamic and static registration) - Dereferencable Client Identifier with a proper Client ID Document and valid DPoP Proof (for a Solid client identifier) -the IdP MUST return two tokens to the Client: +the IdP MUST return A DPoP-bound OIDC ID Token. -1. A DPoP-bound Access Token -2. An OIDC ID Token +## DPoP-bound OIDC ID Token ## {#tokens-id} -## DPoP-bound Access Token ## {#tokens-access} - -The DPoP-bound Access Token MUST be a valid JWT. See also: [[!RFC7519]]. - -When requesting a DPoP-bound Access Token, the Client MUST send a DPoP proof JWT +When requesting a DPoP-bound OIDC ID Token, the Client MUST send a DPoP proof JWT that is valid according to the [[DPOP#section-5]]. The DPoP proof JWT is used to -bind the access token to a public key. See also: [[!DPOP]]. +bind the OIDC ID Token to a public key. See also: [[!DPOP]]. -With the `webid` scope, the DPoP-bound Access Token payload MUST contain these claims: +With the `webid` scope, the DPoP-bound OIDC ID Token payload MUST contain these claims: * `webid` — The WebID claim MUST be the user's WebID. * `iss` — The issuer claim MUST be a valid URL of the IdP instantiating this token. - * `aud` — The audience claim MUST either be the string `solid` or be an array - of values, one of which is the string `solid`. In the decentralized world - of Solid OIDC, the principal of an access token is not a specific endpoint, - but rather the Solid API; that is, any Solid server at any accessible address + * `aud` — The audience claim MUST an array of values, + one of which is the ClientID claim is used to identify the client. + (See also: [section 5. Client Identifiers](#clientids)). + another one is the string `solid`. + In the decentralized world + of Solid OIDC, the audience of ID TOken is not only the client, + but rather the Solid Authorization Server; + that is, any Solid Authorization Server at any accessible address on the world wide web. See also: [[RFC7519#section-4.1.3]]. + * `azp` - The ClientID claim is used to identify the client. + (See also: [section 5. Client Identifiers](#clientids)). * `iat` — The issued-at claim is the time at which the DPoP-bound - Access Token was issued. + OIDC ID Token was issued. * `exp` — The expiration claim is the time at which the DPoP-bound - Access Token becomes invalid. + OIDC ID Token becomes invalid. * `cnf` — The confirmation claim is used to identify the DPoP Public - Key bound to the Access Token. See also: [[DPOP#section-7]]. - * `client_id` - The ClientID claim is used to identify the client. See also: - [section 5. Client Identifiers](#clientids). - -
-

An example DPoP-bound Access Token: - -

-    {
-        "webid": "https://janedoe.com/web#id",
-        "iss": "https://idp.example.com",
-        "aud": "solid",
-        "iat": 1541493724,
-        "exp": 1573029723,
-        "cnf":{
-          "jkt":"0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
-        },
-        "client_id": "https://client.example.com/web#id"
-    }
-    
-
- -## OIDC ID Token ## {#tokens-id} - -When requesting the `webid` scope, the user's WebID MUST be present in the ID Token as the `webid` claim. + Key bound to the OIDC ID Token. See also: [[DPOP#section-7]].

An example OIDC ID Token: @@ -363,10 +340,13 @@ When requesting the `webid` scope, the user's WebID MUST be present in the ID To "webid": "https://janedoe.com/web#id", "iss": "https://idp.example.com", "sub": "janedoe", - "aud": "https://client.example.com/web#id", - "nonce": "n-0S6_WzA2Mj", - "exp": 1311281970, + "aud": ["https://client.example.com/web#id", "solid"], + "azp": "https://client.example.com/web#id", "iat": 1311280970, + "exp": 1311281970, + "cnf":{ + "jkt":"0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" + }, }

@@ -377,23 +357,23 @@ When requesting the `webid` scope, the user's WebID MUST be present in the ID To A DPoP Proof that is valid according to [DPoP Internet-Draft, Section 4.3](https://tools.ietf.org/html/draft-ietf-oauth-dpop-04#section-4.3), -MUST be present when a DPoP-bound Access Token is used. +MUST be present when a DPoP-bound OIDC ID Token is used. -## Access Token Validation ## {#resource-access-validation} +## OIDC ID Token Validation ## {#resource-access-validation} -The DPoP-bound Access Token MUST be validated according to +The DPoP-bound OIDC ID Token MUST be validated according to [DPoP Internet-Draft, Section 6](https://tools.ietf.org/html/draft-ietf-oauth-dpop-04#section-6), -but the RS MAY perform additional verification in order to determine whether to grant access to the +but the AS MAY perform additional verification in order to determine whether to grant access to the requested resource. The user's WebID in the `webid` claim MUST be dereferenced and checked against the `iss` claim in the -Access Token. If the `iss` claim is different from the domain of the WebID, then the RS MUST check +OIDC ID Token. If the `iss` claim is different from the domain of the WebID, then the AS MUST check the WebID document for the existence of a statement matching `?webid ?iss.`, where `?webid` and `?iss` are the values of the `webid` and `iss` claims respectively. -This prevents a malicious identity provider from issuing valid Access Tokens for arbitrary WebIDs. +This prevents a malicious identity provider from issuing valid OIDC ID Tokens for arbitrary WebIDs. -Unless the RS acquires IdP keys through some other means, or the RS chooses to reject tokens issued by this IdP, -the RS MUST follow OpenID Connect Discovery 1.0 [[!OIDC.Discovery]] to find an IdP's signing keys (JWK). +Unless the AS acquires IdP keys through some other means, or the AS chooses to reject tokens issued by this IdP, +the AS MUST follow OpenID Connect Discovery 1.0 [[!OIDC.Discovery]] to find an IdP's signing keys (JWK). ### WebID Issuer Discovery via Link Headers ### {#webid-issuer-discovery} @@ -445,7 +425,7 @@ All tokens, Client, and User credentials MUST only be transmitted over TLS. ## Client IDs ## {#security-client-ids} -An RS SHOULD assign a fixed set of low trust policies to any client identified as anonymous. +An AS SHOULD assign a fixed set of low trust policies to any client identified as anonymous. Implementors SHOULD expire ephemeral Client IDs that are kept in server storage to mitigate the potential for a bad actor to fill server storage with unexpired or otherwise useless Client IDs. @@ -464,15 +444,15 @@ among other factors, are what makes Client trust challenging. # Privacy Considerations # {#privacy} -## Access Token Reuse ## {#privacy-token-reuse} +## OIDC ID Token Reuse ## {#privacy-token-reuse} *This section is non-normative* -With JWTs being extendable by design, there is potential for a privacy breach if Access Tokens get -reused across multiple resource servers. It is not unimaginable that a custom claim is added to the -Access Token on instantiation. This addition may unintentionally give other resource servers -consuming the Access Token information about the user that they may not wish to share outside of the -intended RS. +With JWTs being extendable by design, there is potential for a privacy breach if OIDC ID Tokens get +reused across multiple authorization servers. It is not unimaginable that a custom claim is added to the +OIDC ID Token on instantiation. This addition may unintentionally give other authorization servers +consuming the OIDC ID Token information about the user that they may not wish to share outside of the +intended AS. # Acknowledgments # {#acknowledgments} From a5a966c7342da01a57bfb316e5533ea7d82fd245 Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 13 Dec 2021 15:07:09 -0600 Subject: [PATCH 07/14] initial primer update introducing AS --- .gitignore | 1 + primer/index.bs | 217 +++++++++++++++++++++----------------- primer/primer-login.mmd | 57 +++++----- primer/primer-request.mmd | 45 +++++--- 4 files changed, 176 insertions(+), 144 deletions(-) diff --git a/.gitignore b/.gitignore index 394b17a..edc7bac 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.html *.mmd.svg publish +node_modules diff --git a/primer/index.bs b/primer/index.bs index 7264a82..2404cd1 100644 --- a/primer/index.bs +++ b/primer/index.bs @@ -505,61 +505,16 @@ Our JWK thumbprint looks more like: 9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8 ``` -

18. Generates access token

- -The OP generates an access token. To do so it constructs a JSON Web Token and signs it using -its own keys. The access token could look like (remember you can decode it using -[https://jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwiYXVkIjoic29saWQiLCJjbmYiOnsiamt0IjoiOVhtd0s4bVEzSDUtUG56QXQzbEZIeldCV192NVFoWXluZXpiYml0NGtDOCJ9LCJjbGllbnRfaWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJqdGkiOiJmZDBhMTM1My0yNWYzLTRjN2UtOGY4Yi1jMTQyNjRmMWMxMmUiLCJpYXQiOjE2MDMzODUyNjEsImV4cCI6MTYwMzM4NTg2MX0.HIIYiovILPvsdkD3s3xomR1MkA_Ir8Mx_C-eHxbWEnw9Z2rv2rOWbvMPZ--BqH3qVSupgYTZZsTDbZXA8giCLA)): - -```text -eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwiYXVkIjoic29saWQiLCJjbmYiOnsiamt0IjoiOVhtd0s4bVEzSDUtUG56QXQzbEZIeldCV192NVFoWXluZXpiYml0NGtDOCJ9LCJjbGllbnRfaWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJqdGkiOiJmZDBhMTM1My0yNWYzLTRjN2UtOGY4Yi1jMTQyNjRmMWMxMmUiLCJpYXQiOjE2MDMzODUyNjEsImV4cCI6MTYwMzM4NTg2MX0.HIIYiovILPvsdkD3s3xomR1MkA_Ir8Mx_C-eHxbWEnw9Z2rv2rOWbvMPZ--BqH3qVSupgYTZZsTDbZXA8giCLA -``` - -Token Header: -```json -{ - "alg": "ES256", - "typ": "JWT" -} -``` - - - `"alg": "ES256"`: indicates the token was signed using eliptic curve - - `"typ": "JWT"`: indicates that this is a JSON web token - -Token Body: -```json -{ - "webid": "https://alice.coolpod.example/profile/card#me", - "iss": "https://secureauth.example", - "aud": "solid", - "cnf": { - "jkt": "9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8" - }, - "client_id": "https://decentphotos.example/webid#this", - "jti": "fd0a1353-25f3-4c7e-8f8b-c14264f1c12e", - "iat": 1603385261, - "exp": 1603385861 -} -``` - -- `"webid": "https://alice.coolpod.example/profile/card#me"`: The WebId of the user that - logged in -- `"iss": "https://secureauth.example"`: The OP that was used to generate this token -- `"aud": "solid"`: The token's audience. Because DPoP handles scoping the audience in all - practical senses, the audience for a solid token is simply "solid." -- `"cnf": { "jkt": "9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8" }`: The jwk thrumbprint must - be embedded in an object on the field "jkt" -- `"client_id": "https://decentphotos.example/webid#this"`: The client id of the application -- `"iat": 1603370641`: The date the token was issued, in this case October 22, 2020 8:44:01 -- `"exp": 1603371241`: The token's expiration date, in this case October 22, 2020 8:54:01 -

19. Generates the id_token

-If `openid` was listed as a scope during the authorization request, the OP generates an id -token. The id token is not used to access anything. It is simply to communicate information to -the client. This token is a [JSON Web Token](https://jwt.io/introduction/). It would look like +Since `openid` was listed as a scope during the authorization request, the OP generates an id +token. The id token will be pushed as claim to any Solid Authorization Server needed, which clients want to get Access Token from. +This token is a [JSON Web Token](https://jwt.io/introduction/). It would look like the following (you can decrypt the token with [https://jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg)): + +Issue: encoded token needs to be updated + ```text eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg ``` @@ -579,19 +534,26 @@ Token Body: ```json { "sub": "https://alice.coolpod.example/profile/card#me", - "aud": "https://decentphotos.example/webid#this", + "aud": [ "solid", "https://decentphotos.example/webid#this"], + "azp": "https://decentphotos.example/webid#this" "webid": "https://alice.coolpod.example/profile/card#me", "iss": "https://secureauth.example", "jti": "844a095c-9cdb-47e5-9510-1dba987c0a5f", "iat": 1603370641, - "exp": 1603371241 + "exp": 1603371241, + "cnf": { + "jkt": "9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8" + }, } ``` - `"sub": "https://alice.coolpod.example/profile/card#me"`: The subject claim. It must be the same as the authenticated user's WebID. - `"aud": "https://decentphotos.example/webid#this"`: The token's audience. Because an - id_token is intended for the client, its audience is the client id. + id_token is intended for the client and any Solid Authorization Server, + its audience is the client id and the string "solid". +- `"azp": "https://decentphotos.example/webid#this"`: The token's authorized party. Because an + id_token is intended to be used by the client, its authorized party is the client id. - `"webid": "https://alice.coolpod.example/profile/card#me"`: The WebID of the user that logged in - `"iss": "https://secureauth.example"`: The OP that was used to generate this token @@ -599,6 +561,8 @@ Token Body: for this token that can be used to prevent replay attacks. - `"iat": 1603370641`: The date the token was issued, in this case October 22, 2020 8:44:01 - `"exp": 1603371241`: The token's expiration date, in this case October 22, 2020 8:54:01 +- `"cnf": { "jkt": "9XmwK8mQ3H5-PnzAt3lFHzWBW_v5QhYynezbbit4kC8" }`: The jwk thrumbprint must + be embedded in an object on the field "jkt"

20. Generates refresh token

@@ -636,20 +600,12 @@ a response with the tokens in the body: Response (content-type: application/json) ```json { - "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwiYXVkIjoic29saWQiLCJjbmYiOnsiamt0IjoiOVhtd0s4bVEzSDUtUG56QXQzbEZIeldCV192NVFoWXluZXpiYml0NGtDOCJ9LCJjbGllbnRfaWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJqdGkiOiJmZDBhMTM1My0yNWYzLTRjN2UtOGY4Yi1jMTQyNjRmMWMxMmUiLCJpYXQiOjE2MDMzODUyNjEsImV4cCI6MTYwMzM4NTg2MX0.HIIYiovILPvsdkD3s3xomR1MkA_Ir8Mx_C-eHxbWEnw9Z2rv2rOWbvMPZ--BqH3qVSupgYTZZsTDbZXA8giCLA", - "expires_in": 300, - "token_type": "DPoP", "id_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg", "refresh_token": "eyJhbGciOiJub25lIn0.eyJqdGkiOiJhNzhiNDllZi03MWM1LTQ5ODUtYTUwYy01ZWYzYWVmMGZkOGYifQ.", "scope": "openid webid offline_access" } ``` -- `access_token": "eyJhbGciOiJ..."`: The access token we generated. The client will use this - to authenticate with the server. -- `"expires_in": 300`: Tells the client that the access token will expire in 300 seconds (5 - minutes) -- `"token_type": "DPoP"`: Tells the client that the token type is DPoP - `"id_token": "eyJhbGciOiJFU..."`: The id token we generated. The client will use this to extract information like the user's WebId. - `"refresh_token": "eyJhbGciOiJ..."`: The refresh token. The client will use this to get a @@ -660,16 +616,53 @@ Response (content-type: application/json) -

1. An AJAX request is initiated

In this example, Alice has logged into `https://decentphotos.example` and has completed the authentication steps above. She wants to make a request to Bob's Pod to get a photo album information at `https://bob.otherpod.example/private/photo_album.ttl`. Bob has previously granted access to Alice but has not granted access to anyone else. +

1. Discover Authorization Server

+ +Client can discover Authorization Server by making request to the resource + +Request: +```http +GET https://bob.otherpod.example/private/photo_album.ttl +``` + +Response: +```http +HTTP/1.1 401 Unauthorized +WWW-Authenticate: UMA realm="example", + as_uri="https://auth.otherpod.example", + ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de" +``` + +

1. Discover Token Endpoint

+ +Knowing Authorization Server now client can discover its Token Endpoint ([[!RFC8414]]) + +Request: +```http +GET https://auth.otherpod.example/.well-known/uma2-configuration +``` + +Response: +```json +{ + "issuer": "https://auth.otherpod.example", + "token_endpoint": "https://auth.otherpod.example/token", + "grant_types_supported": [ + "urn:ietf:params:oauth:grant-type:uma-ticket" + ] + ... +} +``` +

2. Creates a DPoP header token

-Before we send a request, we need to generate a DPoP header token. A new DPoP token must be +Before we request access token, we need to generate a DPoP header token. A new DPoP token must be generated every time a request is made. Generating a DPoP token is done the same way we did it in the authentication section. It must be signed by the same keypair that we generated in the authentication section. Our token @@ -702,68 +695,77 @@ Token Header: Token Body: ```json { - "htu": "https://bob.otherpod.example/private/photo_album.ttl", - "htm": "GET", + "htu": "https://auth.otherpod.example/token", + "htm": "POST", "jti": "fb1264dd-fff1-4509-a7b1-0fe58d08d3e1", "iat": 1603389616 } ``` -- `"htu": "https://bob.otherpod.example/private/photo_album.ttl"`: htu limits the token for +- `"htu": "https://auth.otherpod.example/token"`: htu limits the token for use only on the given url. -- `"htm": "GET"`: htm limits the token for use only on a specific http method, in this case +- `"htm": "POST"`: htm limits the token for use only on a specific http method, in this case `POST`. - `"jti": "fb1264dd-fff1-4509-a7b1-0fe58d08d3e1"`: jti is a unique identifier for the DPoP token that can optionally be used by the server to defend against replay attacks - `"iat": 1603389616`: The date the token was issued, in this case October 22, 2020 14:00:16 GMT. -

3. Sends request

-With the access token and new DPoP token in hand, we're ready to make our request. +

4. Includes ID Token in Solid Claim Token

-```http -GET https://bob.otherpod.example/private/photo_album.ttl -Headers: { - authorization: "DPoP eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwiYXVkIjoic29saWQiLCJjbmYiOnsiamt0IjoiOVhtd0s4bVEzSDUtUG56QXQzbEZIeldCV192NVFoWXluZXpiYml0NGtDOCJ9LCJjbGllbnRfaWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJqdGkiOiJmZDBhMTM1My0yNWYzLTRjN2UtOGY4Yi1jMTQyNjRmMWMxMmUiLCJpYXQiOjE2MDMzODUyNjEsImV4cCI6MTYwMzM4NTg2MX0.HIIYiovILPvsdkD3s3xomR1MkA_Ir8Mx_C-eHxbWEnw9Z2rv2rOWbvMPZ--BqH3qVSupgYTZZsTDbZXA8giCLA", - dpop: "eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwia2lkIjoiQ21HVE9Dd3ZKWXhrb0dGOGNxcFpBNTdab2xVdThBcFJQb3MwVlduWk1TNCIsInVzZSI6InNpZyIsImFsZyI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiU0FZcmF5VUh4Z1FPQ29YSC1MbHdyOW1iSWJpUHBsLXRQRUpLeE1WWFltcyIsInkiOiJ6eGJQODdPQ3JpeEZpMk9vZjU1QkhsTC1ySHhvWHVuUmttNFBkV3duUzJnIn19.eyJodHUiOiJodHRwczovL2JvYi5vdGhlcnBvZC5leGFtcGxlL3ByaXZhdGUvcGhvdG9fYWxidW0udHRsIiwiaHRtIjoiZ2V0IiwianRpIjoiZmIxMjY0ZGQtZmZmMS00NTA5LWE3YjEtMGZlNThkMDhkM2UxIiwiaWF0IjoxNjAzMzg5NjE2LCJleHAiOjE2MDMzOTMyMTZ9.G8JktoMOadenCYtO4Z_ZI7ACnjKJvT59OyKlQ6WpB1Qq2GoCK6v1ocrpsfELDOKIL5nt5fwWccfvCAA2bMrkjA" +ID token received from OP MUST be included in Claim Token which we will use in next step. + +```json +{ + "id_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg" } ``` -- `headers.authorization: "DPoP eyJhbGciOiJFUzI1N..."`: The authorization header contains the - ACCESS TOKEN that we received from the OP during the auth phase. Putting "DPoP" followed by a - space tells the Resource Server that this is a DPoP bound access token. -- `dpop: "eyJhbGciOi..."`: The DPoP token we just generated. -

4. Checks Access Token expirations

+

4. Request Access Token

+ +Request: +```http +POST /token HTTP/1.1 +Host: auth.otherpod.example +DPoP: eyJhbGciOi... +... +grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Auma-ticket +&ticket=016f84e8-f9b9-11e0-bd6f-0021cc6004de +&claim_token=eyj0... +&claim_token_format=http%3A%2F%2Fwww.w3.org%2Fns%2Fsolid%2Fterms%23ClaimToken +``` + +

4. Checks ID Token expirations

-The RS checks if the access token is still valid by looking at the `exp` claim. If the token -does not have an `exp` claim or the token is expired, the RS must reject the request with a 403. +The AS checks if the id token is still valid by looking at the `exp` claim. If the token +does not have an `exp` claim or the token is expired, the AS must reject the request with a 403.

5. Checks the DPoP token url and method

-The RS checks the `htu` and `htm` claims of the DPoP token. If the `htu` does not match the +The AS checks the `htu` and `htm` claims of the DPoP token. If the `htu` does not match the protocol, origin and path of the request or the `htm` does not correspond the the http method -of the request, the RS must reject the request with a 403. +of the request, the AS must reject the request with a 403.

5.1. (Optional) Checks DPoP token unique identifier

-The RS can optionally keep track of all DPoP `jti` claims it received. Because a new DPoP token +The AS can optionally keep track of all DPoP `jti` claims it received. Because a new DPoP token must be generated each time a request is made, no two tokens should have the same `jti`. If the -RS receives a DPoP token with a `jti` it has already encountered it may reject the request with +AS receives a DPoP token with a `jti` it has already encountered it may reject the request with a 403.

6. Checks DPoP signature against Access Token

-The RS first confirms that the DPoP token was signed by the public key listed in its `header -jwk`. If it was not, the RS must reject the request with a 403. +The AS first confirms that the DPoP token was signed by the public key listed in its `header +jwk`. If it was not, the AS must reject the request with a 403. -The RS checks if the public key in the DPoP token's `header.jwk` corresponds to the jwk +The AS checks if the public key in the DPoP token's `header.jwk` corresponds to the jwk thumbprint in the access token in the `cnf` field. If they do not, the OP must reject the request with a 403.

7. Retrieves Profile

Using the `webid` claim in the access token (for us, it's -`https://alice.coolpod.example/profile/card#me`), the RS retreives the user's WebId document. +`https://alice.coolpod.example/profile/card#me`), the AS retreives the user's WebId document. Request ```http @@ -790,12 +792,12 @@ Response

8. Checks issuer

-The RS checks that the `iss` claim in the access token matches the issuer listed in the user's -WebID. If it does not, the RS must reject the request with a 403. +The AS checks that the `iss` claim in the access token matches the issuer listed in the user's +WebID. If it does not, the AS must reject the request with a 403.

9. Retrieves OP configuration

-The RS uses the `iss` claim to get the issuer's configuration. The url is the issuer claim with +The AS uses the `iss` claim to get the issuer's configuration. The url is the issuer claim with `/.well-known/openid-configuration` appended to the end. Request @@ -859,7 +861,7 @@ Response

10. Requests JWKS

-Using the `jwks_uri` field in the openid-configuration, the RS makes a request to retreive the +Using the `jwks_uri` field in the openid-configuration, the AS makes a request to retreive the OP's public keys. Request @@ -888,15 +890,34 @@ Notice that the `keys` field is an array, so an OP could have multiple public ke

11. Checks access token signature validity

-Using the `kid` value in the access token, the RS searches the OP keys for one that matches the -access token. Checks to see if the access token was signed by that key. If it was not, the RS +Using the `kid` value in the access token, the AS searches the OP keys for one that matches the +access token. Checks to see if the access token was signed by that key. If it was not, the AS must reject the request with a 403.

12. Performs Authorization

-Now that the RS has performed all of its checks, we can trust that the agent in the `webid` +Now that the AS has performed all of its checks, we can trust that the agent in the `webid` claim (`https://alice.coolpod.example/profile/card#me`) is the same agent on whose behalf the -request was made. Using that information, the RS performs authorization. +request was made. Using that information, the AS performs authorization and returns access token. + +Response: +```json +{ + "access_token":"sbjsbhs(/SSJHBSUSSJHVhjsgvhsgvshgsv", + "token_type":"Bearer" +} +``` + +

3. Sends request

+ +With the access token, we're ready to make our request. + +```http +GET https://bob.otherpod.example/private/photo_album.ttl +Headers: { + authorization: "Bearer sbjsbhs(/SSJHBSUSSJHVhjsgvhsgvshgsv", +} +```

13. Returns Result

diff --git a/primer/primer-login.mmd b/primer/primer-login.mmd index 4edf33b..6bca3cb 100644 --- a/primer/primer-login.mmd +++ b/primer/primer-login.mmd @@ -1,30 +1,29 @@ sequenceDiagram - participant RP - participant Alice's OP - participant Alice's WebID - participant RP's WebID - note over RP: 1. Alice uses the decentphotos app - note over RP: 2. Alice selects her OP or WebID - RP->>Alice's WebID: 2.1 Retrieve Profile - Alice's WebID->>RP: Alice's WebID Profile - RP->>Alice's OP: 3. Retrieves OP configuration - Alice's OP->>RP: OP configuration - note over RP: 4. Generates PKCE code and code verifier - note over RP: 5. Saves code verifier to session storage - RP->>Alice's OP: 6. Authorization Request - Alice's OP->>RP's WebID: 7. Fetch RP WebID - RP's WebID->>Alice's OP: RP's WebID Profile - note over Alice's OP: 8. Validates redirect url with WebID - note over Alice's OP: 9. Alice Logs In - note over Alice's OP: 10. Generates a code - Alice's OP->>RP: 11. Send code to redirect url - note over RP: 12. Generates DPoP Client Key Pair - note over RP: 13. Generates a DPoP Header - RP->>Alice's OP: 14. Token request with code and code verifier - note over Alice's OP: 15. Validates code verifier - note over Alice's OP: 16. Validates DPoP Token signature - note over Alice's OP: 17. Converts the DPoP public key to a JWK thumbprint - note over Alice's OP: 18. Generates access token - note over Alice's OP: 19. Generates id token - note over Alice's OP: 20. Generates refresh token - Alice's OP->>RP: 21. Send tokens + participant WebID as 👩 End-User's WebID Document + participant OP as 👩 OpenID Provider + participant ClientID as ⚙️ Client's ID Document + participant Client as ⚙️ Client + note over Client: 1. Alice uses the decentphotos app + note over Client: 2. Alice selects her OP or WebID + Client->>WebID: 2.1 Retrieve Profile + WebID->>Client: WebID Profile + Client->>OP: 3. Retrieves OP configuration + OP->>Client: OP configuration + note over Client: 4. Generates PKCE code and code verifier + note over Client: 5. Saves code verifier to session storage + Client->>OP: 6. Authorization Request + OP->>ClientID: 7. Fetch Client WebID + ClientID->>OP: ClientID Profile + note over OP: 8. Validates redirect url with WebID + note over OP: 9. Alice Logs In + note over OP: 10. Generates a code + OP->>Client: 11. Send code to redirect url + note over Client: 12. Generates DPoP Client Key Pair + note over Client: 13. Generates a DPoP Header + Client->>OP: 14. Token request with code and code verifier + note over OP: 15. Validates code verifier + note over OP: 16. Validates DPoP Token signature + note over OP: 17. Converts the DPoP public key to a JWK thumbprint + note over OP: 19. Generates id token + note over OP: 20. Generates refresh token + OP->>Client: 21. Send tokens diff --git a/primer/primer-request.mmd b/primer/primer-request.mmd index 1924bae..827b47d 100644 --- a/primer/primer-request.mmd +++ b/primer/primer-request.mmd @@ -1,18 +1,29 @@ sequenceDiagram - note over RP: 1. An AJAX request is initiated - note over RP: 2. Creates a DPoP header token - RP->>Bob's Pod (RS): 3. Sends Request - note over Bob's Pod (RS): 4. Checks Access token expirations - note over Bob's Pod (RS): 5. Checks DPoP Token url and method - note over Bob's Pod (RS): 5.1. (Optional) Checks DPoP token unique identifier - note over Bob's Pod (RS): 6. Checks DPoP signature against Access Token - Bob's Pod (RS)->>Alice's Pod (RS): 7. Retrieves Profile - Alice's Pod (RS)->>Bob's Pod (RS): Profile - note over Bob's Pod (RS): 8. Checks Issuer - Bob's Pod (RS)->>Alice's OP: 9. Retrieves OP configuration - Alice's OP->>Bob's Pod (RS): OP configuration - Bob's Pod (RS)->>Alice's OP: 10. Requests JWKS - Alice's OP->>Bob's Pod (RS): JWKS - note over Bob's Pod (RS): 11. Checks access token signature validity - note over Bob's Pod (RS): 12. Performs Authorization - Bob's Pod (RS)->>RP: 13. Returns Result \ No newline at end of file + participant WebID as 👩 End-User's WebID Document + participant OP as 👩 OpenID Provider + participant Client as ⚙️ Client + participant AS as ☁️ Authorization Server + participant RS as ☁️ Resource Server + note over Client: 1. An AJAX request is initiated + note over Client: 2. Creates a DPoP header token + Client->>RS: 3. Sends Request + RS->>Client: 4. Responds 401 with as_uri + Client->>AS: Requests AS configuration + AS-->>Client: AS configuration + Client->>AS: Request Access Token + note over AS: 4. Checks Access token expirations + note over AS: 5. Checks DPoP Token url and method + note over AS: 5.1. (Optional) Checks DPoP token unique identifier + note over AS: 6. Checks DPoP signature against Access Token + AS->>WebID: 7. Retrieves Profile + WebID->>AS: Profile + note over AS: 8. Checks Issuer + AS->>OP: 9. Retrieves OP configuration + OP->>AS: OP configuration + AS->>OP: 10. Requests JWKS + OP->>AS: JWKS + note over AS: 11. Checks access token signature validity + AS->>Client: Access Token Response + Client->>RS: 3. Sends Request with Access Token + note over AS, RS: Have pre-established usage of Access Tokens + RS->>Client: 13. Returns Result From 8a10b6542f028024513552a4695f363976049c5e Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 20 Dec 2021 08:26:12 -0600 Subject: [PATCH 08/14] added UMA AS details to the spec --- index.bs | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index c7622f6..2045fe3 100644 --- a/index.bs +++ b/index.bs @@ -165,9 +165,6 @@ provided at `https://www.w3.org/ns/solid/oidc-context.jsonld` such that the resu document produces a JSON serialization of an OIDC client registration, per the definition of client registration metadata from [[!RFC7591]] section 2. -Issue: [Related Issue](https://github.com/solid/authentication-panel/issues/75) -Solid-OIDC requirements for client description document - Also, the IdP MUST dereference the Client ID Document and match any Client-supplied parameters with the values in the Client ID Document. @@ -346,13 +343,40 @@ With the `webid` scope, the DPoP-bound OIDC ID Token payload MUST contain these "exp": 1311281970, "cnf":{ "jkt":"0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" - }, + } } # Resource Access # {#resource} +## Solid Claim Token ## {#solid-claim-token} + +Issue: Solid Claim Token (JWT) should have common definition outside of this spec + +Client MUST embed obtained [[#tokens-id]] in Solid Claim Token as value of `id_token` claim. + +## Authorization Server Discovery ## {#authorization-server-discovery} + +When client performs unauthenticated request to protected resource, +Resource Server MUST respond with HTTP 401 status code, +as well as WWW-Authenticate HTTP header including UMA +as authentication scheme, with the issuer URI from the +authorization server's discovery document in as_uri parameter +and permission ticket in ticket parameter.[[!UMA]] + +Given as_uri client can discover Authorization Server token endponint via +Authorization Server Metadata available at ./well-known/uma-configuration. +As specified in [[!UMA]] + + +## Obtaining Access Token ## {#obtaining-access-token} + +Client can obtain Access Token from Authorization Server token endpoint using +urn:ietf:params:oauth:grant-type:uma-ticket grant [[!UMA]]. +When using this grant Client MUST use [[#solid-claim-token]] as claim_token +and http://www.w3.org/ns/solid/terms#ClaimToken as claim_token_format. + ## DPoP Proof Validation ## {#resource-dpop-validation} A DPoP Proof that is valid according to @@ -546,6 +570,16 @@ Verborgh, Ricky White, Paul Worrall, Dmitri Zagidulin. "href": "https://www.w3.org/2005/Incubator/webid/spec/identity/", "title": "WebID 1.0", "publisher": "WebID Incubator Group" + }, + "UMA": { + "authors": [ + "Eve Maler", + "Maciej Machulak", + "Justin Richer" + ], + "href": "https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html", + "title": "User-Managed Access (UMA) 2.0 Grant for OAuth 2.0 Authorization", + "publisher": "Kantara Initiative, Inc" } } From c01a6ea937ab356b276bc94b3d0b88bb3a5f0d7f Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Mon, 3 Jan 2022 08:05:50 -0600 Subject: [PATCH 09/14] incorporated feedback from review --- index.bs | 14 +++++--------- primer/index.bs | 20 ++++++-------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/index.bs b/index.bs index 2045fe3..cc6aa41 100644 --- a/index.bs +++ b/index.bs @@ -350,12 +350,6 @@ With the `webid` scope, the DPoP-bound OIDC ID Token payload MUST contain these # Resource Access # {#resource} -## Solid Claim Token ## {#solid-claim-token} - -Issue: Solid Claim Token (JWT) should have common definition outside of this spec - -Client MUST embed obtained [[#tokens-id]] in Solid Claim Token as value of `id_token` claim. - ## Authorization Server Discovery ## {#authorization-server-discovery} When client performs unauthenticated request to protected resource, @@ -366,7 +360,7 @@ authorization server's discovery document in as_uri parameter and permission ticket in ticket parameter.[[!UMA]] Given as_uri client can discover Authorization Server token endponint via -Authorization Server Metadata available at ./well-known/uma-configuration. +Authorization Server Metadata available at ./well-known/uma2-configuration. As specified in [[!UMA]] @@ -374,8 +368,10 @@ As specified in [[!UMA]] Client can obtain Access Token from Authorization Server token endpoint using urn:ietf:params:oauth:grant-type:uma-ticket grant [[!UMA]]. -When using this grant Client MUST use [[#solid-claim-token]] as claim_token -and http://www.w3.org/ns/solid/terms#ClaimToken as claim_token_format. +When using this grant Client MUST use [[#tokens-id]] as claim_token +and http://openid.net/specs/openid-connect-core-1_0.html#IDToken as claim_token_format. + +Note: Client can push additional claims by requesting upgraded RPT [[!UMA]] ## DPoP Proof Validation ## {#resource-dpop-validation} diff --git a/primer/index.bs b/primer/index.bs index 2404cd1..69d000e 100644 --- a/primer/index.bs +++ b/primer/index.bs @@ -601,8 +601,7 @@ Response (content-type: application/json) ```json { "id_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg", - "refresh_token": "eyJhbGciOiJub25lIn0.eyJqdGkiOiJhNzhiNDllZi03MWM1LTQ5ODUtYTUwYy01ZWYzYWVmMGZkOGYifQ.", - "scope": "openid webid offline_access" + "refresh_token": "eyJhbGciOiJub25lIn0.eyJqdGkiOiJhNzhiNDllZi03MWM1LTQ5ODUtYTUwYy01ZWYzYWVmMGZkOGYifQ." } ``` @@ -610,7 +609,6 @@ Response (content-type: application/json) extract information like the user's WebId. - `"refresh_token": "eyJhbGciOiJ..."`: The refresh token. The client will use this to get a new access token when its current one expires. -- `"scope": "openid webid offline_access"`: The scopes that were used. ## Request Flow ## {#request-flow} @@ -711,16 +709,6 @@ Token Body: GMT. -

4. Includes ID Token in Solid Claim Token

- -ID token received from OP MUST be included in Claim Token which we will use in next step. - -```json -{ - "id_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL2FsaWNlLmNvb2xwb2QuZXhhbXBsZS9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2RlY2VudHBob3Rvcy5leGFtcGxlL3dlYmlkI3RoaXMiLCJ3ZWJpZCI6Imh0dHBzOi8vYWxpY2UuY29vbHBvZC5leGFtcGxlL3Byb2ZpbGUvY2FyZCNtZSIsImlzcyI6Imh0dHBzOi8vc2VjdXJlYXV0aC5leGFtcGxlIiwianRpIjoiODQ0YTA5NWMtOWNkYi00N2U1LTk1MTAtMWRiYTk4N2MwYTVmIiwiaWF0IjoxNjAzMzg2NDQ4LCJleHAiOjE2MDMzODcwNDh9.T306vT8dmn9gQIMEdG92AM4WRnrhqWZTfDpovwqZ6Zn0mK9yxj0iOVGqXD4CW8-tzDTitNwEGorAo85atL0Oeg" -} -``` -

4. Request Access Token

Request: @@ -732,7 +720,7 @@ DPoP: eyJhbGciOi... grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Auma-ticket &ticket=016f84e8-f9b9-11e0-bd6f-0021cc6004de &claim_token=eyj0... -&claim_token_format=http%3A%2F%2Fwww.w3.org%2Fns%2Fsolid%2Fterms%23ClaimToken +&claim_token_format=http%3A%2F%2Fopenid.net%2Fspecs%2Fopenid-connect-core-1_0.html%23IDToken ```

4. Checks ID Token expirations

@@ -910,6 +898,10 @@ Response:

3. Sends request

+Note: In practice client would need to upgrade RPT by pushing +authorization related claim. This primer only focuses on authentication so we omit +this detail here. + With the access token, we're ready to make our request. ```http From 963a7f9929442c3b7d25adbfd96a7df7bf96057d Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Fri, 7 Jan 2022 07:26:03 -0600 Subject: [PATCH 10/14] Apply suggestions from code review Co-authored-by: Zwifi --- index.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index 2b361d4..240c81a 100644 --- a/index.bs +++ b/index.bs @@ -314,12 +314,12 @@ With the `webid` scope, the DPoP-bound OIDC ID Token payload MUST contain these * `webid` — The WebID claim MUST be the user's WebID. * `iss` — The issuer claim MUST be a valid URL of the IdP instantiating this token. - * `aud` — The audience claim MUST an array of values, + * `aud` — The audience claim MUST be an array of values, one of which is the ClientID claim is used to identify the client. (See also: [section 5. Client Identifiers](#clientids)). another one is the string `solid`. In the decentralized world - of Solid OIDC, the audience of ID TOken is not only the client, + of Solid OIDC, the audience of an ID Token is not only the client, but rather the Solid Authorization Server; that is, any Solid Authorization Server at any accessible address on the world wide web. See also: [[RFC7519#section-4.1.3]]. @@ -355,14 +355,14 @@ With the `webid` scope, the DPoP-bound OIDC ID Token payload MUST contain these ## Authorization Server Discovery ## {#authorization-server-discovery} -When client performs unauthenticated request to protected resource, +When Client performs an unauthenticated request to a protected resource, Resource Server MUST respond with HTTP 401 status code, as well as WWW-Authenticate HTTP header including UMA as authentication scheme, with the issuer URI from the authorization server's discovery document in as_uri parameter and permission ticket in ticket parameter.[[!UMA]] -Given as_uri client can discover Authorization Server token endponint via +Given as_uri client can discover Authorization Server token endpoint via Authorization Server Metadata available at ./well-known/uma2-configuration. As specified in [[!UMA]] From 7872a59cad36129338014d65d451f09021c3addc Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Fri, 7 Jan 2022 07:27:46 -0600 Subject: [PATCH 11/14] Apply suggestions from code review Co-authored-by: Aaron Coburn --- index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.bs b/index.bs index 240c81a..8c410c5 100644 --- a/index.bs +++ b/index.bs @@ -369,12 +369,12 @@ As specified in [[!UMA]] ## Obtaining Access Token ## {#obtaining-access-token} -Client can obtain Access Token from Authorization Server token endpoint using +Clients can obtain an Access Token from the Authorization Server's token endpoint using urn:ietf:params:oauth:grant-type:uma-ticket grant [[!UMA]]. When using this grant Client MUST use [[#tokens-id]] as claim_token and http://openid.net/specs/openid-connect-core-1_0.html#IDToken as claim_token_format. -Note: Client can push additional claims by requesting upgraded RPT [[!UMA]] +Note: Client can push additional claims by requesting an upgraded RPT [[!UMA]] ## DPoP Proof Validation ## {#resource-dpop-validation} From b838aeee2ed6161f7c15cb3823bea73ab730f71b Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Sun, 9 Jan 2022 13:48:12 -0600 Subject: [PATCH 12/14] adjust sequence numbering in the primer --- index.bs | 4 ++-- primer/index.bs | 28 ++++++++++++++-------------- primer/primer-request.mmd | 37 ++++++++++++++++++------------------- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/index.bs b/index.bs index 8c410c5..391e450 100644 --- a/index.bs +++ b/index.bs @@ -177,7 +177,7 @@ list. This example uses [JSON-LD ](https://www.w3.org/TR/json-ld11/) for the Client ID Document:
-

https://app.example/id +

https://app.example/id

         {
@@ -404,7 +404,7 @@ A server hosting a WebID document MAY transmit the `http://www.w3.org/ns/solid/t
 
 
-        Link: ; rel="http://www.w3.org/ns/solid/terms#oidcIssuer"; anchor="#id"
+        Link: <https://oidc.example>; rel="http://www.w3.org/ns/solid/terms#oidcIssuer"; anchor="#id"
     
diff --git a/primer/index.bs b/primer/index.bs index 69d000e..f17494a 100644 --- a/primer/index.bs +++ b/primer/index.bs @@ -637,7 +637,7 @@ WWW-Authenticate: UMA realm="example", ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de" ``` -

1. Discover Token Endpoint

+

2. Request AS configuration

Knowing Authorization Server now client can discover its Token Endpoint ([[!RFC8414]]) @@ -658,7 +658,7 @@ Response: } ``` -

2. Creates a DPoP header token

+

3. Creates a DPoP header token

Before we request access token, we need to generate a DPoP header token. A new DPoP token must be generated every time a request is made. @@ -723,25 +723,25 @@ grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Auma-ticket &claim_token_format=http%3A%2F%2Fopenid.net%2Fspecs%2Fopenid-connect-core-1_0.html%23IDToken ``` -

4. Checks ID Token expirations

+

5. Checks ID Token expirations

The AS checks if the id token is still valid by looking at the `exp` claim. If the token does not have an `exp` claim or the token is expired, the AS must reject the request with a 403. -

5. Checks the DPoP token url and method

+

6. Checks the DPoP token url and method

The AS checks the `htu` and `htm` claims of the DPoP token. If the `htu` does not match the protocol, origin and path of the request or the `htm` does not correspond the the http method of the request, the AS must reject the request with a 403. -

5.1. (Optional) Checks DPoP token unique identifier

+

6.1. (Optional) Checks DPoP token unique identifier

The AS can optionally keep track of all DPoP `jti` claims it received. Because a new DPoP token must be generated each time a request is made, no two tokens should have the same `jti`. If the AS receives a DPoP token with a `jti` it has already encountered it may reject the request with a 403. -

6. Checks DPoP signature against Access Token

+

7. Checks DPoP signature against Access Token

The AS first confirms that the DPoP token was signed by the public key listed in its `header jwk`. If it was not, the AS must reject the request with a 403. @@ -750,7 +750,7 @@ The AS checks if the public key in the DPoP token's `header.jwk` corresponds to thumbprint in the access token in the `cnf` field. If they do not, the OP must reject the request with a 403. -

7. Retrieves Profile

+

8. Retrieves WebID Document

Using the `webid` claim in the access token (for us, it's `https://alice.coolpod.example/profile/card#me`), the AS retreives the user's WebId document. @@ -778,12 +778,12 @@ Response solid:oidcIssuer ; ``` -

8. Checks issuer

+

9. Checks issuer

The AS checks that the `iss` claim in the access token matches the issuer listed in the user's WebID. If it does not, the AS must reject the request with a 403. -

9. Retrieves OP configuration

+

10. Retrieves OP configuration

The AS uses the `iss` claim to get the issuer's configuration. The url is the issuer claim with `/.well-known/openid-configuration` appended to the end. @@ -847,7 +847,7 @@ Response } ``` -

10. Requests JWKS

+

11. Requests JWKS

Using the `jwks_uri` field in the openid-configuration, the AS makes a request to retreive the OP's public keys. @@ -876,13 +876,13 @@ Response (application/json) Notice that the `keys` field is an array, so an OP could have multiple public keys. -

11. Checks access token signature validity

+

12. Checks access token signature validity

Using the `kid` value in the access token, the AS searches the OP keys for one that matches the access token. Checks to see if the access token was signed by that key. If it was not, the AS must reject the request with a 403. -

12. Performs Authorization

+

13. Access Token Response

Now that the AS has performed all of its checks, we can trust that the agent in the `webid` claim (`https://alice.coolpod.example/profile/card#me`) is the same agent on whose behalf the @@ -896,7 +896,7 @@ Response: } ``` -

3. Sends request

+

14. Sends request with Access Token

Note: In practice client would need to upgrade RPT by pushing authorization related claim. This primer only focuses on authentication so we omit @@ -911,7 +911,7 @@ Headers: { } ``` -

13. Returns Result

+

15. Returns Result

Given all went well, the RS should return the requested content. diff --git a/primer/primer-request.mmd b/primer/primer-request.mmd index 827b47d..33ee34c 100644 --- a/primer/primer-request.mmd +++ b/primer/primer-request.mmd @@ -4,26 +4,25 @@ sequenceDiagram participant Client as ⚙️ Client participant AS as ☁️ Authorization Server participant RS as ☁️ Resource Server - note over Client: 1. An AJAX request is initiated - note over Client: 2. Creates a DPoP header token - Client->>RS: 3. Sends Request - RS->>Client: 4. Responds 401 with as_uri - Client->>AS: Requests AS configuration - AS-->>Client: AS configuration - Client->>AS: Request Access Token - note over AS: 4. Checks Access token expirations - note over AS: 5. Checks DPoP Token url and method - note over AS: 5.1. (Optional) Checks DPoP token unique identifier - note over AS: 6. Checks DPoP signature against Access Token - AS->>WebID: 7. Retrieves Profile + Client->>RS: 1. Discover Authorization Server + RS->>Client: Responds 401 with as_uri + Client->>AS: 2. Requests AS configuration + AS->>Client: AS configuration + note over Client: 3. Creates a DPoP header token + Client->>AS: 4. Request Access Token + note over AS: 5. Checks ID Token expirations + note over AS: 6. Checks DPoP Token url and method + note over AS: 6.1. (Optional) Checks DPoP token unique identifier + note over AS: 7. Checks DPoP signature against Access Token + AS->>WebID: 8. Retrieves WebID Document WebID->>AS: Profile - note over AS: 8. Checks Issuer - AS->>OP: 9. Retrieves OP configuration + note over AS: 9. Checks Issuer + AS->>OP: 10. Retrieves OP configuration OP->>AS: OP configuration - AS->>OP: 10. Requests JWKS + AS->>OP: 11. Requests JWKS OP->>AS: JWKS - note over AS: 11. Checks access token signature validity - AS->>Client: Access Token Response - Client->>RS: 3. Sends Request with Access Token + note over AS: 12. Checks access token signature validity + AS->>Client: 13. Access Token Response + Client->>RS: 14. Sends Request with Access Token note over AS, RS: Have pre-established usage of Access Tokens - RS->>Client: 13. Returns Result + RS->>Client: 15. Returns Result From 302f5608a0413180343aa8f812fac78ce495dc7e Mon Sep 17 00:00:00 2001 From: elf Pavlik Date: Sun, 9 Jan 2022 13:51:32 -0600 Subject: [PATCH 13/14] removed image of old basic flow diagram --- basic-flow-diagram.png | Bin 51772 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 basic-flow-diagram.png diff --git a/basic-flow-diagram.png b/basic-flow-diagram.png deleted file mode 100644 index df9d36c16a810b3759ba66b0c583be217af20ed9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51772 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 Date: Tue, 11 Jan 2022 15:15:45 -0500 Subject: [PATCH 14/14] Clarify UMA requirements * Set UMA requirement as SHOULD * Set OpenID profile as required for UMA servers * Set WWW-Authenticate (as_uri) as MUST unless a different discovery mechanism is used --- index.bs | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/index.bs b/index.bs index 391e450..56d585f 100644 --- a/index.bs +++ b/index.bs @@ -81,8 +81,9 @@ This specification uses the terms "access token", "authorization server", "resou "grant type", and "client" as defined by The OAuth 2.0 Authorization Framework [[!RFC6749]]. Throughout this specification, we will use the term Identity Provider (IdP) in line with the -terminology used in the Open ID Connect Core 1.0 specification (OIDC) [[!OIDC.Core]]. It should be noted that -The OAuth 2.0 Authorization Framework (OAuth) [[!RFC6749]] refers to this same entity as an Authorization Server. +terminology used in the Open ID Connect Core 1.0 specification (OIDC) [[!OIDC.Core]]. +It should be noted that this is distinct from the entity referred to as an Authorization Server +by the OAuth 2.0 Authorization Framework (OAuth) [[!RFC6749]]. This specification also uses the following terms: @@ -320,7 +321,7 @@ With the `webid` scope, the DPoP-bound OIDC ID Token payload MUST contain these another one is the string `solid`. In the decentralized world of Solid OIDC, the audience of an ID Token is not only the client, - but rather the Solid Authorization Server; + but also a Solid Authorization Server; that is, any Solid Authorization Server at any accessible address on the world wide web. See also: [[RFC7519#section-4.1.3]]. * `azp` - The ClientID claim is used to identify the client. @@ -355,26 +356,29 @@ With the `webid` scope, the DPoP-bound OIDC ID Token payload MUST contain these ## Authorization Server Discovery ## {#authorization-server-discovery} -When Client performs an unauthenticated request to a protected resource, -Resource Server MUST respond with HTTP 401 status code, -as well as WWW-Authenticate HTTP header including UMA -as authentication scheme, with the issuer URI from the -authorization server's discovery document in as_uri parameter -and permission ticket in ticket parameter.[[!UMA]] +When a Client performs an unauthenticated request to a protected resource, +the Resource Server MUST respond with the HTTP 401 status code, +and a WWW-Authenticate HTTP header. See also: [[RFC7235#section-4.1]] -Given as_uri client can discover Authorization Server token endpoint via -Authorization Server Metadata available at ./well-known/uma2-configuration. -As specified in [[!UMA]] +The WWW-Authenticate HTTP header MUST include an as_uri +parameter unless the authentication scheme requires a different mechanism +for discovering an associated authorization server. +Authorization Servers SHOULD implement User-Managed Access (UMA) 2.0 Grant for +OAuth 2.0 Authorization [[!UMA]]. -## Obtaining Access Token ## {#obtaining-access-token} +## Obtaining an Access Token ## {#obtaining-access-token} -Clients can obtain an Access Token from the Authorization Server's token endpoint using -urn:ietf:params:oauth:grant-type:uma-ticket grant [[!UMA]]. -When using this grant Client MUST use [[#tokens-id]] as claim_token -and http://openid.net/specs/openid-connect-core-1_0.html#IDToken as claim_token_format. +For Authorization Servers that conform to [[!UMA]], the +http://openid.net/specs/openid-connect-core-1_0.html#IDToken profile MUST +be supported. This profile MUST be advertised in the uma_profiles_supported +metadata of the Authorization Server discovery document [[UMA#rfc.section.2]]. -Note: Client can push additional claims by requesting an upgraded RPT [[!UMA]] +When using the http://openid.net/specs/openid-connect-core-1_0.html#IDToken +profile with an UMA-based Authorization Server, the Authorization Server MUST be capable +of exchanging a valid Solid-OIDC ID Token [[#tokens-id]] for an OAuth 2.0 Access Token. + +Note: Clients can push additional claims by requesting an upgraded RPT [[UMA#rfc.section.3.3.1]] ## DPoP Proof Validation ## {#resource-dpop-validation}