Обновления FedCM: пробные версии Origin для пакета Continuation API и автоматическое предоставление Storage Access API.

Начиная с Chrome 126, разработчики могут начать использовать исходную пробную версию пакета функций Federated Credential Management API (FedCM) для настольных компьютеров, которые позволяют использовать некоторые варианты использования авторизации . Пакет состоит из API-интерфейса продолжения и API-интерфейса параметров, которые обеспечивают процесс авторизации OAuth, аналогичный потоку авторизации, с использованием диалогового окна разрешений, предоставленного поставщиком удостоверений (IdP). Пакет также включает в себя другие изменения, такие как API полей, несколько URL-адресов configURL и пользовательские метки учетной записи. Начиная с Chrome 126, мы также представляем исходную пробную версию API доступа к хранилищу (SAA), которая автоматически разрешает запросы SAA, если пользователь ранее успешно входил в систему с помощью FedCM.

Пробная версия Origin: пакет API продолжения FedCM

Пакет FedCM Continuation API состоит из нескольких расширений FedCM:

Продолжение API

Пользователь входит в систему RP, а затем авторизуется в режиме кнопки.

Вы можете посмотреть демо-версию API на Glitch .

API продолжения позволяет конечной точке утверждения идентификатора IdP при необходимости возвращать URL-адрес, который FedCM отобразит, чтобы позволить пользователю продолжить многоэтапный процесс входа. Это позволяет IdP запросить у пользователя предоставить проверяющей стороне (RP) разрешения, выходящие за рамки существующих возможностей пользовательского интерфейса FedCM, например, доступ к ресурсам на стороне сервера пользователя.

Обычно конечная точка утверждения идентификатора возвращает токен, необходимый для аутентификации.

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

Однако с помощью Continuation API конечная точка утверждения идентификатора может возвращать свойство continue_on , которое включает в себя абсолютный или относительный путь к конечной точке утверждения идентификатора.

{
  // 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=..."
}

Как только браузер получает ответ continue_on , открывается новое всплывающее окно, которое направляет пользователя по указанному пути.

После того, как пользователь взаимодействует со страницей, например, предоставляя дополнительное разрешение на обмен дополнительной информацией с RP, страница IdP может вызвать IdentityProvider.resolve() , чтобы разрешить исходный вызов navigator.credentials.get() и вернуть токен в качестве аргумента. .

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);
});

Затем браузер сам закроет всплывающее окно и вернет токен вызывающему API.

Если пользователь отклоняет запрос, вы можете закрыть окно, вызвав IdentityProvider.close() .

IdentityProvider.close();

Если по какой-то причине пользователь изменил свою учетную запись во всплывающем окне (например, IdP предлагает функцию «переключить пользователя» или в случаях делегирования), вызов разрешения принимает необязательный второй аргумент, позволяющий что-то вроде:

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

API параметров

API параметров позволяет RP предоставлять дополнительные параметры конечной точке утверждения идентификатора . С помощью API параметров RP могут передавать IdP дополнительные параметры для запроса разрешений на доступ к ресурсам помимо базового входа. Пользователь авторизует эти разрешения через управляемый IdP поток пользовательского интерфейса, который запускается через Continuation API .

Чтобы использовать API, добавьте параметры в свойство params как объект в вызове navigator.credentials.get() .

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',
      }
    },
  }
});

Имена свойств в объекте params начинаются с param_ . В приведенном выше примере свойство params содержит IDP_SPECIFIC_PARAM как '1' , foo как 'BAR' , ETC как 'MOAR' и scope как 'calendar.readonly photos.write' . Это будет переведено как param_IDP_SPECIFIC_PARAM=1&param_foo=BAR&param_ETC=MOAR&param_scope=calendar.readonly%20photos.write в теле 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=234234&disclosure_text_shown=false&param_IDP_SPECIFIC_PARAM=1&param_foo=BAR&param_ETC=MOAR&param_scope=calendar.readonly%20photos.write

Получайте разрешения динамически

В общем, для пользователей наиболее полезно запрашивать разрешения тогда, когда они необходимы, а не тогда, когда разработчик считает, что их легче всего реализовать. Например, запрашивать разрешение на доступ к камере, когда пользователь собирается сделать фотографию, предпочтительнее, чем запрашивать разрешение, как только пользователь заходит на веб-сайт. Та же практика применяется и к ресурсам сервера. Запрашивайте разрешения только тогда, когда они необходимы пользователю. Это называется «динамическая авторизация».

Чтобы динамически запрашивать авторизацию с помощью FedCM, IdP может:

  1. Вызовите navigator.credentials.get() с необходимыми параметрами, которые IdP может понять, например, scope .
  2. Конечная точка утверждения идентификатора подтверждает, что пользователь уже вошел в систему, и отправляет в ответ URL-адрес continue_on .
  3. Браузер открывает всплывающее окно со страницей разрешений IdP с запросом дополнительных разрешений, соответствующих запрошенным областям.
  4. После авторизации через IdentityProvider.resolve() от IdP окно закрывается, и исходный вызов navigator.credentials.get() RP получает соответствующий токен или код авторизации, чтобы RP мог обменять его на соответствующий токен доступа.

API полей

API Fields позволяет RP объявлять атрибуты учетной записи для запроса у IdP, чтобы браузер мог отображать правильный пользовательский интерфейс раскрытия информации в диалоговом окне FedCM; IdP несет ответственность за включение запрошенных полей в возвращаемый токен. Рассмотрим это, запрашивая «базовый профиль» в OpenID Connect, а не «области» в OAuth.

Сообщение о раскрытии информации в режиме виджета.
Сообщение о раскрытии информации в режиме виджета.
Сообщение о раскрытии информации в режиме кнопки.
Сообщение о раскрытии информации в режиме кнопки.

Чтобы использовать Fields API, добавьте параметры в свойство fields в виде массива при вызове navigator.credentials.get() . На данный момент поля могут содержать 'name' , 'email' и 'picture' , но в будущем их можно расширить, чтобы включить больше значений.

Запрос с fields будет выглядеть так:

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',
});

HTTP-запрос к конечной точке утверждения идентификатора включает параметр fields , указанный RP, с параметром disclosure_text_shown , установленным как true если это не возвращающийся пользователь, и поля, которые браузер раскрыл пользователю в параметре disclosure_shown_for :

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

Если RP требуется доступ к каким-либо дополнительным данным от IdP, например доступ к календарю, это должно быть обработано с помощью специального параметра, как указано выше. IdP возвращает URL-адрес continue_on для запроса разрешения.

Если fields — пустой массив, запрос будет выглядеть так:

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',
});

Если fields — это пустой массив, пользовательский агент пропустит пользовательский интерфейс раскрытия.

Сообщение о раскрытии информации не отображается в режиме виджета. В потоке кнопок пользовательский интерфейс раскрытия полностью пропускается.
Сообщение о раскрытии информации не отображается в режиме виджета. В потоке кнопок пользовательский интерфейс раскрытия полностью пропускается.

Это так, даже если ответ от конечной точки учетных записей не содержит идентификатор клиента, соответствующий RP в approved_clients .

В этом случае disclosure_text_shown , отправленное в конечную точку утверждения идентификатора, является ложным в теле HTTP:

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

Несколько configURL

Несколько URL-адресов configURL позволяют поставщикам удостоверений размещать несколько файлов конфигурации для одного поставщика удостоверений путем указания accounts_endpoint и login_url в общеизвестном файле так же, как и файлы конфигурации.

Если в общеизвестный файл добавляются accounts_endpoint и login_url , provider_urls игнорируется, чтобы IdP мог поддерживать несколько файлов конфигурации. Если это не так, provider_urls продолжает действовать, обеспечивая обратную совместимость.

Хорошо известный файл, поддерживающий несколько configURL, может выглядеть так:

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

Это позволяет нам:

  1. Поддерживайте обратную и прямую совместимость с существующими общеизвестными файлами и более ранними версиями браузеров, которые уже развернуты в реальных условиях.
  2. Имейте произвольное количество файлов конфигурации — при условии, что все они указывают на одни и те же accounts_endpoint и login_url .
  3. Не иметь возможности добавлять энтропию к запросу на выборку учетных данных, сделанному в accounts_endpoint , поскольку он должен быть указан на уровне «.well-known».

Поддержка нескольких configURL не является обязательной, и существующие реализации FedCM могут остаться прежними.

Пользовательские ярлыки учетной записи

Пользовательские метки учетных записей позволяют поставщикам удостоверений FedCM аннотировать учетные записи, чтобы RP могли их фильтровать, указав метку в файле конфигурации. Аналогичная фильтрация возможна с использованием API подсказок домена и API подсказок для входа, указав их в вызове navigator.credentials.get() , но пользовательские метки учетной записи могут фильтровать пользователей, указав файл конфигурации, что особенно полезно при наличии нескольких configURL. используются. Пользовательские метки учетной записи также отличаются тем, что они предоставляются с сервера IdP, а не от RP, как подсказки для входа в систему или домена.

Пример

IdP поддерживает два configURL для потребителя и предприятия соответственно. Файл конфигурации потребителя имеет метку 'consumer' , а файл конфигурации предприятия — метку 'enterprise' .

При такой настройке известный файл включает в себя accounts_endpoint и login_url чтобы разрешить использование нескольких configURL.

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

Если accounts_endpoint указан в известном файле, provider_urls игнорируется. RP может напрямую указывать на соответствующие файлы конфигурации в вызове navigator.credentials.get() .

Файл конфигурации потребителя находится по адресу https://idp.example/fedcm.json и включает свойство accounts , определяющее 'consumer' с помощью include .

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

Корпоративный файл конфигурации находится по адресу https://idp.example/enterprise/fedcm.json и включает в себя свойство accounts , указывающее 'enterprise' с помощью include .

{
  "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"
  }
}

Конечная точка общих учетных записей IdP (в этом примере https://idp.example/accounts ) возвращает список учетных записей, который включает свойство labels с назначенными labels в массиве для каждой учетной записи. Ниже приведен пример ответа для пользователя, имеющего две учетные записи. Один для потребителя, а другой для предприятия:

{
 "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"]
  }]
}

Если RP хочет разрешить пользователям 'enterprise' входа в систему, они могут указать configURL 'enterprise' 'https://idp.example/enterprise/fedcm.json' в вызове navigator.credentials.get() :

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

В результате пользователю для входа доступен только идентификатор учетной записи '4567' . Идентификатор учетной записи '123' незаметно скрывается браузером, поэтому пользователю не будет предоставлена ​​учетная запись, которая не поддерживается IdP на этом сайте.

Пробная версия Origin: FedCM как сигнал доверия для Storage Access API

Chrome 126 начинает тестирование FedCM в качестве сигнала доверия для Storage Access API . Благодаря этому изменению предварительное предоставление разрешения через FedCM становится веской причиной для автоматического одобрения запроса на доступ к хранилищу API-интерфейсами доступа к хранилищу .

Это полезно, когда встроенный iframe хочет получить доступ к персонализированным ресурсам: например, если idp.example встроен в rp.example и ему необходимо отобразить персонализированный ресурс. Если браузер ограничивает доступ к сторонним файлам cookie, даже если пользователь вошел в систему rp.example, используя idp.example с FedCM, встроенный iframe idp.example не сможет запрашивать персонализированные ресурсы, поскольку запросы не будут включать сторонние файлы cookie.

Для этого idp.example необходимо получить разрешение на доступ к хранилищу через iframe, встроенный в веб-сайт, и это можно получить только через запрос разрешения.

Поскольку FedCM является сигналом доверия для API доступа к хранилищу , проверки разрешений API доступа к хранилищу не только принимают разрешение, предоставленное запросом доступа к хранилищу, но также разрешение, предоставленное запросом FedCM.

// 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();

После входа пользователя в систему с помощью FedCM разрешение предоставляется автоматически, пока активна аутентификация FedCM. Это означает, что после отключения пользователя при запросе разрешения отобразится запрос.

Примите участие в испытании происхождения

Вы можете попробовать пакет FedCM Continuation API локально, включив флаг Chrome chrome://flags#fedcm-authz в Chrome 126 или более поздней версии. Вы также можете попробовать FedCM в качестве сигнала доверия для API доступа к хранилищу локально, включив #fedcm-with-storage-access-api в Chrome 126 или более поздней версии.

Эти функции также доступны в виде пробных версий. Пробные версии Origin позволяют вам опробовать новые функции и оставить отзыв об их удобстве использования, практичности и эффективности. Для получения дополнительной информации ознакомьтесь с разделом Начало работы с пробными версиями Origin .

Чтобы попробовать исходную пробную версию пакета FedCM Continuation API , создайте два пробных токена источника:

Если вы заинтересованы во включении Continuation API вместе с потоком кнопок , включите также пробную версию API-интерфейса режима кнопок :

Чтобы попробовать FedCM в качестве сигнала доверия для пробной версии Storage Access API :

Пробная версия исходного пакета Continuation API и FedCM в качестве сигнала доверия для пробной версии исходного API Storage Access доступны в Chrome 126.

Зарегистрируйте стороннюю пробную версию для RP

  1. Перейдите на страницу регистрации пробной версии Origin.
  2. Нажмите кнопку «Зарегистрироваться» и заполните форму, чтобы запросить токен.
  3. Введите источник IdP как Web Origin .
  4. Установите флажок Стороннее сопоставление, чтобы внедрить токен с помощью JavaScript в другие источники.
  5. Нажмите «Отправить» .
  6. Встройте выданный токен на сторонний сайт.

Чтобы встроить токен на сторонний веб-сайт, добавьте следующий код в библиотеку JavaScript или SDK поставщика удостоверений, обслуживаемую источником поставщика удостоверений.

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

Замените TOKEN_GOES_HERE своим собственным токеном.