From c8b28c89d91050f6d0d5d701753cac46bdf334c3 Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Mon, 28 Oct 2024 17:27:25 -0400 Subject: [PATCH 1/7] Specify the fields API Bug: w3c-fedid/custom-requests#4 --- spec/index.bs | 75 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 9ee1e37f..74470793 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -653,6 +653,7 @@ dictionary IdentityProviderRequestOptions : IdentityProviderConfig { USVString nonce; DOMString loginHint; DOMString domainHint; + sequence fields; any params; }; @@ -872,7 +873,7 @@ the exception thrown. 1. For each |acc| in |accountsList|: 1. If |acc| is [=eligible for auto reauthentication=] given |provider|, and |globalObject|, set |registeredAccount| to |acc| and increase |numRegisteredAccounts| by 1. - 1. Let |permission|, |disclosureTextShown|, and |isAutoSelected| be set to false. + 1. Let |permission|, |permissionRequested|, and |isAutoSelected| be set to false. 1. If |mediation| is not "{{CredentialMediationRequirement/required}}", |requiresUserMediation| is false, and |numRegisteredAccounts| is equal to 1: 1. Set |account| to |registeredAccount| and |permission| to true. When doing this, the user @@ -888,23 +889,24 @@ the exception thrown. {{IdentityCredentialRequestOptions/context}} to customize the dialog. 1. Otherwise, let |permission| be the result of running [=request permission to sign-up=] algorithm with |account|, |config|, |provider|, and |globalObject|. Also set - |disclosureTextShown| to true. + |permissionRequested| to true. 1. Otherwise: 1. Set |account| to the result of running the [=select an account=] from the |accountsList|. 1. If |account| is failure, return (failure, true). 1. If [=compute the connection status=] of |account|, |provider| and |globalObject| is - [=compute the connection status/connected=], set |permission| to true. + [=compute the connection status/connected=], or if |provider|. + {{IdentityProviderRequestOptions/fields}} is [=list/empty=], set |permission| to true. 1. Otherwise: 1. Let |permission| be the result of running the [=request permission to sign-up=] algorithm with |account|, |config|, |provider|, and |globalObject|. - 1. Set |disclosureTextShown| to true. + 1. Set |permissionRequested| to true. 1. Wait until the [=user agent=]'s dialogs requesting for user choice or permission to be closed, if any are created in the previous steps. 1. Assert: |account| is not null. 1. If |permission| is false, then return (failure, true). 1. Let |credential| be the result of running the [=fetch an identity assertion=] algorithm with - |account|'s {{IdentityProviderAccount/id}}, |disclosureTextShown|, |isAutoSelected|, + |account|'s {{IdentityProviderAccount/id}}, |permissionRequested|, |isAutoSelected|, |provider|, |config|, and |globalObject|. 1. Return |credential|. @@ -1193,18 +1195,32 @@ the token that will be provided to the [=RP=].
To fetch an identity assertion given a {{USVString}} - |accountId|, a boolean |disclosureTextShown|, a boolean |isAutoSelected|, an + |accountId|, a boolean |permissionRequested|, a boolean |isAutoSelected|, an {{IdentityProviderRequestOptions}} |provider|, an {{IdentityProviderAPIConfig}} |config|, and |globalObject|, run the following steps. This returns an {{IdentityCredential}} or failure. 1. Let |tokenUrl| be the result of [=computing the manifest URL=] given |provider|, |config|["{{IdentityProviderAPIConfig/id_assertion_endpoint}}"], and |globalObject|. 1. If |tokenUrl| is failure, return failure. + 1. Let |disclosureShownFor| and |fields| be the empty list. + 1. If |permissionRequested| is true: + 1. Set |fields| to |provider|.{{IdentityProviderRequestOptions/fields}}. + 1. Set |disclosureShownFor| to the subset of strings in |fields| that are + in the [=list of recognized fields=]. 1. Let |list| be a list with the following entries: 1. ("client_id", |provider|'s {{IdentityProviderConfig/clientId}}) 1. ("nonce", |provider|'s {{IdentityProviderRequestOptions/nonce}}) 1. ("account_id", |accountId|) - 1. ("disclosure_text_shown", |disclosureTextShown|) 1. ("is_auto_selected", |isAutoSelected|) + 1. If |fields| is not empty: + 1. Let |serializedFields| be the entries of |fields| concatenated with a "," + between elements. + 1. Append ("fields", |serializedFields|) to |list|. + 1. If |disclosureShownFor| is not empty: + 1. Let |serializedDisclosure| be the entries of |disclosureShownFor| concatenated + with a "," between elements. + 1. Append ("disclosure_shown_for", |serializedDisclosure|) to |list|. + 1. If |disclosureShownFor| contains all of "name", "email" and "picture", append + ("disclosure_text_shown", true) to |list|. 1. If |provider|'s {{IdentityProviderRequestOptions/params}} is not empty: 1. Let |json| be the result of [=serializing a JavaScript value to a JSON string=] with |provider|'s {{IdentityProviderRequestOptions/params}}. @@ -1280,25 +1296,40 @@ The request permission to sign-up algorithm fetches the [=client metadata waits for the user to grant permission to use the given account, and returns whether the user granted permission or not. -
+
To request permission to sign-up the user with a given an {{IdentityProviderAccount}} |account|, an {{IdentityProviderAPIConfig}} |config|, an {{IdentityProviderRequestOptions}} |provider|, and a |globalObject|, run the following steps. This returns a boolean. 1. Assert: These steps are running [=in parallel=]. 1. Let |metadata| be the result of running [=fetch the client metadata=] with |config|, |provider|, and |globalObject|. + 1. Let |fields| be |provider|.{{IdentityProviderRequestOptions/fields}} or, if not specified, + `["name", "email", "picture"]`. + + Note: Unspecified is different from an explicitly specified empty list. 1. Prompt the user to gather explicit intent to create an account. The user agent MAY use the {{IdentityProviderBranding}} to inform the style choices of its UI. Additionally: - 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] - is defined and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of - |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display - the |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] link. - 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/terms_of_service_url}}"] - is defined, and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of - |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display - the |metadata|["{{IdentityProviderClientMetadata/terms_of_service_url}}"] link. - 1. The user agent MAY use the {{IdentityCredentialRequestOptions/context}} to customize the - dialog shown. + 1. If |provider|.{{IdentityProviderRequestOptions/fields}} is not [=list/empty=]: + 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] + is defined and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of + |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display + the |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] link. + 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/terms_of_service_url}}"] + is defined, and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of + |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display + the |metadata|["{{IdentityProviderClientMetadata/terms_of_service_url}}"] link. + 1. The user agent MUST prompt the user for permission to share the data in |fields|, + interpreting the strings as follows (this is the list of recognized fields): + : `"name"` + :: The user's name as given in {{IdentityProviderAccount}}.{{IdentityProviderAccount/name}}. + : `"email"` + :: The user's email address as given in {{IdentityProviderAccount}}.{{IdentityProviderAccount/email}}. + : `"picture"` + :: The user's profile picture as given in {{IdentityProviderAccount}}.{{IdentityProviderAccount/picture}}. + + Any other string is ignored for forwards compatibility. + 1. The user agent MAY use the {{IdentityCredentialRequestOptions/context}} to customize the + dialog shown. 1. If the user does not grant permission, return false. 1. [=Create a connection between the RP and the IdP account=] with |provider|, |account|, and |globalObject|. @@ -1923,6 +1954,12 @@ It will also contain the following parameters in the request body `application/x with rp.example"), used by the [=request permission to sign-up=] algorithm for new users. It is used as an assurance by the user agent to the [=IDP=] that it has indeed shown the terms of service and privacy policy to the user in the cases where it is required to do so. + : fields + :: The list of fields that the [=RP=] has requested in {{IdentityProviderRequestOptions/fields}}. + : disclosure_shown_for + :: The list of fields that the user was prompted for. This can be a subset of + {{IdentityProviderRequestOptions/fields}} if a field is requested that is not in the [=list + of recognized fields=]. For example: @@ -1935,7 +1972,7 @@ Origin: https://rp.example/ Content-Type: application/x-www-form-urlencoded Cookie: 0x23223 Sec-Fetch-Dest: webidentity -account_id=123&client_id=client1234&nonce=Ct60bD&disclosure_text_shown=true +account_id=123&client_id=client1234&nonce=Ct60bD&disclosure_text_shown=true&fields=name,email,picture&disclosure_shown_for=name,email,picture ```
From 5751d6c88588989280abf3ea98f01cabf0d852a9 Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Tue, 29 Oct 2024 12:00:06 -0400 Subject: [PATCH 2/7] Address comments from TallTed --- spec/index.bs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 74470793..2839d8e9 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -1212,14 +1212,14 @@ To fetch an identity assertion given a {{USVString}} 1. ("account_id", |accountId|) 1. ("is_auto_selected", |isAutoSelected|) 1. If |fields| is not empty: - 1. Let |serializedFields| be the entries of |fields| concatenated with a "," + 1. Let |serializedFields| be the entries of |fields| concatenated with a comma ("`,`") between elements. 1. Append ("fields", |serializedFields|) to |list|. 1. If |disclosureShownFor| is not empty: 1. Let |serializedDisclosure| be the entries of |disclosureShownFor| concatenated - with a "," between elements. + with a comma ("`,`") between elements. 1. Append ("disclosure_shown_for", |serializedDisclosure|) to |list|. - 1. If |disclosureShownFor| contains all of "name", "email" and "picture", append + 1. If |disclosureShownFor| contains all of "name", "email", and "picture", append ("disclosure_text_shown", true) to |list|. 1. If |provider|'s {{IdentityProviderRequestOptions/params}} is not empty: 1. Let |json| be the result of [=serializing a JavaScript value to a JSON string=] @@ -1311,7 +1311,7 @@ an {{IdentityProviderAPIConfig}} |config|, an {{IdentityProviderRequestOptions}} {{IdentityProviderBranding}} to inform the style choices of its UI. Additionally: 1. If |provider|.{{IdentityProviderRequestOptions/fields}} is not [=list/empty=]: 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] - is defined and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of + is defined, and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display the |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] link. 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/terms_of_service_url}}"] @@ -1319,7 +1319,7 @@ an {{IdentityProviderAPIConfig}} |config|, an {{IdentityProviderRequestOptions}} |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display the |metadata|["{{IdentityProviderClientMetadata/terms_of_service_url}}"] link. 1. The user agent MUST prompt the user for permission to share the data in |fields|, - interpreting the strings as follows (this is the list of recognized fields): + interpreting the strings in the list of recognized fields as follows: : `"name"` :: The user's name as given in {{IdentityProviderAccount}}.{{IdentityProviderAccount/name}}. : `"email"` From 01b3796a7bab9600582d4a1ca0b3e25e72717656 Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Thu, 7 Nov 2024 16:10:32 -0500 Subject: [PATCH 3/7] npm comments --- spec/index.bs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 2839d8e9..73d276f9 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -895,8 +895,13 @@ the exception thrown. |accountsList|. 1. If |account| is failure, return (failure, true). 1. If [=compute the connection status=] of |account|, |provider| and |globalObject| is - [=compute the connection status/connected=], or if |provider|. - {{IdentityProviderRequestOptions/fields}} is [=list/empty=], set |permission| to true. + [=compute the connection status/connected=], set |permission| to true. + 1. Otherwise, if |provider|.{{IdentityProviderRequestOptions/fields}} is [=list/empty=], + [=create a connection between the RP and the IdP account=] with |provider|, |account|, + and |globalObject| and set |permission| to |true|. + + Note: The connection would normally be created in the [=request permission to sign-up=] + algorithm, but we do not want to show an extra dialog in this case. 1. Otherwise: 1. Let |permission| be the result of running the [=request permission to sign-up=] algorithm with |account|, |config|, |provider|, and |globalObject|. @@ -1212,15 +1217,20 @@ To fetch an identity assertion given a {{USVString}} 1. ("account_id", |accountId|) 1. ("is_auto_selected", |isAutoSelected|) 1. If |fields| is not empty: - 1. Let |serializedFields| be the entries of |fields| concatenated with a comma ("`,`") + 1. Let |fieldsString| be the entries of |fields| concatenated with a comma ("`,`") between elements. - 1. Append ("fields", |serializedFields|) to |list|. + 1. Append ("fields", |fieldsString|) to |list|. 1. If |disclosureShownFor| is not empty: - 1. Let |serializedDisclosure| be the entries of |disclosureShownFor| concatenated + 1. Let |disclosureString| be the entries of |disclosureShownFor| concatenated with a comma ("`,`") between elements. - 1. Append ("disclosure_shown_for", |serializedDisclosure|) to |list|. + 1. Append ("disclosure_shown_for", |disclosureString|) to |list|. 1. If |disclosureShownFor| contains all of "name", "email", and "picture", append ("disclosure_text_shown", true) to |list|. + + Note: This parameter exists for backwards compatibility with older identity providers + that do not yet support `disclosure_shown_for`. At the time, the disclosure text, + if shown, always included name, email and picture. Newer identity providers should + check `disclosure_shown_for` instead. 1. If |provider|'s {{IdentityProviderRequestOptions/params}} is not empty: 1. Let |json| be the result of [=serializing a JavaScript value to a JSON string=] with |provider|'s {{IdentityProviderRequestOptions/params}}. @@ -1301,15 +1311,16 @@ To request permission to sign-up the user with a given an {{IdentityP an {{IdentityProviderAPIConfig}} |config|, an {{IdentityProviderRequestOptions}} |provider|, and a |globalObject|, run the following steps. This returns a boolean. 1. Assert: These steps are running [=in parallel=]. - 1. Let |metadata| be the result of running [=fetch the client metadata=] with |config|, - |provider|, and |globalObject|. - 1. Let |fields| be |provider|.{{IdentityProviderRequestOptions/fields}} or, if not specified, + 1. Let |fields| be |provider|.{{IdentityProviderRequestOptions/fields}} or, if not present, `["name", "email", "picture"]`. - Note: Unspecified is different from an explicitly specified empty list. + Note: Omitted is different from an explicitly present empty list. + 1. Let |metadata| be null. + 1. If |fields| is not [=list/empty=], set |metadata| to the result of running [=fetch the client + metadata=] with |config|, |provider|, and |globalObject|. 1. Prompt the user to gather explicit intent to create an account. The user agent MAY use the {{IdentityProviderBranding}} to inform the style choices of its UI. Additionally: - 1. If |provider|.{{IdentityProviderRequestOptions/fields}} is not [=list/empty=]: + 1. If |fields| is not [=list/empty=]: 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] is defined, and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display From dec043b479664229377ddf7958b136ed8160036d Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Tue, 19 Nov 2024 16:02:00 -0500 Subject: [PATCH 4/7] TallTed oxford commas --- spec/index.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 204ba804..b8102f74 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -899,7 +899,7 @@ the exception thrown. 1. Otherwise, if |mediation| is "{{CredentialMediationRequirement/silent}}", return (failure, true). 1. Otherwise, if |accountsList|'s size is 1: 1. Set |account| to |accountsList|[0]. - 1. If [=compute the connection status=] of |account|, |provider| and |globalObject| returns + 1. If [=compute the connection status=] of |account|, |provider|, and |globalObject| returns [=compute the connection status/connected=], show a dialog to request user permission to sign in via |account|, and set the result in |permission|. The user agent MAY use |options|'s {{IdentityCredentialRequestOptions/context}} and |options|'s @@ -915,7 +915,7 @@ the exception thrown. [=compute the connection status/connected=], set |permission| to true. 1. Otherwise, if |provider|.{{IdentityProviderRequestOptions/fields}} is [=list/empty=], [=create a connection between the RP and the IdP account=] with |provider|, |account|, - and |globalObject| and set |permission| to |true|. + and |globalObject|, and set |permission| to |true|. Note: The connection would normally be created in the [=request permission to sign-up=] algorithm, but we do not want to show an extra dialog in this case. @@ -1246,8 +1246,8 @@ To fetch an identity assertion given a {{USVString}} Note: This parameter exists for backwards compatibility with older identity providers that do not yet support `disclosure_shown_for`. At the time, the disclosure text, - if shown, always included name, email and picture. Newer identity providers should - check `disclosure_shown_for` instead. + if shown, always included name, email, and picture. Newer identity providers should + instead check `disclosure_shown_for`. 1. If |provider|'s {{IdentityProviderRequestOptions/params}} is not empty: 1. Let |json| be the result of [=serializing a JavaScript value to a JSON string=] with |provider|'s {{IdentityProviderRequestOptions/params}}. From b1c4998d17e29ec008562c7f19edcad110263130 Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Wed, 4 Dec 2024 17:31:11 -0500 Subject: [PATCH 5/7] allow UAs to not show the permission prompt --- spec/index.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 21b7d8c5..075b62d6 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -906,7 +906,7 @@ the exception thrown. {{IdentityCredentialRequestOptions/mode}} to customize the dialog. 1. Otherwise, let |permission| be the result of running [=request permission to sign-up=] algorithm with |account|, |config|, |provider|, and |globalObject|. Also set - |permissionRequested| to true. + |permissionRequested| to true if the user agent [=supports showing a permission prompt=]. 1. Otherwise: 1. Set |account| to the result of running the [=select an account=] from the |accountsList|. @@ -1378,7 +1378,8 @@ an {{IdentityProviderAPIConfig}} |config|, an {{IdentityProviderRequestOptions}} metadata=] with |config|, |provider|, and |globalObject|. 1. Prompt the user to gather explicit intent to create an account. The user agent MAY use the {{IdentityProviderBranding}} to inform the style choices of its UI. Additionally: - 1. If |fields| is not [=list/empty=]: + 1. The user agent SHOULD support showing a permission prompt as follows. + 1. If |fields| is not [=list/empty=] and the user agent [=supports showing a permission prompt=]: 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] is defined, and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display From b5760feae2fab35ce2e07c37d2a3d5261ffac93f Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Thu, 5 Dec 2024 14:31:54 -0500 Subject: [PATCH 6/7] comments --- spec/index.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/index.bs b/spec/index.bs index 075b62d6..5884f50e 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -1377,9 +1377,9 @@ an {{IdentityProviderAPIConfig}} |config|, an {{IdentityProviderRequestOptions}} 1. If |fields| is not [=list/empty=], set |metadata| to the result of running [=fetch the client metadata=] with |config|, |provider|, and |globalObject|. 1. Prompt the user to gather explicit intent to create an account. The user agent MAY use the - {{IdentityProviderBranding}} to inform the style choices of its UI. Additionally: - 1. The user agent SHOULD support showing a permission prompt as follows. - 1. If |fields| is not [=list/empty=] and the user agent [=supports showing a permission prompt=]: + {{IdentityProviderBranding}} to inform the style choices of its UI. Additionally, if the user agent + supports showing a permission prompt: + 1. If |fields| is not [=list/empty=]: 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] is defined, and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of |account|["{{IdentityProviderAccount/approved_clients}}"], then the user agent MUST display From cf5dc4237b42d3234cd0039ebb69f64caab79b25 Mon Sep 17 00:00:00 2001 From: Christian Biesinger Date: Thu, 5 Dec 2024 14:35:48 -0500 Subject: [PATCH 7/7] note --- spec/index.bs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/index.bs b/spec/index.bs index 5884f50e..96c02b8e 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -1379,6 +1379,12 @@ an {{IdentityProviderAPIConfig}} |config|, an {{IdentityProviderRequestOptions}} 1. Prompt the user to gather explicit intent to create an account. The user agent MAY use the {{IdentityProviderBranding}} to inform the style choices of its UI. Additionally, if the user agent supports showing a permission prompt: + + Note: Identity providers should support showing their own permission prompt using + {{IdentityAssertionResponse/continue_on}} when the `disclosure_shown_for` parameter + does not contain the fields required by the IDP. This is to support user agents that + do not support showing a permission prompt. + 1. If |fields| is not [=list/empty=]: 1. If |metadata| is not failure, |metadata|["{{IdentityProviderClientMetadata/privacy_policy_url}}"] is defined, and the |provider|'s {{IdentityProviderConfig/clientId}} is not in the list of