diff --git a/spec/index.bs b/spec/index.bs index 7fa089b..df22892 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -263,443 +263,6 @@ identity federation. -+- -+- -+- - - -# The Identity Provider API # {#idp-api} - - -This section is non-normative. - -The [=IDP=] proactively and cooperatively exposes itself as a comformant agent -by exposing a series of HTTP endpoints: - -1. A [[#idp-api-manifest]] endpoint in an agreed upon location that points to -1. An [[#idp-api-accounts-endpoint]] endpoint -1. A [[#idp-api-client-id-metadata-endpoint]] endpoint -1. An [[#idp-api-id-assertion-endpoint]] endpoint - - -The [=IDP=] must also host a file at the ".well-known/web-identity" path of its [=registrable domain=] that has JSON contents that are convertable to an {{IdentityProviderWellKnown}} object. - - -dictionary IdentityProviderWellKnown { - required sequence<USVString> provider_urls; -}; - - -The FedCM API introduces the ability for a site to ask the browser to execute a few different -network requests. It is important for the browser -to execute these in such a way that it does not allow the user to be tracked (by an attacker -impersonating an [=IDP=]) on to the site using FedCM. The following table has information about the -network requests performed: - - - - -## The Well-Known File ## {#idp-api-well-known} - - -NOTE: The browser uses the well-known file to prevent the following attack described [here](https://github.com/fedidcg/FedCM/issues/230#issuecomment-1067029200). - -The [=IDP=] exposes a well-known file in a pre-defined location, specifically at the "web-identity" file at the [=IDPs=]'s path ".well-known". - -The well-known file is fetched: - -(a) **without** cookies, -(b) **with** the Sec-Fetch-Dest header set to `webidentity`, and -(c) **without** revealing the [=RP=] in the Origin or - [[RFC9110#field.referer|Referer]] headers. - -For example: - -
-```http -GET /.well-known/web-identity HTTP/1.1 -Host: idp.example -Accept: application/json -Sec-Fetch-Dest: webidentity -``` -
- -The file is parsed expecting a [=Well-Known=] JSON object. - -The Well-Known JSON object has the following properties: - -
- : provider_urls (required) - :: A list of URLs that points to valid [[#idp-api-manifest]]s. -
- - -## Manifest ## {#idp-api-manifest} - - -The config endpoint is an endpoint -which serves as a discovery device to other endpoints provided by the -[=IDP=]. - -The config endpoint is fetched: - -(a) **without** cookies, -(b) **with** the Sec-Fetch-Dest header set to `webidentity`, -(c) **without** revealing the [=RP=] in the Origin or - [[RFC9110#field.referer|Referer]] headers, and -(c) **without** following [[RFC9110#field.location|HTTP redirects]]. - -For example: - -
-```http -GET /config.json HTTP/1.1 -Host: idp.example -Accept: application/json -Sec-Fetch-Dest: webidentity -``` -
- -The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderAPIConfig}} without an exception. - - -dictionary IdentityProviderIcon { - required USVString url; - unsigned long size; -}; - -dictionary IdentityProviderBranding { - USVString background_color; - USVString color; - sequence<IdentityProviderIcon> icons; -}; - -dictionary IdentityProviderAPIConfig { - required USVString accounts_endpoint; - required USVString client_metadata_endpoint; - required USVString id_assertion_endpoint; - IdentityProviderBranding branding; -}; - - -The {{IdentityProviderAPIConfig}} object's members have the following semantics: - -
- : accounts_endpoint - :: A URL that points to an HTTP API that complies with the [[#idp-api-accounts-endpoint]] API. - : client_metadata_endpoint - :: A URL that points to an HTTP API that complies with the [[#idp-api-client-id-metadata-endpoint]] API. - : id_assertion_endpoint - :: A URL that points to an HTTP API that complies with the [[#idp-api-id-assertion-endpoint]] API. - : branding - :: A set of {{IdentityProviderBranding}} options. -
- -The {{IdentityProviderBranding}} enables an [=IDP=] to express their branding -preferences, which may be used by [=user agents=] to customize the permission prompt. - -Note: The branding preferences are deliberately designed to be high level -/ abstract (rather than opinionated about a specific UI structure), to -enable different [=user agents=] to offer different UI experiences and -for them to evolve independently over time. - -Its members have the following semantics: -
- : background_color - :: Background [=color=] for [=IDP=]-branded widgets such as buttons. - : color - :: [=color=] for text on [=IDP=] branded widgets. - : icons - :: A list of {{IdentityProviderIcon}} objects. -
- -Note: The branding preferences are deliberately designed to be high level -/ abstract (rather than opinionated about a specific UI structure), to -enable different [=user agents=] to offer different UI experiences and -for them to evolve independently over time. - - -The {{IdentityProviderIcon}} has members with the following semantics: - -
- : url - :: The url pointing to the icon image, which must be square and single resolution - (not a multi-resolution .ico). The icon needs to comply with the - [maskable](https://www.w3.org/TR/appmanifest/#icon-masks) specification. - : size - :: The width/height of the square icon. The size may be omitted if the icon is in a vector - graphic format (like SVG). -
- -Note: the [=user agent=] reserves a square size for the icons provided by the developer. If the -developer provides an icon that is not square, the [=user agent=] may choose to not display it at -all, trim the icon and show a square portion of it, or even transform it into a square icon and show -that. - -The color is a subset of CSS <> syntax, namely <>s, ''hsl()''s, ''rgb()''s and <>. - -For example: - -
-```json -{ - "accounts_endpoint": "/accounts", - "client_metadata_endpoint": "/metadata", - "id_assertion_endpoint": "/assertion", - "branding": { - "background_color": "green", - "color": "0xFFEEAA", - "icons": [{ - "url": "https://idp.example/icon.ico", - "size": 25 - }] - } -} -``` -
- - -## Accounts List ## {#idp-api-accounts-endpoint} - - -The accounts list endpoint provides the list of accounts the user has at the [=IDP=]. - -The accounts list endpoint is fetched -(a) **with** [=IDP=] cookies, -(b) **with** the Sec-Fetch-Dest header set to `webidentity`, -(c) **without** revealing the [=RP=] in the Origin or - [[RFC9110#field.referer|Referer]] headers, and -(d) **without** following [[RFC9110#field.location|HTTP redirects]]. - -For example: - -
-```http -GET /accounts_list HTTP/1.1 -Host: idp.example -Accept: application/json -Cookie: 0x23223 -Sec-Fetch-Dest: webidentity -``` -
- -The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderAccountList}} without an exception. - - - -dictionary IdentityProviderAccount { - required USVString id; - required USVString name; - required USVString email; - USVString given_name; - USVString picture; - sequence<USVString> approved_clients; -}; -dictionary IdentityProviderAccountList { - sequence<IdentityProviderAccount> accounts; -}; - - -Every {{IdentityProviderAccount}} is expected to have members with the following semantics: - -
- : id - :: The account unique identifier. - : name - :: The user's full name. - : email - :: The user's email address. - : given_name - :: The user's given name. - : picture - :: URL for the account's picture. - : approved_clients - :: A list of [=RP=]s (that gets matched against the requesting {{IdentityProviderConfig/clientId}}) this account is already registered with. - Used in the [=request permission to sign-up=] to allow the [=IDP=] to control whether to show - the Privacy Policy and the Terms of Service. -
- -For example: - -
-```json -{ - "accounts": [{ - "id": "1234", - "given_name": "John", - "name": "John Doe", - "email": "john_doe@idp.example", - "picture": "https://idp.example/profile/123", - "approved_clients": ["123", "456", "789"] - }, { - "id": "5678", - "given_name": "Johnny", - "name": "Johnny", - "email": "johnny@idp.example", - "picture": "https://idp.example/profile/456" - "approved_clients": ["abc", "def", "ghi"] - }] -} -``` -
- -Issue: [Clarify](https://github.com/fedidcg/FedCM/issues/218) the IDP API response when the user is not signed in. - - -## Client Metadata ## {#idp-api-client-id-metadata-endpoint} - - -The client metadata endpoint provides metadata about [=RP=]s. - -The client medata endpoint is fetched -(a) **without** cookies, -(b) **with** the Sec-Fetch-Dest header set to `webidentity`, -(c) **with** the [=RP=]'s origin in the Origin header, and -(d) **without** following [[RFC9110#field.location|HTTP redirects]]. - -The user agent also passes the **client_id**. - -For example: - -
-```http -GET /client_medata?client_id=1234 HTTP/1.1 -Host: idp.example -Origin: https://rp.example/ -Accept: application/json -Sec-Fetch-Dest: webidentity -``` -
- -The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderClientMetadata}} without an exception. - - -dictionary IdentityProviderClientMetadata { - USVString privacy_policy_url; - USVString terms_of_service_url; -}; - - -The {{IdentityProviderClientMetadata}} object's members have the following semantics: - -
- : privacy_policy_url - :: A link to the [=RP=]'s Privacy Policy. - : terms_of_service_url - :: A link to the [=RP=]'s Terms of Service. -
- -For example: - -
-```json -{ - "privacy_policy_url": "https://rp.example/clientmetadata/privacy_policy.html", - "terms_of_service_url": "https://rp.example/clientmetadata/terms_of_service.html" -} -``` -
- - -## Identity Assertions ## {#idp-api-id-assertion-endpoint} - - -The identity assertion endpoint is responsible for minting a new token that contains signed assertions about the user. - -The identity assertion endpoint is fetched - -(a) as a **POST** request, -(b) **with** [=IDP=] cookies, -(c) **with** the [=RP=]'s origin in the Origin header, and -(d) **with** the Sec-Fetch-Dest header set to `webidentity`, -(e) **without** following [[RFC9110#field.location|HTTP redirects]]. - -It will also contain the following parameters in the request body `application/x-www-form-urlencoded`: - -
- : client_id - :: The [=RP=]'s unique identifier from the [=IDP=] - : nonce - :: The request nonce - : account_id - :: The account identifier that was selected. - : disclosure_text_shown - :: Whether the user agent has explicitly shown to the user what specific information the - [=IDP=] intends to share with the [=RP=] (e.g. "idp.example will share your name, email... - with rp.example"), used by the [=request permission to sign-up=] algorithm for new users but - not by the [=sign-in=] algorithm for returning users. -
- -For example: - -
-```http -POST /fedcm_assertion_endpoint HTTP/1.1 -Host: idp.example -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 -``` -
- -
-An [=IDP=] MUST check the Origin header to ensure that a malicious [=RP=] does -not receive an ID token corresponding to another [=RP=]. In other words, the [=IDP=] MUST check that -the Origin header value is represented by the -{{IdentityProviderConfig/clientId}}. As the {{IdentityProviderConfig/clientId}} are -[=IDP=]-specific, the [=user agent=] cannot perform this check. -
- -The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderToken}} without an exception. - - -dictionary IdentityProviderToken { - required USVString token; -}; - - -Every {{IdentityProviderToken}} is expected to have members with the following semantics: - -
- : token - :: The resulting token. -
- -The content of the {{IdentityProviderToken/token}} is opaque to the user agent and can contain -anything that the [=IDP=] would like to pass to the -[=RP=] to facilitate the login. For this reason the [=RP=] -is expected to be the party responsible for validating the {{IdentityProviderToken/token}} passed -along from the [=IDP=] using the appropriate token validation -algorithms defined. One example of how this might be done is defined -in [[OIDC-Connect-Core#IDTokenValidation]]. - -NOTE: For [=IDPs=], it is worth considering how -[portable](https://github.com/fedidcg/FedCM/issues/314) accounts are. -Portability is left entirely up to [=IDPs=], who can choose -between a variety of different mechanisms to accomplish it -(e.g. [OIDC's Account Porting](https://openid.net/specs/openid-connect-account-porting-1_0.html)). - -For example: - -
-```json -{ - "token" : "eyJC...J9.eyJzdWTE2...MjM5MDIyfQ.SflV_adQssw....5c" -} -``` -
- # The Browser API # {#browser-api} @@ -1344,6 +907,442 @@ To sign-in the user with a given an [=AccountState=] |accountState|: 1. Change |accountState|'s {{AccountState/allows logout}} from false to true. + +# Identity Provider HTTP API # {#idp-api} + + +This section is non-normative. + +The [=IDP=] proactively and cooperatively exposes itself as a comformant agent +by exposing a series of HTTP endpoints: + +1. A [[#idp-api-manifest]] endpoint in an agreed upon location that points to +1. An [[#idp-api-accounts-endpoint]] endpoint +1. A [[#idp-api-client-id-metadata-endpoint]] endpoint +1. An [[#idp-api-id-assertion-endpoint]] endpoint + + +The [=IDP=] must also host a file at the ".well-known/web-identity" path of its [=registrable domain=] that has JSON contents that are convertable to an {{IdentityProviderWellKnown}} object. + + +dictionary IdentityProviderWellKnown { + required sequence<USVString> provider_urls; +}; + + +The FedCM API introduces the ability for a site to ask the browser to execute a few different +network requests. It is important for the browser +to execute these in such a way that it does not allow the user to be tracked (by an attacker +impersonating an [=IDP=]) on to the site using FedCM. The following table has information about the +network requests performed: + + + + +## The Well-Known File ## {#idp-api-well-known} + + +NOTE: The browser uses the well-known file to prevent the following attack described [here](https://github.com/fedidcg/FedCM/issues/230#issuecomment-1067029200). + +The [=IDP=] exposes a well-known file in a pre-defined location, specifically at the "web-identity" file at the [=IDPs=]'s path ".well-known". + +The well-known file is fetched: + +(a) **without** cookies, +(b) **with** the Sec-Fetch-Dest header set to `webidentity`, and +(c) **without** revealing the [=RP=] in the Origin or + [[RFC9110#field.referer|Referer]] headers. + +For example: + +
+```http +GET /.well-known/web-identity HTTP/1.1 +Host: idp.example +Accept: application/json +Sec-Fetch-Dest: webidentity +``` +
+ +The file is parsed expecting a [=Well-Known=] JSON object. + +The Well-Known JSON object has the following properties: + +
+ : provider_urls (required) + :: A list of URLs that points to valid [[#idp-api-manifest]]s. +
+ + +## Manifest ## {#idp-api-manifest} + + +The config endpoint is an endpoint +which serves as a discovery device to other endpoints provided by the +[=IDP=]. + +The config endpoint is fetched: + +(a) **without** cookies, +(b) **with** the Sec-Fetch-Dest header set to `webidentity`, +(c) **without** revealing the [=RP=] in the Origin or + [[RFC9110#field.referer|Referer]] headers, and +(c) **without** following [[RFC9110#field.location|HTTP redirects]]. + +For example: + +
+```http +GET /config.json HTTP/1.1 +Host: idp.example +Accept: application/json +Sec-Fetch-Dest: webidentity +``` +
+ +The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderAPIConfig}} without an exception. + + +dictionary IdentityProviderIcon { + required USVString url; + unsigned long size; +}; + +dictionary IdentityProviderBranding { + USVString background_color; + USVString color; + sequence<IdentityProviderIcon> icons; +}; + +dictionary IdentityProviderAPIConfig { + required USVString accounts_endpoint; + required USVString client_metadata_endpoint; + required USVString id_assertion_endpoint; + IdentityProviderBranding branding; +}; + + +The {{IdentityProviderAPIConfig}} object's members have the following semantics: + +
+ : accounts_endpoint + :: A URL that points to an HTTP API that complies with the [[#idp-api-accounts-endpoint]] API. + : client_metadata_endpoint + :: A URL that points to an HTTP API that complies with the [[#idp-api-client-id-metadata-endpoint]] API. + : id_assertion_endpoint + :: A URL that points to an HTTP API that complies with the [[#idp-api-id-assertion-endpoint]] API. + : branding + :: A set of {{IdentityProviderBranding}} options. +
+ +The {{IdentityProviderBranding}} enables an [=IDP=] to express their branding +preferences, which may be used by [=user agents=] to customize the permission prompt. + +Note: The branding preferences are deliberately designed to be high level +/ abstract (rather than opinionated about a specific UI structure), to +enable different [=user agents=] to offer different UI experiences and +for them to evolve independently over time. + +Its members have the following semantics: +
+ : background_color + :: Background [=color=] for [=IDP=]-branded widgets such as buttons. + : color + :: [=color=] for text on [=IDP=] branded widgets. + : icons + :: A list of {{IdentityProviderIcon}} objects. +
+ +Note: The branding preferences are deliberately designed to be high level +/ abstract (rather than opinionated about a specific UI structure), to +enable different [=user agents=] to offer different UI experiences and +for them to evolve independently over time. + + +The {{IdentityProviderIcon}} has members with the following semantics: + +
+ : url + :: The url pointing to the icon image, which must be square and single resolution + (not a multi-resolution .ico). The icon needs to comply with the + [maskable](https://www.w3.org/TR/appmanifest/#icon-masks) specification. + : size + :: The width/height of the square icon. The size may be omitted if the icon is in a vector + graphic format (like SVG). +
+ +Note: the [=user agent=] reserves a square size for the icons provided by the developer. If the +developer provides an icon that is not square, the [=user agent=] may choose to not display it at +all, trim the icon and show a square portion of it, or even transform it into a square icon and show +that. + +The color is a subset of CSS <> syntax, namely <>s, ''hsl()''s, ''rgb()''s and <>. + +For example: + +
+```json +{ + "accounts_endpoint": "/accounts", + "client_metadata_endpoint": "/metadata", + "id_assertion_endpoint": "/assertion", + "branding": { + "background_color": "green", + "color": "0xFFEEAA", + "icons": [{ + "url": "https://idp.example/icon.ico", + "size": 25 + }] + } +} +``` +
+ + +## Accounts List ## {#idp-api-accounts-endpoint} + + +The accounts list endpoint provides the list of accounts the user has at the [=IDP=]. + +The accounts list endpoint is fetched +(a) **with** [=IDP=] cookies, +(b) **with** the Sec-Fetch-Dest header set to `webidentity`, +(c) **without** revealing the [=RP=] in the Origin or + [[RFC9110#field.referer|Referer]] headers, and +(d) **without** following [[RFC9110#field.location|HTTP redirects]]. + +For example: + +
+```http +GET /accounts_list HTTP/1.1 +Host: idp.example +Accept: application/json +Cookie: 0x23223 +Sec-Fetch-Dest: webidentity +``` +
+ +The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderAccountList}} without an exception. + + + +dictionary IdentityProviderAccount { + required USVString id; + required USVString name; + required USVString email; + USVString given_name; + USVString picture; + sequence<USVString> approved_clients; +}; +dictionary IdentityProviderAccountList { + sequence<IdentityProviderAccount> accounts; +}; + + +Every {{IdentityProviderAccount}} is expected to have members with the following semantics: + +
+ : id + :: The account unique identifier. + : name + :: The user's full name. + : email + :: The user's email address. + : given_name + :: The user's given name. + : picture + :: URL for the account's picture. + : approved_clients + :: A list of [=RP=]s (that gets matched against the requesting {{IdentityProviderConfig/clientId}}) this account is already registered with. + Used in the [=request permission to sign-up=] to allow the [=IDP=] to control whether to show + the Privacy Policy and the Terms of Service. +
+ +For example: + +
+```json +{ + "accounts": [{ + "id": "1234", + "given_name": "John", + "name": "John Doe", + "email": "john_doe@idp.example", + "picture": "https://idp.example/profile/123", + "approved_clients": ["123", "456", "789"] + }, { + "id": "5678", + "given_name": "Johnny", + "name": "Johnny", + "email": "johnny@idp.example", + "picture": "https://idp.example/profile/456" + "approved_clients": ["abc", "def", "ghi"] + }] +} +``` +
+ +Issue: [Clarify](https://github.com/fedidcg/FedCM/issues/218) the IDP API response when the user is not signed in. + + +## Client Metadata ## {#idp-api-client-id-metadata-endpoint} + + +The client metadata endpoint provides metadata about [=RP=]s. + +The client medata endpoint is fetched +(a) **without** cookies, +(b) **with** the Sec-Fetch-Dest header set to `webidentity`, +(c) **with** the [=RP=]'s origin in the Origin header, and +(d) **without** following [[RFC9110#field.location|HTTP redirects]]. + +The user agent also passes the **client_id**. + +For example: + +
+```http +GET /client_medata?client_id=1234 HTTP/1.1 +Host: idp.example +Origin: https://rp.example/ +Accept: application/json +Sec-Fetch-Dest: webidentity +``` +
+ +The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderClientMetadata}} without an exception. + + +dictionary IdentityProviderClientMetadata { + USVString privacy_policy_url; + USVString terms_of_service_url; +}; + + +The {{IdentityProviderClientMetadata}} object's members have the following semantics: + +
+ : privacy_policy_url + :: A link to the [=RP=]'s Privacy Policy. + : terms_of_service_url + :: A link to the [=RP=]'s Terms of Service. +
+ +For example: + +
+```json +{ + "privacy_policy_url": "https://rp.example/clientmetadata/privacy_policy.html", + "terms_of_service_url": "https://rp.example/clientmetadata/terms_of_service.html" +} +``` +
+ + +## Identity Assertions ## {#idp-api-id-assertion-endpoint} + + +The identity assertion endpoint is responsible for minting a new token that contains signed assertions about the user. + +The identity assertion endpoint is fetched + +(a) as a **POST** request, +(b) **with** [=IDP=] cookies, +(c) **with** the [=RP=]'s origin in the Origin header, and +(d) **with** the Sec-Fetch-Dest header set to `webidentity`, +(e) **without** following [[RFC9110#field.location|HTTP redirects]]. + +It will also contain the following parameters in the request body `application/x-www-form-urlencoded`: + +
+ : client_id + :: The [=RP=]'s unique identifier from the [=IDP=] + : nonce + :: The request nonce + : account_id + :: The account identifier that was selected. + : disclosure_text_shown + :: Whether the user agent has explicitly shown to the user what specific information the + [=IDP=] intends to share with the [=RP=] (e.g. "idp.example will share your name, email... + with rp.example"), used by the [=request permission to sign-up=] algorithm for new users but + not by the [=sign-in=] algorithm for returning users. +
+ +For example: + +
+```http +POST /fedcm_assertion_endpoint HTTP/1.1 +Host: idp.example +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 +``` +
+ +
+An [=IDP=] MUST check the Origin header to ensure that a malicious [=RP=] does +not receive an ID token corresponding to another [=RP=]. In other words, the [=IDP=] MUST check that +the Origin header value is represented by the +{{IdentityProviderConfig/clientId}}. As the {{IdentityProviderConfig/clientId}} are +[=IDP=]-specific, the [=user agent=] cannot perform this check. +
+ +The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderToken}} without an exception. + + +dictionary IdentityProviderToken { + required USVString token; +}; + + +Every {{IdentityProviderToken}} is expected to have members with the following semantics: + +
+ : token + :: The resulting token. +
+ +The content of the {{IdentityProviderToken/token}} is opaque to the user agent and can contain +anything that the [=IDP=] would like to pass to the +[=RP=] to facilitate the login. For this reason the [=RP=] +is expected to be the party responsible for validating the {{IdentityProviderToken/token}} passed +along from the [=IDP=] using the appropriate token validation +algorithms defined. One example of how this might be done is defined +in [[OIDC-Connect-Core#IDTokenValidation]]. + +NOTE: For [=IDPs=], it is worth considering how +[portable](https://github.com/fedidcg/FedCM/issues/314) accounts are. +Portability is left entirely up to [=IDPs=], who can choose +between a variety of different mechanisms to accomplish it +(e.g. [OIDC's Account Porting](https://openid.net/specs/openid-connect-account-porting-1_0.html)). + +For example: + +
+```json +{ + "token" : "eyJC...J9.eyJzdWTE2...MjM5MDIyfQ.SflV_adQssw....5c" +} +``` +
+ ## Backwards Compatibility ## {#browser-api-backwards-compatibility}