-
Notifications
You must be signed in to change notification settings - Fork 112
Add initial Identity Handler SW Registration Tie In. #842
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,6 +54,18 @@ spec: login-status; urlPrefix: https://w3c-fedid.github.io/login-status | |
| text: unknown; url: unknown | ||
| text: get the login status; url: get-the-login-status | ||
| text: set the login status; url: set-the-login-status | ||
| spec: service-workers; urlPrefix: https://w3c.github.io/ServiceWorker/ | ||
| type: dfn | ||
| text: register; url: register | ||
| text: unregister; url: unregister | ||
| text: soft update; url: soft-update | ||
| text: create job; url: create-job | ||
| text: schedule job; url: schedule-job | ||
| text: registration map; url: dfn-scope-to-registration-map | ||
| type: dfn; for: job | ||
| text: update via cache mode; url: dfn-job-update-via-cache-mode | ||
| text: referrer; url: job-referrer | ||
| text: worker type; url: dfn-job-worker-type | ||
| </pre> | ||
|
|
||
| <pre class=link-defaults> | ||
|
|
@@ -69,6 +81,13 @@ spec:fetch; type:dfn; for:/; text:response | |
| spec:css-color-5; type:type; text:<color> | ||
| spec:html; type:dfn; text:allowed to use | ||
| spec:html; type:dfn; for:/; text:same site | ||
| spec:service-workers; type:dfn; for:/; text:service worker | ||
| spec:service-workers; type:dfn; for:/; text:service workers | ||
| spec:service-workers; type:dfn; for:/; text:service worker registration | ||
| spec:storage; type:dfn; text:storage key | ||
| spec:html; type:dfn; text:parallel queue | ||
| spec:html; type:dfn; text:start a new parallel queue | ||
| spec:html; type:dfn; text:enqueue steps | ||
| </pre> | ||
|
|
||
| <style> | ||
|
|
@@ -1296,6 +1315,28 @@ or failure. | |
| 1. Let |allowed_config_url| be the result of [=computing the manifest URL=] with |provider|, | ||
| |wellKnown|.{{IdentityProviderWellKnown/provider_urls}}[0], and |globalObject|. | ||
| 1. If |allowed_config_url| is not [=url/equal=] to |configUrl|, return failure. | ||
| 1. Let |idpOrigin| be |configUrl|'s [=url/origin=]. | ||
| 1. If |wellKnown|'s {{IdentityProviderWellKnown/identity_handler}} is present, then | ||
| [=enqueue steps=] to |idpOrigin|'s [=identity handler queue=] to | ||
| [=register the identity handler=] given |configUrl| and |wellKnown|'s | ||
| {{IdentityProviderWellKnown/identity_handler}}. | ||
| 1. Otherwise, [=enqueue steps=] to |idpOrigin|'s [=identity handler queue=] to | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a common pattern, eg having a queue per origin? Is it needed here, or is having a single queue for this type of task enough? |
||
| [=unregister the identity handler=] given |idpOrigin|. | ||
|
|
||
| Note: The identity handler is keyed to |configUrl|'s [=/origin=], not to the [=/origin=] the | ||
| [=well-known file=] was fetched from. Every credentialed endpoint is [=same origin=] with | ||
| |configUrl| (see [=computing the manifest URL=]), so keying the handler this way guarantees | ||
| its registration scope can cover the endpoints it intercepts, regardless of where the | ||
| [=well-known file=] is hosted. | ||
|
|
||
| Note: These steps are only reached when the [=well-known file=] was fetched successfully, so | ||
| a network error or an unparsable [=well-known file=] leaves an existing registration | ||
| unchanged. An [=IDP=] disables interception by serving a [=well-known file=] without an | ||
| {{IdentityProviderWellKnown/identity_handler}} member, not by making the file unavailable. | ||
|
|
||
| Note: These steps do not wait for the registration to complete. A concurrent FedCM request | ||
| for the same [=IDP=] runs on the same [=identity handler queue=], so it observes a | ||
| consistent registration state rather than racing this one. | ||
| 1. Return |config|. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IdentityProviderWellKnown.provider_urls is a list and can hold URLs on distinct [=/origins=] under the same eTLD+1. Because registration is keyed to |configUrl|'s origin, each origin gets its own [=FedCM identity-handler storage key=] and its own independent registration — all sourced from the same well-known identity_handler.service_worker value.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would fetching the well-known file end up in failure if provider_urls are > 1 currently due to :
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, I missed the size > 1 failure at L1249–1251. So this is moot on today's spec text. |
||
|
|
||
| </div> | ||
|
|
@@ -1316,11 +1357,172 @@ to keep their actual config files on an arbitary path while allowing the user ag | |
| path manipulation to fingerprint (for instance, by including the RP in the path). See | ||
| [[#manifest-fingerprinting]]. | ||
|
|
||
| <!-- ============================================================ --> | ||
| ### Register the identity handler ### {#register-identity-handler} | ||
| <!-- ============================================================ --> | ||
|
|
||
| An [=IDP=] MAY declare a FedCM-specific [=/service worker=] in its [=well-known file=] using the | ||
| {{IdentityProviderWellKnown/identity_handler}} member. When it is present, the [=user agent=] | ||
| registers and manages this [=/service worker=] itself and dispatches credentialed FedCM requests to | ||
| it. The [=IDP=] page does not register the [=/service worker=]: because credentialed FedCM requests | ||
| can happen long after the [=IDP=] page was last visited, the [=user agent=] is responsible for | ||
| ensuring the declared [=/service worker=] is registered and has an [=active worker=] when it is | ||
| needed. | ||
|
|
||
| Note: There is no separate "enabled" switch. An [=IDP=] disables interception by removing the | ||
| {{IdentityProviderWellKnown/identity_handler}} member from its [=well-known file=], which causes the | ||
| [=user agent=] to [=unregister the identity handler=] and use the direct-network path. | ||
|
|
||
| Each [=/origin=] has an associated <dfn>FedCM identity-handler storage key</dfn>: a | ||
| [=storage key=] the [=user agent=] constructs for that [=/origin=]'s FedCM identity handler. | ||
| It MUST be distinct from the [=/origin=]'s first-party [=storage key=], such that: | ||
|
|
||
| 1. The two do not [=storage key/equals|compare as equal=], so that a [=registration map=] lookup | ||
| given one of them never returns a [=service worker registration=] stored under the other; | ||
| 1. No Web API surface accepts this [=storage key=] or otherwise allows page script to cause a | ||
| [=service worker registration=] to be stored under it; and | ||
| 1. It is derivable from the [=/origin=] alone by the [=user agent=], so that a later FedCM flow can | ||
| recompute it to look up or [=unregister=] the [=/service worker=] without any persisted state. | ||
|
|
||
| Note: This isolation ensures FedCM registrations neither collide with the [=IDP=]'s ordinary | ||
| first-party [=/service workers=] nor are reachable or removable by unrelated code sharing the | ||
| [=IDP=]'s [=/origin=]. | ||
|
|
||
| Note: When the [=user agent=] later dispatches a credentialed request to the identity handler, it | ||
| uses this explicit, [=user agent=]-only association between the [=IDP=] and the registration it | ||
| created (keyed by the [=FedCM identity-handler storage key=]), rather than the implicit service | ||
| worker match path used for client-controlled fetches. Because a credentialed FedCM request has no | ||
| controlling client, this ensures the request reaches only a [=/service worker=] the [=user agent=] | ||
| registered for that specific [=IDP=]. | ||
|
|
||
| Each [=/origin=] has an associated <dfn>identity handler queue</dfn>, which is a | ||
| [=parallel queue=] the [=user agent=] [=starts a new parallel queue|starts=] the first time it is | ||
| needed. [=Register the identity handler=] and [=unregister the identity handler=] run on it, so at | ||
| most one of them runs at a time for a given [=/origin=] and their effects are observable in the | ||
| order they were enqueued. | ||
|
|
||
| Note: Concurrent FedCM requests for the same [=IDP=] can otherwise observe different | ||
| [=well-known files=] from the HTTP cache and race to opposite conclusions about whether an identity | ||
| handler is declared. | ||
|
|
||
| <div algorithm> | ||
| To <dfn>register the identity handler</dfn> given a [=/URL=] |configUrl| and an | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we avoid calling this |
||
| {{IdentityHandler}} |handler|, run these steps: | ||
|
|
||
| 1. Assert: These steps are running on |configUrl|'s [=url/origin=]'s | ||
| [=identity handler queue=]. | ||
| 1. Let |idpOrigin| be |configUrl|'s [=url/origin=]. | ||
| 1. Let |scriptURL| be the result of running the [=URL parser=] on |handler|'s | ||
| {{IdentityHandler/service_worker}} with |configUrl| as the base URL. | ||
| 1. If |scriptURL| is failure, return. | ||
| 1. If |scriptURL|'s [=url/origin=] is not [=same origin=] with |idpOrigin|, return. | ||
|
|
||
| Note: A cross-origin declaration is ignored, and credentialed requests fall back to the | ||
| network. | ||
| 1. Let |scope| be the result of running the [=URL parser=] on "`./`" with |scriptURL| as the | ||
|
Brandr0id marked this conversation as resolved.
|
||
| base URL. | ||
|
|
||
| Note: This matches the [=/scope=] that [[!SERVICE-WORKERS]] derives for a page-initiated | ||
| registration that does not pass one explicitly. | ||
| 1. Let |storageKey| be the [=FedCM identity-handler storage key=] for |idpOrigin|. | ||
| 1. Let |registration| be the result of running [=match service worker registration=] given | ||
| |storageKey| and |scriptURL| [[!SERVICE-WORKERS]]. | ||
|
|
||
| Note: [=Match service worker registration=] returns the registration whose [=/scope=] is | ||
| the longest prefix of |scriptURL|. The [=user agent=] keeps at most one | ||
| [=service worker registration=] under |storageKey| (see the teardown step below), so that | ||
| is the identity handler's registration if one exists. It is used here in preference to the | ||
| [=registration map=] lookups it wraps, because it is the entry point [[!SERVICE-WORKERS]] | ||
| exports for this purpose. | ||
| 1. If |registration| is not null, |registration|'s [=active worker=] is not null, and | ||
| |registration|'s [=active worker=]'s [=service worker/script url=] [=url/equals=] | ||
| |scriptURL|, then: | ||
| 1. [=Soft update=] |registration|. | ||
| 1. Return. | ||
|
|
||
| Note: The [=user agent=] proceeds with the [=active worker=] that is already registered and | ||
| checks for a newer version in parallel, so update discovery never delays a FedCM request. | ||
| Because [=job/update via cache mode=] is "`all`", how often that check reaches the network | ||
| is governed by the script's `Cache-Control` and `ETag` headers, which the [=IDP=] controls. | ||
| Running it on each invocation, rather than relying on the [=/service worker=]'s own | ||
| staleness threshold, is what lets an [=IDP=] ship a fix at an unchanged script URL without | ||
| waiting a day for it to be picked up. | ||
| 1. [=Unregister the identity handler=] given |idpOrigin|. | ||
|
Brandr0id marked this conversation as resolved.
|
||
|
|
||
| Note: This step matters most when |registration| is null. If the [=IDP=] moves the declared | ||
| script to a path outside the previous [=/scope=], the lookup misses while the earlier | ||
| registration is still stored under |storageKey|. Tearing down first also clears a | ||
| registration whose script never activated, and keeps at most one | ||
| [=service worker registration=] under |storageKey|. | ||
| 1. Let |job| be the result of running [=create job=] with *register*, |storageKey|, |scope|, | ||
| |scriptURL|, null, and null. | ||
| 1. Set |job|'s [=job/worker type=] to "`classic`". | ||
|
|
||
| Note: [=Create job=] does not initialize [=job/worker type=], and [=register=] compares it | ||
| against the newest worker's type. {{IdentityHandler}} has no member for selecting a module | ||
| worker, so the [=user agent=] always registers a classic one. | ||
| 1. Set |job|'s [=job/update via cache mode=] to "`all`". | ||
|
Brandr0id marked this conversation as resolved.
|
||
|
|
||
| Note: Setting update-via-cache to "`all`" lets the [=IDP=] control update discovery through | ||
| the script's `Cache-Control`/`ETag` headers. | ||
| 1. Set |job|'s [=job/referrer=] to |scriptURL|. | ||
|
|
||
| Note: [=Register=] compares |job|'s script URL and [=/scope=] against |job|'s | ||
| [=job/referrer=]'s [=/origin=], which [=create job=] otherwise derives from a | ||
| [=/service worker client=]. A FedCM registration has no client, so the [=user agent=] sets | ||
| the [=job/referrer=] to the declared script URL, which is [=same origin=] with both by | ||
| construction. | ||
| 1. Invoke [=schedule job=] with |job|. | ||
|
|
||
| Note: The job has no [=/service worker client=] and no promise, so a script that fails to | ||
| fetch or install is not reported to the [=IDP=]. Credentialed requests fall back to the | ||
| network until a later FedCM flow retries the registration. | ||
| </div> | ||
|
|
||
| <div algorithm> | ||
| To <dfn>unregister the identity handler</dfn> given an [=/origin=] |idpOrigin|, run these steps: | ||
|
|
||
| 1. Assert: These steps are running on |idpOrigin|'s [=identity handler queue=]. | ||
| 1. Let |storageKey| be the [=FedCM identity-handler storage key=] for |idpOrigin|. | ||
| 1. Let |scopes| be an empty [=list=]. | ||
| 1. [=list/For each=] (|entryStorageKey|, |entryScope|) of the [=registration map=]'s | ||
|
Brandr0id marked this conversation as resolved.
|
||
| [=map/keys=]: | ||
| 1. If |entryStorageKey| is |storageKey|, [=list/append=] |entryScope| to |scopes|. | ||
| 1. [=list/For each=] |scope| of |scopes|: | ||
| 1. Let |job| be the result of running [=create job=] with *unregister*, |storageKey|, | ||
| |scope|, null, null, and null. | ||
| 1. Invoke [=schedule job=] with |job|. | ||
|
|
||
| Note: The [=user agent=] collects the matching [=/scopes=] before scheduling any job, rather | ||
| than scheduling while iterating the [=registration map=]. It iterates at all, rather than | ||
| [=unregister=]ing one known [=/scope=], because the | ||
| {{IdentityProviderWellKnown/identity_handler}} declaration, and with it the declared | ||
| [=/scope=], may already be gone by the time these steps run. [=Register the identity handler=] | ||
| keeps at most one entry under |storageKey|, so |scopes| is not expected to hold more than one | ||
| [=/scope=]. | ||
| </div> | ||
|
|
||
| Note: Because the [=well-known file=] is fetched subject to ordinary HTTP caching, its freshness | ||
| lifetime bounds how quickly adding or removing the {{IdentityProviderWellKnown/identity_handler}} | ||
| member takes effect. [=IDPs=] are encouraged to serve it with a short `Cache-Control` `max-age` so | ||
| that enabling or disabling the identity handler propagates promptly. | ||
|
|
||
| The [=/origin=]'s storage, for the purposes of clearing storage [[!STORAGE]] and of any | ||
| [[CLEAR-SITE-DATA]] operation targeting that [=/origin=], includes any | ||
| [=service worker registration|registrations=] stored under the | ||
| [=FedCM identity-handler storage key=] for that [=/origin=], so no FedCM-specific clearing | ||
| mechanism is required. | ||
|
|
||
| <xmp class="idl"> | ||
| dictionary IdentityProviderWellKnown { | ||
| sequence<USVString> provider_urls; | ||
| USVString accounts_endpoint; | ||
| USVString login_url; | ||
| IdentityHandler identity_handler; | ||
| }; | ||
|
|
||
| dictionary IdentityHandler { | ||
| required USVString service_worker; | ||
| }; | ||
|
|
||
| dictionary IdentityProviderIcon { | ||
|
|
@@ -2134,6 +2336,21 @@ The {{IdentityProviderWellKnown}} JSON object has the following semantics: | |
| :: A URL that points to the same location as the {{IdentityProviderAPIConfig/accounts_endpoint}} in [[#idp-api-config-file]]s. | ||
| : <dfn>login_url</dfn> | ||
| :: A URL that points to the same location as the {{IdentityProviderAPIConfig/login_url}} in [[#idp-api-config-file]]s. | ||
| : <dfn>identity_handler</dfn> | ||
| :: An optional {{IdentityHandler}} declaring a FedCM-specific [=/service worker=] that the | ||
| [=user agent=] registers and manages in order to intercept credentialed FedCM requests. | ||
| See [[#register-identity-handler]]. | ||
| </dl> | ||
|
|
||
| The {{IdentityHandler}} JSON object has the following semantics: | ||
|
|
||
| <dl dfn-type="dict-member" dfn-for="IdentityHandler"> | ||
| : <dfn>service_worker</dfn> | ||
| :: The [=/service worker=] script URL, which is [=same origin=] with the [=config file=], so | ||
| that a party who controls a different subdomain of the [=IDP=] cannot declare an identity | ||
| handler for this [=/origin=]. The [=user agent=] derives the registration [=/scope=] from | ||
| this URL, and ignores the declaration when it is cross-origin. See | ||
| [=register the identity handler=]. | ||
| </dl> | ||
|
|
||
| Either {{IdentityProviderWellKnown/provider_urls}} or both | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.