w3c-fedid / FedCM

A privacy preserving identity exchange Web API
https://w3c-fedid.github.io/FedCM/
Other
369 stars 71 forks source link

Share performance measurement with IDP #352

Open yi-gu opened 1 year ago

yi-gu commented 1 year ago

Motivation

A successful web API must perform to the level required by the developers that use it. Sharing metrics with the developer to assist them monitoring how the API performs is beneficial to developers, users and FedCM API itself. e.g. an identity provider (IDP) can debug relying party (RP) specific deployment issues or monitor timing related measurements to fix bottlenecks such that their users could have more smooth federation experience. IDP developers can also provide feedback based on the metrics to improve FedCM API.

To achieve this goal at scale, we propose to send data to IDP via a new endpoint in the config file (optional unless IDP wants to receive measurement).

Proposal

Server

If an IDP wants to receive the measurement, it should specify a new endpoint metrics_endpoint in the config file. e.g.

{
  "metrics_endpoint": "/metrics.php"
}

Similar to other endpoints, it must be same-origin with the IDP.

Client

Depending on whether the API call is successful (user grants permission via the "Continue as" button), we can share different data with the IDP. For privacy reasons, any data sent to IDP must be uncredentialed without user information.

API Succeeded

When the API call succeeded, i.e. IDP has issued an id token to the RP upon user’s approval, we can send the following timing information to IDP:

A sample request to the metrics endpoint in the API success case looks like:

POST /metrics_endpoint HTTP/1.1
Host: idp.example
Origin: https://rp.example/
Content-Type: application/json
Sec-Fetch-Dest: webidentity

{
    "body": {
      "clientId": "client123",
      "timing": {
        "time_to_show_ui": 3000,
        "time_to_receive_configuration": 1000,
        "time_to_receive_accounts": 1000,
        "time_to_receive_client_metadata": 1000,
        "time_to_continue_on_ui": 3000,
        "time_to_receive_token": 2000,
        "turnaround_time": 8000,
      },
    },
    "url": "https://rp.example/",
}

Note: there could be a survivorship bias. e.g. higher latency may lead to lower reporting rate.

API failed

For privacy reasons, we cannot inform the RP “when the API call fails and why” in most cases via DOM exceptions. Thus, it’s hard for developers to understand the performance of the API without knowing the API call status.

One important reason why we cannot reject the promise immediately is that the API is called on the RP site and we do not want to leak user or IDP information to RP. However, we could communicate with IDP directly with failure information depending on the failure type.

Failure types

Type Error Code Error Message Description
IDP 101 Unavailable server: 404 HTTP 404
IDP 102 Invalid response to the well-known file request Invalid WellKnown Response
IDP 103 Invalid response to the config file request Invalid Config Response
IDP 104 Invalid response to the accounts request Invalid Accounts Response
IDP 105 Invalid response to the client metadata request Invalid ClientMetadata Response
IDP 106 Invalid response to the token request Invalid Token Response
RP 201 RP related failure Multiple Inflight Requests OR Request Aborted
User 301 User related failure User Dismissed UI OR IgnoredUI OR DisabledFedCM OR InCooldown

Note

A sample request to the metrics endpoint looks like:

POST /metrics_endpoint HTTP/1.1
Host: idp.example
Origin: https://rp.example/
Content-Type: application/json
Sec-Fetch-Dest: webidentity

{
    "body": {
      "clientId": "client123",
      "errorId": 106,
      "message": "Invalid response for the token request",
    },
    "url": "https://rp.example/",
}

Forwards Compatibility Consideration

As noted in the blog post, FedCM API is under active development and some of the new features may have impact on the metrics endpoint as well. Notably:

Once we support multiple IDP there will be some new challenges:

Privacy Consideration

The new endpoint is for IDPs to receive API call status. Thus, it should follow the same principal of FedCM API:

In this proposal, all data shared with IDP is uncredentialed without user cookies so IDP cannot correlate the RP centric measurement with users. (note: there’s a known timing attack problem that’s orthogonal to this proposal and once IdpSigninStatus API is launched, the timing attack problem can be mitigated).

Security Consideration

The new endpoint follows the same standard as other FedCM endpoints do. e.g. it has a Sec-Fetch-Dest: webidentity header with Origin instead of Referer.

In addition, we use uncredentialed POST to the IDP endpoint which could technically be abused. e.g. an attacker can use curl out of any browser client to POST data to the endpoint. Similar to the Reporting API, typically it’s up to the server to build mechanisms to mitigate the potential problem.

Considered Alternatives

Reporting API

The Reporting API allows browsers to send reports created based on various platform features (e.g. document policy violation, CSP violation etc.) to web developers to help them with fixing bugs or improving their websites. See a sample report below for document policy violation:

[
  {
    "age": 420,
    "body": {
      "columnNumber": 12,
      "disposition": "enforce",
      "lineNumber": 11,
      "message": "Document policy violation: document-write is not allowed in this document.",
      "policyId": "document-write",
      "sourceFile": "https://site.example/script.js"
    },
    "type": "document-policy-violation",
    "url": "https://site.example/",
    "user_agent": "Mozilla/5.0... Chrome/92.0.4504.0"
  },
]

The Reporting API should be able to achieve the same goal. That being said, a dedicated API should be a better choice because:

That being said, we chose to use a format that's compatible with the Reporting API in case that we want to switch to it in the future.

yi-gu commented 1 year ago

We have updated the proposal with details. PTAL. @bvandersloot-mozilla FYI

cbiesinger commented 1 month ago

OK, to keep y'all updated on the current status: We have a prototype in Chrome behind the chrome://flags/#fedcm-metrics-endpoint flag

If the fedcm request was successful, we send a credentialed request that includes the RP origin and these timings:

     "time_to_show_ui=%d"
     "&time_to_continue=%d"
     "&time_to_receive_token=%d"
     "&turnaround_time=%d"

In the failure case, we send an uncredentialed request that does not contain the RP origin and only the "error_code=%d" data. The error code is one of:

    kOther = 1,
    // Errors triggered by how RP calls FedCM API.
    kRpFailure = 100,
    // User Failures.
    kUserFailure = 200,
    // Generic IDP Failures.
    kIdpServerInvalidResponse = 300,
    kIdpServerUnavailable = 301,
    kManifestError = 302,
    // Specific IDP Failures.
    kAccountsEndpointInvalidResponse = 401,
    kTokenEndpointInvalidResponse = 402,

The metrics endpoint is specified using a metrics_endpoint field in the configURL.

Things I would love to have feedback on:

cbiesinger commented 1 month ago

For that last point, it would be possible to send the timing data in the ID assertion endpoint request itself, but of course then we couldn't include the timing for the ID assertion request itself. If you have thoughts on this tradeoff, please comment :)