Bug 1788220 - Add source documentation for FedCM - r=timhuang

Also, I've added some function comments to Gecko code.

Overall, I think returning to FedCM code after 3 months away will be feasible with this documentation in place.

Differential Revision: https://phabricator.services.mozilla.com/D167958
This commit is contained in:
Benjamin VanderSloot 2023-01-27 13:04:47 +00:00
Родитель 86d43cae15
Коммит 8b357e66c9
4 изменённых файлов: 177 добавлений и 0 удалений

Просмотреть файл

@ -15,8 +15,18 @@
namespace mozilla::dom {
// This is the primary starting point for FedCM in the platform.
// This class is the implementation of the IdentityCredential object
// that is the value returned from the navigator.credentials.get call
// with an "identity" argument. It also includes static functions that
// perform operations that are used in constructing the credential.
class IdentityCredential final : public Credential {
public:
// These are promise types, all used to support the async implementation of
// this API. All are of the form MozPromise<RefPtr<T>, nsresult>.
// Tuples are included to shuffle additional values along, so that the
// intermediate state is entirely in the promise chain and we don't have to
// capture an early step's result into a callback for a subsequent promise.
typedef MozPromise<RefPtr<IdentityCredential>, nsresult, true>
GetIdentityCredentialPromise;
typedef MozPromise<IPCIdentityCredential, nsresult, true>
@ -36,6 +46,7 @@ class IdentityCredential final : public Credential {
GetAccountPromise;
typedef MozPromise<IdentityClientMetadata, nsresult, true> GetMetadataPromise;
// This needs to be constructed in the context of a window
explicit IdentityCredential(nsPIDOMWindowInner* aParent);
protected:
@ -45,22 +56,60 @@ class IdentityCredential final : public Credential {
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
// This builds a value from an IPC-friendly version. This type is returned
// to the caller of navigator.credentials.get, however we get an IPC friendly
// version back from the main process to the content process.
// This is a deep copy of the token, ID, and type.
void CopyValuesFrom(const IPCIdentityCredential& aOther);
// This is the inverse of CopyValuesFrom. Included for completeness.
IPCIdentityCredential MakeIPCIdentityCredential();
// Getter and setter for the token member of this class
void GetToken(nsAString& aToken) const;
void SetToken(const nsAString& aToken);
// This function allows a relying party to send one last credentialed request
// to the IDP when logging out. This only works if the current account state
// in the IdentityCredentialStorageService allows logouts and clears that bit
// when a request is sent.
//
// Arguments:
// aGlobal: the global of the window calling this function
// aLogoutRequest: all of the logout requests to try to send.
// This is pairs of the IDP's logout url and the account
// ID for that IDP.
// Return value:
// a promise resolving to undefined
// Side effects:
// Will send a network request to each IDP that have a state allowing
// logouts and disables that bit.
static already_AddRefed<Promise> LogoutRPs(
GlobalObject& aGlobal,
const Sequence<IdentityCredentialLogoutRPsRequest>& aLogoutRequests,
ErrorResult& aRv);
// This is the main static function called when a credential needs to be
// fetched from the IDP. Called in the content process.
// This is mostly a passthrough to `DiscoverFromExternalSourceInMainProcess`.
static RefPtr<GetIdentityCredentialPromise> DiscoverFromExternalSource(
nsPIDOMWindowInner* aParent, const CredentialRequestOptions& aOptions,
bool aSameOriginWithAncestors);
// Start the FedCM flow. This will start the timeout timer, fire initial
// network requests, prompt the user, and call into CreateCredential.
//
// Arguments:
// aPrincipal: the caller of navigator.credentials.get()'s principal
// aBrowsingContext: the BC of the caller of navigator.credentials.get()
// aOptions: argument passed to navigator.credentials.get()
// Return value:
// a promise resolving to an IPC credential with type "identity", id
// constructed to identify it, and token corresponding to the token
// fetched in FetchToken. This promise may reject with nsresult errors.
// Side effects:
// Will send network requests to the IDP. The details of which are in the
// other static methods here.
static RefPtr<GetIPCIdentityCredentialPromise>
DiscoverFromExternalSourceInMainProcess(
nsIPrincipal* aPrincipal, CanonicalBrowsingContext* aBrowsingContext,
@ -72,6 +121,7 @@ class IdentityCredential final : public Credential {
//
// Arguments:
// aPrincipal: the caller of navigator.credentials.get()'s principal
// aBrowsingContext: the BC of the caller of navigator.credentials.get()
// aProvider: the provider to validate the root manifest of
// Return value:
// a promise resolving to an IPC credential with type "identity", id
@ -160,24 +210,84 @@ class IdentityCredential final : public Credential {
const IdentityInternalManifest& aManifest,
const IdentityAccount& aAccount);
// Performs a Fetch for links to legal info about the identity provider.
// The returned promise resolves with the information in an object.
//
// Arguments:
// aPrincipal: the caller of navigator.credentials.get()'s principal
// aProvider: the identity provider to get information from
// aManfiest: the identity provider's manifest
// Return value:
// promise that resolves with an object containing legal information for
// aProvider
// Side effects:
// Network request to the provider supplied token endpoint with
// credentials and including information about the requesting principal.
//
static RefPtr<GetMetadataPromise> FetchMetadata(
nsIPrincipal* aPrincipal, const IdentityProvider& aProvider,
const IdentityInternalManifest& aManifest);
// Show the user a dialog to select what identity provider they would like
// to try to log in with.
//
// Arguments:
// aBrowsingContext: the BC of the caller of navigator.credentials.get()
// aProviders: the providers to let the user select from
// Return value:
// a promise resolving to an identity provider that the user took action
// to select. This promise may reject with nsresult errors.
// Side effects:
// Will show a dialog to the user.
static RefPtr<GetIdentityProviderPromise> PromptUserToSelectProvider(
BrowsingContext* aBrowsingContext,
const Sequence<IdentityProvider>& aProviders);
// Show the user a dialog to select what account they would like
// to try to log in with.
//
// Arguments:
// aBrowsingContext: the BC of the caller of navigator.credentials.get()
// aAccounts: the accounts to let the user select from
// aManifest: the identity provider that was chosen's manifest
// Return value:
// a promise resolving to an account that the user took action
// to select (and aManifest). This promise may reject with nsresult errors.
// Side effects:
// Will show a dialog to the user.
static RefPtr<GetAccountPromise> PromptUserToSelectAccount(
BrowsingContext* aBrowsingContext, const IdentityAccountList& aAccounts,
const IdentityInternalManifest& aManifest);
// Show the user a dialog to select what account they would like
// to try to log in with.
//
// Arguments:
// aBrowsingContext: the BC of the caller of navigator.credentials.get()
// aAccount: the accounts the user chose
// aManifest: the identity provider that was chosen's manifest
// aProvider: the identity provider that was chosen
// Return value:
// a promise resolving to an account that the user agreed to use (and
// aManifest). This promise may reject with nsresult errors. This includes
// if the user denied the terms and privacy policy
// Side effects:
// Will show a dialog to the user. Will send a network request to the
// identity provider. Modifies the IdentityCredentialStorageService state
// for this account.
static RefPtr<GetAccountPromise> PromptUserWithPolicy(
BrowsingContext* aBrowsingContext, nsIPrincipal* aPrincipal,
const IdentityAccount& aAccount,
const IdentityInternalManifest& aManifest,
const IdentityProvider& aProvider);
// Close all dialogs associated with IdentityCredential generation on the
// provided browsing context
//
// Arguments:
// aBrowsingContext: the BC of the caller of navigator.credentials.get()
// Side effects:
// Will close a dialog shown to the user.
static void CloseUserInterface(BrowsingContext* aBrowsingContext);
private:

Просмотреть файл

@ -14,6 +14,9 @@
namespace mozilla::dom {
// Helper to get a JSON structure via a Fetch.
// The Request must already be built and T should be a webidl type with
// annotation GenerateConversionToJS so it has an Init method.
template <typename T, typename TPromise = MozPromise<T, nsresult, true>>
RefPtr<TPromise> FetchJSONStructure(Request* aRequest) {
MOZ_ASSERT(XRE_IsParentProcess());

63
dom/docs/fedcm.rst Normal file
Просмотреть файл

@ -0,0 +1,63 @@
===============================
Federated Credential Management
===============================
FedCM, as it is abbreviated, is a platform feature that requires a full-stack implementation.
As such, its code is scattered throughout the codebase and it can be hard to follow the flow of execution.
This documentation aims to make those two points easier.
Code sites
==========
Code relevant to it can be found in all of the following places.
The webidl for this spec lives in ``dom/webidl/IdentityCredential.webidl``
Core spec algorithm logic and the implementation of the ``IdentityCredential`` live in ``dom/credentialmanagement/identity/IdentityCredential.{cpp,h}``. The static functions of ``IdentityCredential`` are the spec algorithm logic. Helpers for managing the ``IdentityCredential.webidl`` objects are in the other files in ``dom/credentialmanagement/identity/``. The IPC is defined on the WindowGlobal in ``dom/ipc/PWindowGlobal.ipdl`` and ``dom/ipc/WindowGlobalParent.cpp``, and is a very thin layer.
The service for managing state associated with IdentityCredentials is ``IdentityCredentialStorageService`` and the service for managing the UI prompts associated with IdentityCredentials is ``IdentityCredentialPromptService``. Both definitions and implementations are in ``toolkit/components/credentialmanagement``.
The UI panel is spread around a little. The actual DOM elements are in the HTML subtree with root at ``#identity-credential-notification`` in ``browser/base/content/popup-notifications.inc``. But the CSS describing it is spread through ``browser/themes/shared/customizableui/panelUI-shared.css``, ``browser/themes/shared/identity-credential-notification.css``, and ``browser/themes/shared/notification-icons.css``. Generally speaking, search for ``identity-credential`` in those files to find the relevant ids and classes.
Temporary content strings, which will be moved when included in i18n: ``browser/components/credentialmanager/identityCredentialNotification.ftl``, for now. This will eventually be moved to ``browser/locales/en-US/browser/``.
All of this is entered from the ``navigator.credentials`` object, implemented in ``dom/credentialmanagement/CredentialsContainer.{cpp,h}``.
Flow of Execution
=================
This is the general flow through code relevant to the core spec algorithms, which happens to be the complicated parts imo.
A few notes:
- All functions without a class specified are in ``IdentityCredential``.
- Functions in ``IdentityCredentialPromptService`` mutate the Chrome DOM
- FetchT functions send network requests via ``mozilla::dom::FetchJSONStructure<T>``.
- A call to ``IdentityCredentialStorageService`` is made in ``PromptUserWithPolicy``
.. graphviz::
digraph fedcm {
"RP (visited page) calls ``navigator.credentials.get()``" -> "CredentialsContainer::Get"
"CredentialsContainer::Get" -> "DiscoverFromExternalSource"
"DiscoverFromExternalSource" -> "DiscoverFromExternalSourceInMainProcess" [label="IPC via WindowGlobal's DiscoverIdentityCredentialFromExternalSource"]
"DiscoverFromExternalSourceInMainProcess" -> "anonymous timeout callback" -> "CloseUserInterface" -> "IdentityCredentialPromptService::Close"
"DiscoverFromExternalSourceInMainProcess" -> "PromptUserToSelectProvider"
"PromptUserToSelectProvider" -> "IdentityCredentialPromptService::ShowProviderPrompt"
"IdentityCredentialPromptService::ShowProviderPrompt" -> "CreateCredential" [label="via promise chain in DiscoverFromExternalSourceInMainProcess"]
"CreateCredential" -> "CheckRootManifest"
"CheckRootManifest" -> "FetchInternalManifest" [label="via promise chain in CreateCredential"]
"FetchInternalManifest" -> "FetchAccountList" [label="via promise chain in CreateCredential"]
"FetchAccountList" -> "PromptUserToSelectAccount" [label="via promise chain in CreateCredential"]
"PromptUserToSelectAccount" -> "IdentityCredentialPromptService::ShowAccountListPrompt"
"IdentityCredentialPromptService::ShowAccountListPrompt" -> "PromptUserWithPolicy" [label="via promise chain in CreateCredential"]
"PromptUserWithPolicy" -> "FetchMetadata"
"FetchMetadata" -> "IdentityCredentialPromptService::ShowPolicyPrompt" [label="via promise chain in PromptUserWithPolicy"]
"IdentityCredentialPromptService::ShowPolicyPrompt" -> "FetchToken" [label="via promise chain in CreateCredential"]
"FetchToken" -> "cancel anonymous timeout callback"
"FetchToken" -> "CreateCredential inline anonymous callback"
"CreateCredential inline anonymous callback" -> "DiscoverFromExternalSourceInMainProcess inline anonymous callback"
"DiscoverFromExternalSourceInMainProcess inline anonymous callback" -> "DiscoverFromExternalSource inline anonymous callback" [label="Resolving IPC via WindowGlobal's DiscoverIdentityCredentialFromExternalSource"]
"DiscoverFromExternalSource inline anonymous callback" -> "CredentialsContainer::Get inline anonymous callback"
"CredentialsContainer::Get inline anonymous callback" -> "RP (visited page) gets the credential"
}

Просмотреть файл

@ -13,3 +13,4 @@ These linked pages contain design documents for the DOM implementation in Gecko.
workersAndStorage/index
webIdlBindings/index
ioutils_migration
fedcm