FedCM-Updates: Ursprungstests für das Continuation API-Bundle und die automatische Zuweisung der Storage Access API

Ab Chrome 126 können Entwickler einen Ursprungstest für eine Reihe von FedCM-Funktionen (FedCM) für Desktop-Computer ausführen, die einige Anwendungsfälle der Autorisierung ermöglichen. Das Bundle besteht aus der Continuation API und der Parameters API. Diese ermöglichen einen OAuth-Autorisierungsablauf ähnlich wie ein Berechtigungsdialogfeld, das vom Identitätsanbieter (IdP) bereitgestellt wird. Das Bundle enthält auch andere Änderungen wie die Fields API, mehrere configURLs und benutzerdefinierte Kontolabels. Ab Chrome 126 führen wir auch einen Ursprungstest für die Storage Access API (SAA) ein, bei dem SAA-Anfragen automatisch genehmigt werden, wenn sich der Nutzer in der Vergangenheit erfolgreich mit FedCM angemeldet hat.

Ursprungstest: FedCM Continuation API-Bundle

Das FedCM Continuation API-Paket besteht aus mehreren FedCM-Erweiterungen:

Continuation API

Ein Nutzer meldet sich beim RP an und autorisiert ihn dann über den Schaltflächenmodus.

Du kannst dir eine Demo der API auf Glitch ansehen.

Mit der Continuation API kann der ID-Assertion-Endpunkt des IdP optional eine URL zurückgeben, die FedCM rendert, damit der Nutzer einen mehrstufigen Anmeldevorgang fortsetzen kann. Dadurch kann der IdP den Nutzer auffordern, der vertrauenden Partei Berechtigungen zu erteilen, die über die Möglichkeiten der vorhandenen FedCM-UI hinausgehen, z. B. Zugriff auf die serverseitigen Ressourcen des Nutzers.

Normalerweise gibt der ID-Assertion-Endpunkt ein Token zurück, das für die Authentifizierung erforderlich ist.

{
  "token": "***********"
}

Mit der Continuation API kann der ID-Assertion-Endpunkt jedoch ein continue_on-Attribut zurückgeben, das einen absoluten Pfad oder einen relativen Pfad zum ID-Assertion-Endpunkt enthält.

{
  // In the id_assertion_endpoint, instead of returning a typical
  // "token" response, the IdP decides that it needs the user to
  // continue on a pop-up window:
  "continue_on": "/oauth/authorize?scope=..."
}

Sobald der Browser die continue_on-Antwort erhält, wird ein neues Pop-up-Fenster geöffnet, in dem der Nutzer zum angegebenen Pfad weitergeleitet wird.

Nachdem der Nutzer mit der Seite interagiert hat, z. B. indem er weitere Berechtigungen zum Teilen zusätzlicher Informationen mit dem RP gewährt hat, kann die IdP-Seite IdentityProvider.resolve() aufrufen, um den ursprünglichen navigator.credentials.get()-Aufruf aufzulösen und ein Token als Argument zurückzugeben.

document.getElementById('allow_btn').addEventListener('click', async () => {
  let accessToken = await fetch('/generate_access_token.cgi');
  // Closes the window and resolves the promise (that is still hanging
  // in the relying party's renderer) with the value that is passed.
  IdentityProvider.resolve(accessToken);
});

Der Browser schließt dann das Pop-up und gibt das Token an den API-Aufrufer zurück.

Wenn der Nutzer die Anfrage ablehnt, können Sie das Fenster durch Aufrufen von IdentityProvider.close() schließen.

IdentityProvider.close();

Wenn der Nutzer aus irgendeinem Grund sein Konto im Pop-up geändert hat (z. B. bietet der IdP eine Funktion zum Wechseln des Nutzers an oder in Delegierungsfällen), verwendet der Auflösungsaufruf ein optionales zweites Argument, das in etwa Folgendes zulässt:

IdentityProvider.resolve(token, {accountId: '1234');

Parameter-API

Über die Parameters API kann die RP zusätzliche Parameter für den Endpunkt der ID-Assertion bereitstellen. Mit der Parameters API können RPs zusätzliche Parameter an den IdP weitergeben, um Berechtigungen für Ressourcen anzufordern, die über die einfache Anmeldung hinausgehen. Der Nutzer autorisiert diese Berechtigungen über einen IdP-gesteuerten UX-Ablauf, der über die Continuation API gestartet wird.

Wenn du die API verwenden möchtest, füge dem Attribut params im navigator.credentials.get()-Aufruf Parameter als Objekt hinzu.

let {token} = await navigator.credentials.get({
  identity: {
    providers: [{
      clientId: '1234',
      configURL: 'https://idp.example/fedcm.json',
      // Key/value pairs that need to be passed from the
      // RP to the IdP but that don't really play any role with
      // the browser.
      params: {
        IDP_SPECIFIC_PARAM: '1',
        foo: 'BAR',
        ETC: 'MOAR',
        scope: 'calendar.readonly photos.write',
      }
    },
  }
});

Den Attributnamen im params-Objekt wird das Präfix param_ vorangestellt. Im obigen Beispiel enthält das Attribut „params“ IDP_SPECIFIC_PARAM als '1', foo als 'BAR', ETC als 'MOAR' und scope als 'calendar.readonly photos.write'. Dies wird im HTTP-Text der Anfrage als param_IDP_SPECIFIC_PARAM=1&param_foo=BAR&param_ETC=MOAR&param_scope=calendar.readonly%20photos.write übersetzt:

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=234234&disclosure_text_shown=false&param_IDP_SPECIFIC_PARAM=1&param_foo=BAR&param_ETC=MOAR&param_scope=calendar.readonly%20photos.write

Berechtigungen dynamisch abrufen

Im Allgemeinen ist es für Nutzer am hilfreichsten, Berechtigungen dann anzufordern, wenn sie benötigt werden, und nicht, wenn der Entwickler der Meinung ist, dass sie sich am einfachsten implementieren lassen. Wenn der Nutzer beispielsweise kurz vor der Aufnahme eines Fotos ist, wird die Berechtigung für den Zugriff auf eine Kamera bevorzugt, sobald er die Website aufruft. Dasselbe gilt für Serverressourcen. Fordern Sie Berechtigungen nur dann an, wenn sie für den Nutzer benötigt werden. Dies wird als „dynamische Autorisierung“ bezeichnet.

Für eine dynamische Autorisierung mit FedCM kann der IdP Folgendes tun:

  1. Rufen Sie navigator.credentials.get() mit den erforderlichen Parametern auf, die der IdP verstehen kann, z. B. scope.
  2. Der Endpunkt für die ID-Assertion bestätigt, dass der Nutzer bereits angemeldet ist, und antwortet mit der URL continue_on.
  3. Der Browser öffnet ein Pop-up-Fenster mit der Berechtigungsseite des IdP, in der Sie um zusätzliche Berechtigung für die angeforderten Bereiche gebeten werden.
  4. Nach der Autorisierung über IdentityProvider.resolve() vom IdP wird das Fenster geschlossen und der ursprüngliche navigator.credentials.get()-Aufruf des RP erhält ein relevantes Token oder einen Autorisierungscode, damit der RP es gegen ein passendes Zugriffstoken austauschen kann.

Fields API

Mit der Fields API kann der RP Kontoattribute für Anfragen vom IdP deklarieren, damit der Browser eine entsprechende Benutzeroberfläche zur Offenlegung im FedCM-Dialogfeld rendern kann. Der IdP ist dafür verantwortlich, die angeforderten Felder in das zurückgegebene Token aufzunehmen. Stellen Sie sich vor, Sie fordern ein „einfaches Profil“ in OpenID Connect an, im Gegensatz zu „Bereichen“ in OAuth.

Offenlegungsmeldung im Widget-Modus.
Hinweis zur Offenlegung im Widget-Modus.
Offenlegungsmitteilung im Schaltflächenmodus.
Hinweis zur Offenlegung im Schaltflächenmodus.

Wenn Sie die Fields API verwenden möchten, fügen Sie dem Attribut fields im navigator.credentials.get()-Aufruf Parameter als Array hinzu. Die Felder können vorerst 'name', 'email' und 'picture' enthalten, aber sie lassen sich für später um weitere Werte erweitern.

Eine Anfrage mit fields würde so aussehen:

let { token } = await navigator.credentials.get({
  identity: {
    providers: [{
      fields: ['name', 'email', 'picture'],
      clientId: '1234',
      configURL: 'https://idp.example/fedcm.json',
      params: {
        scope: 'drive.readonly calendar.readonly',
      }
    },
  }
  mediation: 'optional',
});

Die HTTP-Anfrage an den Endpunkt der ID-Assertion enthält den RP-spezifischen Parameter fields, wobei der Parameter disclosure_text_shown auf true festgelegt ist, wenn es sich nicht um einen wiederkehrenden Nutzer handelt, und die Felder, die der Browser dem Nutzer in einem disclosure_shown_for-Parameter offengelegt hat:

POST /id_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=234234&disclosure_text_shown=true&fields=email,name,picture&disclosure_shown_for=email,name,picture

Wenn der RP Zugriff auf zusätzliche Daten vom IdP benötigt, z. B. Zugriff auf einen Kalender, sollte dies wie oben beschrieben mit einem benutzerdefinierten Parameter erfolgen. Der IdP gibt eine continue_on-URL zurück, um die Berechtigung anzufordern.

Wenn fields ein leeres Array ist, würde die Anfrage so aussehen:

let { token } = await navigator.credentials.get({
  identity: {
    providers: [{
      fields: [],
      clientId: '1234',
      configURL: 'https://idp.example/fedcm.json',
      params: {
        scope: 'drive.readonly calendar.readonly',
      }
    },
  }
  mediation: 'optional',
});

Wenn fields ein leeres Array ist, überspringt der User-Agent die Offenlegungs-UI.

Offenlegungsmeldung wird im Widget-Modus nicht angezeigt. Beim Schaltflächenablauf wird die Benutzeroberfläche zur Offenlegung vollständig übersprungen.
Im Widget-Modus wird keine Offenlegung angezeigt. Beim Ablauf der Schaltflächen wird die Benutzeroberfläche zur Offenlegung vollständig übersprungen.

Das ist selbst dann der Fall, wenn die Antwort vom accounts endpoint keine Client-ID enthält, die mit dem RP in approved_clients übereinstimmt.

In diesem Fall ist der an den Endpunkt der ID-Assertion gesendete disclosure_text_shown im HTTP-Text falsch:

POST /id_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=234234&disclosure_text_shown=false

Mehrere configURLs

Mit mehreren configURLs können IdPs mehrere Konfigurationsdateien für einen IdP aufnehmen. Dazu geben sie accounts_endpoint und login_url in der bekannten Datei in derselben Datei an wie in den Konfigurationsdateien.

Wenn accounts_endpoint und login_url der bekannten Datei hinzugefügt werden, werden die provider_urls ignoriert, damit der IdP mehrere Konfigurationsdateien unterstützen kann. Ist dies nicht der Fall, wird provider_urls weiterhin wirksam, sodass es abwärtskompatibel ist.

Eine bekannte Datei, die mehrere configURLs unterstützt, kann wie folgt aussehen:

{
  "provider_urls": [ "https://idp.example/fedcm.json" ],
  "accounts_endpoint": "https://idp.example/accounts",
  "login_url": "https://idp.example/login"
}

Dies ermöglicht uns Folgendes:

  1. Die Abwärts- und Vorwärtskompatibilität mit vorhandenen bekannten Dateien und früheren Versionen von Browsern, die bereits im Einsatz sind, wird beibehalten.
  2. Beliebig viele Konfigurationsdateien haben, solange sie alle auf dieselbe accounts_endpoint und dieselbe login_url verweisen.
  3. Der Abrufanfrage für Anmeldedaten, die an accounts_endpoint gesendet wird, darf keine Entropie hinzugefügt werden, da sie auf der Ebene „.well-known“ angegeben werden muss.

Die Unterstützung mehrerer configURLs ist optional und die vorhandenen FedCM-Implementierungen können unverändert bleiben.

Benutzerdefinierte Kontolabels

Mit benutzerdefinierten Kontolabels können FedCM-IdPs Konten annotieren, damit RPs sie filtern können, indem sie das Label in einer Konfigurationsdatei angeben. Ähnliches Filtern ist mit der Domain Hint API und der Login Hint API möglich, da sie im navigator.credentials.get()-Aufruf angegeben wurden. Mit den benutzerdefinierten Kontolabels können Nutzer jedoch durch Angabe der Konfigurationsdatei gefiltert werden. Dies ist besonders nützlich, wenn mehrere configURLs verwendet werden. Benutzerdefinierte Kontolabels unterscheiden sich außerdem darin, dass sie vom IdP-Server und nicht vom RP bereitgestellt werden, z. B. Anmelde- oder Domainhinweise.

Beispiel

Ein IdP unterstützt zwei configURLs für Nutzer bzw. Unternehmen. Die Konfigurationsdatei für Nutzer hat das Label 'consumer' und die Konfigurationsdatei für Unternehmen das Label 'enterprise'.

Bei einer solchen Konfiguration enthält die bekannte Datei accounts_endpoint und login_url, um mehrere configURLs zuzulassen.

{
  "provider_urls": [ "https://idp.example/fedcm.json" ],
  "accounts_endpoint": "https://idp.example/accounts",
  "login_url": "https://idp.example/login"
}

Wenn accounts_endpoint in der bekannten Datei angegeben ist, werden provider_urls ignoriert. Die RP kann direkt auf die entsprechenden Konfigurationsdateien im navigator.credentials.get()-Aufruf verweisen.

Die Nutzerkonfigurationsdatei befindet sich unter https://idp.example/fedcm.json. Sie enthält das Attribut accounts, das 'consumer' mithilfe von include angibt.

{
  "accounts_endpoint": "https://idp.example/accounts",
  "client_metadata_endpoint": "/client_metadata",
  "login_url": "https://idp.example/login",
  "id_assertion_endpoint": "/assertion",
  "accounts": {
    "include": "consumer"
  }
}

Die Unternehmenskonfigurationsdatei befindet sich unter https://idp.example/enterprise/fedcm.json. Sie enthält das Attribut accounts, das 'enterprise' mithilfe von include angibt.

{
  "accounts_endpoint": "https://idp.example/accounts",
  "client_metadata_endpoint": "/enterprise/client_metadata",
  "login_url": "https://idp.example/login",
  "id_assertion_endpoint": "/assertion",
  "accounts": {
    "include": "enterprise"
  }
}

Der gemeinsame Kontenendpunkt des IdP (in diesem Beispiel https://idp.example/accounts) gibt eine Liste von Konten zurück, die eine Label-Property mit zugewiesenem labels in einem Array für jedes Konto enthalten. Das folgende Beispiel zeigt eine Antwort für einen Nutzer mit zwei Konten. Das eine ist für Privatnutzer und das andere für Unternehmen:

{
 "accounts": [{
   "id": "123",
   "given_name": "John",
   "name": "John Doe",
   "email": "john_doe@idp.example",
   "picture": "https://idp.example/profile/123",
   "labels": ["consumer"]
  }], [{
   "id": "4567",
   "given_name": "Jane",
   "name": "Jane Doe",
   "email": "jane_doe@idp.example",
   "picture": "https://idp.example/profile/4567",
   "labels": ["enterprise"]
  }]
}

Wenn ein RP Nutzern die Anmeldung von 'enterprise' erlauben möchte, können sie die 'enterprise' configURL 'https://idp.example/enterprise/fedcm.json' im navigator.credentials.get()-Aufruf angeben:

let { token } = await navigator.credentials.get({
  identity: {
    providers: [{
      clientId: '1234',
      nonce: '234234',
      configURL: 'https://idp.example/enterprise/fedcm.json',
    },
  }
});

Daher steht dem Nutzer nur die Konto-ID von '4567' für die Anmeldung zur Verfügung. Die Konto-ID von '123' wird vom Browser automatisch ausgeblendet, damit der Nutzer kein Konto erhält, das vom IdP auf dieser Website nicht unterstützt wird.

Ursprungstest: FedCM als Vertrauenssignal für die Storage Access API

In Chrome 126 wird ein Ursprungstest von FedCM als Vertrauenssignal für die Storage Access API gestartet. Mit dieser Änderung ist eine vorherige Gewährung von Berechtigungen über FedCM ein gültiger Grund für die automatische Genehmigung einer Speicherzugriffsanfrage durch die Storage Access APIs.

Dies ist nützlich, wenn ein eingebetteter iFrame auf personalisierte Ressourcen zugreifen möchte, z. B. wenn idp.example in rp.example eingebettet ist und eine personalisierte Ressource anzeigen muss. Wenn der Browser den Zugriff auf Drittanbieter-Cookies einschränkt, kann der eingebettete idp.example-iFrame keine personalisierten Ressourcen anfordern, selbst wenn der Nutzer in rp.example mit idp.example mit FedCM angemeldet ist, da Anfragen keine Drittanbieter-Cookies enthalten.

Dazu muss idp.example eine Speicherzugriffsberechtigung über den auf der Website eingebetteten iFrame erhalten. Dies ist nur über eine Berechtigungsaufforderung möglich.

Mit FedCM als Vertrauenssignal für die Storage Access API akzeptieren Berechtigungsprüfungen der Storage Access API nicht nur die durch eine Speicherzugriffsaufforderung erteilte Berechtigung, sondern auch die durch eine FedCM-Eingabeaufforderung erteilte Berechtigung.

// In top-level rp.example:

// Ensure FedCM permission has been granted.
const cred = await navigator.credentials.get({
  identity: {
    providers: [{
      configURL: 'https://idp.example/fedcm.json',
      clientId: '123',
    }],
  },
  mediation: 'optional',
});

// In an embedded IdP iframe:

// No user gesture is needed to call this, and the call will be auto-granted.
await document.requestStorageAccess();

// This returns `true`.
const hasAccess = await document.hasStorageAccess();

Sobald der Nutzer bei FedCM angemeldet ist, wird die Berechtigung automatisch gewährt, solange die FedCM-Authentifizierung aktiv ist. Sobald die Verbindung zum Nutzer getrennt wurde, wird beim Anfordern der Berechtigung eine Eingabeaufforderung angezeigt.

Am Ursprungstest teilnehmen

Sie können das FedCM Continuation API-Bundle lokal testen. Aktivieren Sie dazu ab Chrome 126 ein Chrome-Flag chrome://flags#fedcm-authz. Sie können FedCM auch lokal als Vertrauenssignal für die Storage Access API verwenden. Aktivieren Sie dazu #fedcm-with-storage-access-api unter Chrome 126 oder höher.

Diese Funktionen sind auch als Ursprungstests verfügbar. Mit Ursprungstests können Sie neue Funktionen ausprobieren und Feedback zu ihrer Nutzerfreundlichkeit, Funktionalität und Effektivität geben. Weitere Informationen finden Sie unter Erste Schritte mit Ursprungstests.

Wenn Sie den Ursprungstest des FedCM Continuation API-Bundles ausprobieren möchten, erstellen Sie zwei Ursprungstesttokens:

Wenn Sie die Continuation API zusammen mit dem Schaltflächenfluss aktivieren möchten, aktivieren Sie auch den Button Mode API-Ursprungstest:

So testen Sie FedCM als Vertrauenssignal für den Ursprungstest der Storage Access API:

Der Ursprungstest des Continuation API-Bundles und FedCM als Vertrauenssignal für den Ursprungstest der Storage Access API sind ab Chrome 126 verfügbar.

Ursprungstest eines Drittanbieters für die RP registrieren

  1. Rufen Sie die Registrierungsseite für den Ursprungstest auf.
  2. Klicken Sie auf die Schaltfläche Registrieren und füllen Sie das Formular aus, um ein Token anzufordern.
  3. Geben Sie den Ursprung des IdP als Web Origin (Web-Ursprung) ein.
  4. Aktivieren Sie den Drittanbieterabgleich, um das Token mit JavaScript aus anderen Quellen zu injizieren.
  5. Klicke auf Senden.
  6. Betten Sie das ausgestellte Token auf der Website eines Drittanbieters ein.

Wenn Sie das Token auf der Website eines Drittanbieters einbetten möchten, fügen Sie den folgenden Code in die JavaScript-Bibliothek oder das SDK des IdP ein, das von der Quelle des IdP bereitgestellt wird.

const tokenElement = document.createElement('meta');
tokenElement.httpEquiv = 'origin-trial';
tokenElement.content = 'TOKEN_GOES_HERE';
document.head.appendChild(tokenElement);

Ersetzen Sie TOKEN_GOES_HERE durch Ihr eigenes Token.