Triển khai giải pháp danh tính bằng FedCM ở phía Nhà cung cấp danh tính

Việc triển khai FedCM bao gồm một số bước cốt lõi cho cả Nhà cung cấp dịch vụ danh tính (IdP)Bên phụ thuộc (RP). Tham khảo tài liệu để tìm hiểu cách triển khai FedCM ở phía RP.

IdPs phải hoàn tất các bước sau để triển khai FedCM:

Tạo một tệp .well-known

Để ngăn các trình theo dõi lạm dụng API, bạn phải phân phát một tệp đã biết từ /.well-known/web-identity của eTLD+1 của IdP.

Tệp đã biết có thể bao gồm các thuộc tính sau:

Thuộc tính Bắt buộc Mô tả
provider_urls bắt buộc Mảng đường dẫn đến tệp cấu hình IdP. Bị bỏ qua (nhưng vẫn bắt buộc) nếu bạn chỉ định accounts_endpointlogin_url.
accounts_endpoint được đề xuất, cần có login_url
URL cho điểm cuối tài khoản. Điều này cho phép hỗ trợ nhiều cấu hình, miễn là mỗi tệp cấu hình đều sử dụng cùng một URL login_urlaccounts_endpoint.

Lưu ý: Tham số này được hỗ trợ từ Chrome 132.
login_url được đề xuất, cần có accounts_endpoint URL trang đăng nhập để người dùng đăng nhập vào IdP. Điều này cho phép hỗ trợ nhiều cấu hình, miễn là mỗi tệp cấu hình đều sử dụng cùng một login_urlaccounts_endpoint.

Lưu ý: thông số này được hỗ trợ từ Chrome 132 trở lên.

Ví dụ: nếu các điểm cuối IdP được phân phát trong https://accounts.idp.example/, thì các điểm cuối đó cũng phải phân phát một tệp well-known tại https://idp.example/.well-known/web-identity cũng như một tệp cấu hình IdP. Sau đây là ví dụ về nội dung tệp đã biết:

  {
    "provider_urls": ["https://accounts.idp.example/config.json"]
  }

IdP có thể chứa nhiều tệp cấu hình cho một IdP bằng cách chỉ định accounts_endpointlogin_url trong tệp well-known. Tính năng này có thể hữu ích trong những trường hợp sau:

  • IdP cần hỗ trợ nhiều cấu hình kiểm thử và sản xuất khác nhau.
  • Nhà cung cấp danh tính (IdP) cần hỗ trợ nhiều cấu hình cho mỗi khu vực (ví dụ: eu-idp.exampleus-idp.example).

Để hỗ trợ nhiều cấu hình (ví dụ: để phân biệt giữa môi trường kiểm thử và môi trường phát hành chính thức), IdP phải chỉ định accounts_endpointlogin_url:

  {
    // This property is required, but will be ignored when IdP supports
    // multiple configs (when `accounts_endpoint` and `login_url` are
    // specified), as long as `accounts_endpoint` and `login_url` in
    // that config file match those in the well-known file.
    "provider_urls": [ "https://idp.example/fedcm.json" ],

    // Specify accounts_endpoint and login_url properties to support
    // multiple config files.
    // Note: The accounts_endpoint and login_url must be identical
    // across all config files. Otherwise,
    // the configurations won't be supported.
    "accounts_endpoint": "https://idp.example/accounts",
    "login_url": "https://idp.example/login"
  }

Tạo tệp cấu hình và điểm cuối IdP

Tệp cấu hình IdP cung cấp danh sách các điểm cuối bắt buộc cho trình duyệt. Nhà cung cấp danh tính phải lưu trữ một hoặc nhiều tệp cấu hình, cũng như các điểm cuối và URL bắt buộc. Tất cả các phản hồi JSON phải được phân phát bằng loại nội dung application/json.

URL của tệp cấu hình được xác định bằng các giá trị được cung cấp cho lệnh gọi navigator.credentials.get() được thực thi trên một RP. RP sẽ truyền URL của tệp cấu hình cho mọi nhà cung cấp danh tính:

  // Executed on RP's side:
  try {
    const credential = await navigator.credentials.get({
      identity: {
        providers: [
          {
            // To allow users to sign in with the IdP1 using FedCM, RP specifies the IdP's config file URL:
            configUrl: 'https://idp1.example/foo.json', // first IdP
            clientId: '123',
          },
          // To allow users to sign in with the IdP2 using FedCM, RP specifies the IdP's config file URL.
          // Note that multiple IdPs in a single get() are supported from Chrome 136.
          {
            configUrl: 'https://idp2.example/bar.json', // second IdP
            clientId: '456',
          },
        ],
      },
    });

    const token = credential.token;
    // Get the current IdP's configURL to identify which provider the user is signed in with
    const currentIdpConfigUrl = credential.configURL;
    if (currentIdpConfigUrl === 'https://idp1.example/foo.json') {
      // handle the case where the user signed in with idp1
    } else if (currentIdpConfigUrl === 'https://idp2.example/bar.json') {
      // handle the case where the user signed in with idp2
    }
  } catch (error) {
    // handle error
  }

Trình duyệt sẽ tìm nạp tệp cấu hình bằng một yêu cầu GET mà không có tiêu đề Origin hoặc tiêu đề Referer. Yêu cầu không có cookie và không tuân theo lệnh chuyển hướng. Điều này ngăn chặn IdP biết được ai đã đưa ra yêu cầu và RP nào đang cố gắng kết nối. Ví dụ:

  GET /config.json HTTP/1.1
  Host: accounts.idp.example
  Accept: application/json
  Sec-Fetch-Dest: webidentity

IdP phải triển khai một điểm cuối cấu hình phản hồi bằng JSON. JSON này bao gồm các thuộc tính sau:

Thuộc tính Mô tả
accounts_endpoint (bắt buộc) URL cho điểm cuối tài khoản.
account_label (không bắt buộc) Chuỗi nhãn tài khoản tuỳ chỉnh, xác định những tài khoản sẽ được trả về khi tệp cấu hình này được dùng, ví dụ: "account_label": "developer".
IdP có thể triển khai tính năng gắn nhãn tài khoản tuỳ chỉnh như sau:

Ví dụ: một IdP triển khai "https://idp.example/developer-config.json" tệp cấu hình"account_label": "developer" được chỉ định. IdP cũng đánh dấu một số tài khoản bằng nhãn "developer" bằng cách sử dụng tham số label_hints trong điểm cuối tài khoản. Khi RP gọi navigator.credentials.get() với tệp cấu hình "https://idp.example/developer-config.json" được chỉ định, chỉ những tài khoản có nhãn "developer" mới được trả về.

Lưu ý: Nhãn tài khoản tuỳ chỉnh được hỗ trợ từ Chrome 132.
supports_use_other_account (không bắt buộc) Giá trị boolean chỉ định việc người dùng có thể đăng nhập bằng một tài khoản khác với tài khoản mà họ hiện đang đăng nhập hay không (nếu IdP hỗ trợ nhiều tài khoản). Điều này chỉ áp dụng cho chế độ đang hoạt động.
client_metadata_endpoint (không bắt buộc) URL cho điểm cuối siêu dữ liệu của ứng dụng.
id_assertion_endpoint (bắt buộc) URL cho điểm cuối xác nhận mã nhận dạng.
disconnect (không bắt buộc) URL cho điểm cuối ngắt kết nối.
login_url (bắt buộc) URL trang đăng nhập để người dùng đăng nhập vào IdP.
branding (không bắt buộc) Đối tượng chứa nhiều lựa chọn về việc xây dựng thương hiệu.
branding.background_color (không bắt buộc) Lựa chọn xây dựng thương hiệu giúp đặt màu nền cho nút "Tiếp tục với...". Sử dụng cú pháp CSS có liên quan, cụ thể là hex-color, hsl(), rgb() hoặc named-color.
branding.color (không bắt buộc) Lựa chọn xây dựng thương hiệu để đặt màu văn bản cho nút "Tiếp tục với tư cách là...". Sử dụng cú pháp CSS có liên quan, cụ thể là hex-color, hsl(), rgb() hoặc named-color.
branding.icons (không bắt buộc) Mảng đối tượng biểu tượng. Các biểu tượng này xuất hiện trong hộp thoại đăng nhập. Đối tượng biểu tượng có 2 tham số:
  • url (bắt buộc): URL của hình ảnh biểu tượng. Ứng dụng này không hỗ trợ hình ảnh SVG.
  • size (không bắt buộc): Kích thước biểu tượng, được ứng dụng giả định là hình vuông và có độ phân giải duy nhất. Số này phải lớn hơn hoặc bằng 25 px ở chế độ thụ động và lớn hơn hoặc bằng 40 px ở chế độ chủ động.

Sau đây là ví dụ về nội dung phản hồi của IdP:

  {
    "accounts_endpoint": "/accounts.example",
    "client_metadata_endpoint": "/client_metadata.example",
    "id_assertion_endpoint": "/assertion.example",
    "disconnect_endpoint": "/disconnect.example",
    "login_url": "/login",
    // When RPs use this config file, only those accounts will be
    //returned that include `developer` label in the accounts endpoint.
    "account_label": "developer",
    "supports_use_other_account": true,
    "branding": {
      "background_color": "green",
      "color": "#FFEEAA",
      "icons": [{
        "url": "https://idp.example/icon.ico",
        "size": 25
      }]
    }
  }

Sau khi tìm nạp tệp cấu hình, trình duyệt sẽ gửi các yêu cầu tiếp theo đến các điểm cuối IdP:

Điểm cuối IdP
Điểm cuối IdP

Sử dụng tài khoản khác

Người dùng có thể chuyển sang một tài khoản khác với tài khoản mà họ hiện đang đăng nhập, nếu IdP hỗ trợ nhiều tài khoản hoặc thay thế tài khoản hiện có.

Để cho phép người dùng chọn tài khoản khác, IdP phải chỉ định tính năng này trong tệp cấu hình:

  {
    "accounts_endpoint" : "/accounts.example",
    "supports_use_other_account": true
  }

Điểm cuối tài khoản

Điểm cuối tài khoản của IdP trả về danh sách tài khoản mà người dùng đã đăng nhập trên IdP. Nếu IdP hỗ trợ nhiều tài khoản, thì điểm cuối này sẽ trả về tất cả các tài khoản đã đăng nhập.

Trình duyệt gửi yêu cầu GET có cookie có SameSite=None, nhưng không có tham số client_id, tiêu đề Origin hoặc tiêu đề Referer. Điều này ngăn chặn IdP một cách hiệu quả việc tìm hiểu xem người dùng đang cố gắng đăng nhập vào RP nào. Ví dụ:

  GET /accounts.example HTTP/1.1
  Host: accounts.idp.example
  Accept: application/json
  Cookie: 0x23223
  Sec-Fetch-Dest: webidentity

Sau khi nhận được yêu cầu, máy chủ phải:

  1. Xác minh rằng yêu cầu chứa một tiêu đề HTTP Sec-Fetch-Dest: webidentity.
  2. So khớp cookie phiên với mã nhận dạng của các tài khoản đã đăng nhập.
  3. Phản hồi bằng danh sách tài khoản.

Trình duyệt mong đợi một phản hồi JSON bao gồm thuộc tính accounts có một mảng thông tin tài khoản với các thuộc tính sau:

Thuộc tính Mô tả
id (bắt buộc) Mã nhận dạng riêng biệt của người dùng.
name Họ tên đầy đủ của người dùng theo ngôn ngữ và lựa chọn ưu tiên của họ.

Lưu ý: Từ Chrome 141, bạn phải dùng ít nhất một trong các tham số name, email, username hoặc tel. Trong các phiên bản Chrome trước đây, cả nameemail đều bắt buộc.
username Tên người dùng do người dùng chọn.

Lưu ý: Từ Chrome 141, bạn phải dùng ít nhất một trong các tham số name, email, username hoặc tel.
email Địa chỉ email của người dùng.

Lưu ý: Từ Chrome 141, bạn phải dùng ít nhất một trong các tham số name, email, username hoặc tel. Trong các phiên bản Chrome trước đây, cả nameemail đều bắt buộc.
tel Số điện thoại của người dùng.

Lưu ý: Từ Chrome 141, bạn phải dùng ít nhất một trong các tham số name, email, username hoặc tel.
picture (không bắt buộc) URL của hình đại diện người dùng.
given_name (không bắt buộc) Tên của người dùng.
approved_clients (không bắt buộc) Một mảng gồm các mã nhận dạng ứng dụng khách RP mà người dùng đã đăng ký.
login_hints (không bắt buộc) Một mảng gồm tất cả các loại bộ lọc có thể mà IdP hỗ trợ để chỉ định một tài khoản. RP có thể gọi navigator.credentials.get() bằng thuộc tính loginHint để chọn lọc hiển thị tài khoản đã chỉ định.
domain_hints (không bắt buộc) Một mảng gồm tất cả các miền mà tài khoản được liên kết. RP có thể gọi navigator.credentials.get() bằng thuộc tính domainHint để lọc các tài khoản.
label_hints (không bắt buộc) Mảng Nhãn tài khoản tuỳ chỉnh dạng chuỗi mà một tài khoản được liên kết.
IdP có thể triển khai tính năng gắn nhãn tài khoản tuỳ chỉnh như sau:
  • Chỉ định nhãn tài khoản trong điểm cuối tài khoản (bằng cách sử dụng tham số label_hints này).
  • Tạo một tệp cấu hình cho từng nhãn cụ thể.

Ví dụ: một IdP triển khai https://idp.example/developer-config.json tệp cấu hình"account_label": "developer" được chỉ định. IdP cũng đánh dấu một số tài khoản bằng nhãn "developer" bằng cách sử dụng tham số label_hints trong điểm cuối tài khoản. Khi một RP gọi navigator.credentials.get() với một tệp cấu hình https://idp.example/developer-config.json được chỉ định, chỉ những tài khoản có nhãn "developer" mới được trả về.

Nhãn tài khoản tuỳ chỉnh khác với Gợi ý đăng nhập và Gợi ý về miền ở chỗ chúng được máy chủ IdP duy trì hoàn toàn và RP chỉ định tệp cấu hình để sử dụng.

Lưu ý: Nhãn tài khoản tuỳ chỉnh được hỗ trợ từ Chrome 132.

Ví dụ về nội dung phản hồi:

  {
    "accounts": [{
      "id": "1234",
      "given_name": "John",
      "name": "John Doe",
      "email": "john_doe@idp.example",
      "picture": "https://idp.example/profile/123",
      // Ids of those RPs where this account can be used
      "approved_clients": ["123", "456", "789"],
      // This account has 'login_hints`. When an RP calls `navigator.credentials.get()`
      // with a `loginHint` value specified, for example, `exampleHint`, only those
      // accounts will be shown to the user whose 'login_hints' array contains the `exampleHint`.
      "login_hints": ["demo1", "exampleHint"],
      // This account is labelled. IdP can implement a specific config file for a
      // label, for example, `https://idp.example/developer-config.json`. Like that
      // RPs can filter out accounts by calling `navigator.credentials.get()` with
      // `https://idp.example/developer-config.json` config file.
      "label_hints": ["enterprise", "developer"]
    }, {
      "id": "5678",
      "given_name": "Johnny",
      "name": "Johnny",
      "email": "johnny@idp.example",
      "picture": "https://idp.example/profile/456",
      "approved_clients": ["abc", "def", "ghi"],
      "login_hints": ["demo2"],
      "domain_hints": ["@domain.example"]
    }]
  }

Nếu người dùng chưa đăng nhập, hãy phản hồi bằng HTTP 401 (Không được phép).

Danh sách tài khoản được trả về sẽ được trình duyệt sử dụng và RP sẽ không truy cập được.

Điểm cuối xác nhận danh tính

Điểm cuối xác nhận danh tính của IdP trả về một câu xác nhận cho người dùng đã đăng nhập. Khi người dùng đăng nhập vào một trang web RP bằng cách sử dụng lệnh gọi navigator.credentials.get(), trình duyệt sẽ gửi một yêu cầu POST có cookie với SameSite=None và loại nội dung application/x-www-form-urlencoded đến điểm cuối này cùng với thông tin sau:

Thuộc tính Mô tả
client_id (bắt buộc) Giá trị nhận dạng ứng dụng của RP.
account_id (bắt buộc) Mã nhận dạng duy nhất của người dùng đăng nhập.
disclosure_text_shown Trả về một chuỗi "true" hoặc "false" (thay vì giá trị boolean). Kết quả là "false" trong những trường hợp sau:
  • Nếu văn bản công bố không xuất hiện vì mã nhận dạng ứng dụng khách của RP có trong danh sách thuộc tính approved_clients của phản hồi từ điểm cuối tài khoản.
  • Nếu văn bản công bố không xuất hiện vì trình duyệt đã ghi nhận một thời điểm đăng ký trong quá khứ khi không có approved_clients.
  • Nếu tham số fields không bao gồm cả 3 trường ("name", "email" và "picture"), ví dụ: fields=[ ] hoặc fields=['name', 'picture']. Điều này là cần thiết để tương thích ngược với các phương thức triển khai cũ.

    Lưu ý: Kể từ Chrome 141, văn bản công bố có thể xuất hiện ngay cả khi giá trị disclosure_text_shown"false". Để xác minh xem văn bản công bố có xuất hiện hay không, hãy kiểm tra giá trị disclosure_shown_for.
disclosure_shown_for Liệt kê các trường mà trình duyệt đã cho người dùng thấy trong văn bản công bố để thông báo cho người dùng biết RP đang yêu cầu IdP cung cấp dữ liệu nào.
is_auto_selected Nếu tự động xác thực lại được thực hiện trên RP, thì is_auto_selected sẽ cho biết "true". Nếu không, hãy chọn "false". Điều này giúp hỗ trợ thêm các tính năng liên quan đến bảo mật. Ví dụ: một số người dùng có thể thích cấp bảo mật cao hơn, yêu cầu hoạt động trung gian rõ ràng của người dùng trong quá trình xác thực. Nếu nhận được yêu cầu mã thông báo mà không có hoạt động dàn xếp như vậy, thì IdP có thể xử lý yêu cầu theo cách khác. Ví dụ: trả về mã lỗi để RP có thể gọi lại FedCM API bằng mediation: required.
fields (không bắt buộc) Mảng gồm các chuỗi chỉ định thông tin người dùng mà RP yêu cầu IdP chia sẻ. Bạn có thể chỉ định các trường sau đây (không bắt buộc):
  • "name"
  • "username"
  • "email"
  • "tel"
  • "picture"
Trình duyệt sẽ gửi fields, disclosure_text_showndisclosure_shown_for liệt kê các trường được chỉ định trong yêu cầu POST, như trong ví dụ sau.

Lưu ý: Chrome 132 trở lên có hỗ trợ Fields API. Các trường"username" và"tel" được hỗ trợ từ Chrome 141.
params (không bắt buộc) Mọi đối tượng JSON hợp lệ cho phép chỉ định các tham số khoá-giá trị tuỳ chỉnh bổ sung, ví dụ::
  • scope: Giá trị chuỗi chứa các quyền bổ sung mà RP cần yêu cầu, ví dụ: "drive.readonly calendar.readonly"
  • nonce: Một chuỗi ngẫu nhiên do RP cung cấp để đảm bảo phản hồi được đưa ra cho yêu cầu cụ thể này. Ngăn chặn các cuộc tấn công phát lại.
  • Các thông số khoá-giá trị tuỳ chỉnh khác.
Khi trình duyệt gửi một yêu cầu POST, giá trị params sẽ được chuyển đổi tuần tự thành JSON rồi mã hoá theo tỷ lệ phần trăm.

Lưu ý: Chrome 132 trở lên hỗ trợ Parameters API.

Ví dụ về tiêu đề HTTP:

  POST /assertion.example HTTP/1.1
  Host: accounts.idp.example
  Origin: https://rp.example/
  Content-Type: application/x-www-form-urlencoded
  Cookie: 0x23223
  Sec-Fetch-Dest: webidentity

  // disclosure_text_shown is set to 'false', as the 'name' field value is missing in 'fields' array
  // params value is serialized to JSON and then percent-encoded.
  account_id=123&client_id=client1234&disclosure_text_shown=false&is_auto_selected=true&params=%22%7B%5C%22nonce%5C%22%3A%5C%22nonce-value%5C%22%7D%22.%0D%0A4&fields=email,picture&disclosure_shown_for=email,picture

Sau khi nhận được yêu cầu, máy chủ phải:

  1. Trả lời yêu cầu bằng CORS (Chia sẻ tài nguyên chéo nguồn gốc).
  2. Xác minh rằng yêu cầu chứa một tiêu đề HTTP Sec-Fetch-Dest: webidentity.
  3. So khớp tiêu đề Origin với nguồn RP do client_id xác định. Từ chối nếu các thông tin không khớp.
  4. So khớp account_id với mã nhận dạng của tài khoản đã đăng nhập. Từ chối nếu chúng không khớp.
  5. Phản hồi bằng mã thông báo. Nếu yêu cầu bị từ chối, hãy phản hồi bằng một phản hồi lỗi.

IdP có thể quyết định cách phát hành mã thông báo. Nói chung, mã thông báo này được ký bằng thông tin như mã nhận dạng tài khoản, mã nhận dạng ứng dụng, nguồn gốc của tổ chức phát hành và số chỉ dùng một lần, để RP có thể xác minh rằng mã thông báo là chính hãng.

Trình duyệt dự kiến sẽ nhận được một phản hồi JSON có thuộc tính sau:

Thuộc tính Mô tả
token Mã thông báo là một chuỗi chứa các thông tin xác nhận về quá trình xác thực.
continue_on URL chuyển hướng cho phép quy trình đăng nhập nhiều bước.

Mã thông báo được trả về sẽ được trình duyệt chuyển đến RP để RP có thể xác thực quá trình xác thực.

  {
    // IdP can respond with a token to authenticate the user
    "token": "***********"
  }

Tính năng Tiếp tục xem

IdP có thể cung cấp một URL chuyển hướng trong phản hồi điểm cuối xác nhận danh tính để bật quy trình đăng nhập nhiều bước. Điều này hữu ích khi IdP cần yêu cầu thêm thông tin hoặc quyền, ví dụ:

  • Quyền truy cập vào các tài nguyên phía máy chủ của người dùng.
  • Xác minh rằng thông tin liên hệ là thông tin mới nhất.
  • Chế độ kiểm soát của cha mẹ.

Điểm cuối xác nhận danh tính có thể trả về một thuộc tính continue_on bao gồm đường dẫn tuyệt đối hoặc tương đối đến điểm cuối xác nhận danh tính.

  {
    // In the id_assertion_endpoint, instead of returning a typical
    // "token" response, the IdP decides that it needs the user to
    // continue on a popup window:
    "continue_on": "https://idp.example/continue_on_url"
  }

Nếu phản hồi chứa tham số continue_on, một cửa sổ bật lên mới sẽ mở ra và điều hướng người dùng đến đường dẫn đã chỉ định. Sau khi người dùng tương tác với trang continue_on, IdP sẽ gọi IdentityProvider.resolve() bằng mã thông báo được truyền dưới dạng một đối số để có thể phân giải lời hứa từ lệnh gọi navigator.credentials.get() ban đầu:

  document.getElementById('example-button').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);
  });

Sau đó, trình duyệt sẽ tự động đóng cửa sổ bật lên và trả mã thông báo về cho phương thức gọi API. Lệnh gọi IdentityProvider.resolve() một lần là cách duy nhất để cửa sổ mẹ (RP) và cửa sổ bật lên (IdP) giao tiếp.
Nếu người dùng từ chối yêu cầu, IdP có thể đóng cửa sổ bằng cách gọi IdentityProvider.close().

  IdentityProvider.close();

Continuation API yêu cầu người dùng tương tác (nhấp chuột) một cách rõ ràng để hoạt động. Sau đây là cách Continuation API hoạt động với các chế độ hoà giải khác nhau:

  • chế độ passive:
    • mediation: 'optional' (mặc định): Continuation API sẽ chỉ hoạt động với một cử chỉ của người dùng, chẳng hạn như nhấp vào một nút trên trang hoặc trên giao diện người dùng FedCM. Khi quá trình xác thực lại tự động được kích hoạt mà không có thao tác của người dùng, cửa sổ bật lên sẽ không mở và lời hứa sẽ bị từ chối.
    • mediation: 'required': Luôn yêu cầu người dùng tương tác, vì vậy Continuation API luôn hoạt động.
  • chế độ đang hoạt động:
    • Bạn luôn phải kích hoạt người dùng. Continuation API luôn tương thích.

Nếu vì lý do nào đó mà người dùng đã thay đổi tài khoản của họ trong cửa sổ bật lên (ví dụ: IdP cung cấp chức năng "sử dụng tài khoản khác" hoặc trong trường hợp uỷ quyền), thì lệnh gọi resolve sẽ lấy một đối số thứ hai không bắt buộc cho phép một số trường hợp như:

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

Trả về một thông báo lỗi

id_assertion_endpoint cũng có thể trả về một phản hồi "error" (lỗi) có 2 trường không bắt buộc:

  • code: IdP có thể chọn một trong các lỗi đã biết trong danh sách lỗi được chỉ định của OAuth 2.0 (invalid_request, unauthorized_client, access_denied, server_errortemporarily_unavailable) hoặc sử dụng chuỗi tuỳ ý. Nếu là trường hợp thứ hai, Chrome sẽ hiển thị giao diện người dùng lỗi kèm theo một thông báo lỗi chung và truyền mã đến RP.
  • url: Xác định một trang web mà con người có thể đọc được, có thông tin về lỗi để cung cấp thêm thông tin về lỗi cho người dùng. Trường này hữu ích cho người dùng vì trình duyệt không thể cung cấp thông báo lỗi chi tiết trong giao diện người dùng tích hợp. Ví dụ: đường liên kết cho các bước tiếp theo hoặc thông tin liên hệ của dịch vụ khách hàng. Nếu muốn tìm hiểu thêm về thông tin chi tiết của lỗi và cách khắc phục, người dùng có thể truy cập vào trang được cung cấp từ giao diện người dùng trình duyệt để biết thêm thông tin chi tiết. URL phải thuộc cùng trang web với IdP configURL.
  // id_assertion_endpoint response
  {
    "error" : {
      "code": "access_denied",
      "url" : "https://idp.example/error?type=access_denied"
    }
  }

Nhãn tài khoản tuỳ chỉnh

Với Nhãn tài khoản tuỳ chỉnh, IdP có thể chú thích tài khoản người dùng bằng nhãn và RP có thể chọn chỉ tìm nạp tài khoản có nhãn cụ thể bằng cách chỉ định configURL cho nhãn cụ thể đó. Điều này có thể hữu ích khi một RP cần lọc tài khoản theo tiêu chí cụ thể, ví dụ: chỉ hiển thị các tài khoản dành riêng cho vai trò như "developer" hoặc "hr".

Bạn có thể lọc tương tự bằng cách sử dụng tính năng Gợi ý về miềnGợi ý đăng nhập bằng cách chỉ định các tính năng này trong lệnh gọi navigator.credentials.get(). Tuy nhiên, Nhãn tài khoản tuỳ chỉnh có thể lọc người dùng bằng cách chỉ định tệp cấu hình. Điều này đặc biệt hữu ích khi bạn sử dụng nhiều configURL. Nhãn tài khoản tuỳ chỉnh cũng khác ở chỗ chúng được cung cấp từ máy chủ IdP, thay vì từ RP, chẳng hạn như gợi ý đăng nhập hoặc miền.

Hãy cân nhắc một IdP muốn phân biệt giữa tài khoản "developer""hr". Để đạt được mục đích này, IdP cần hỗ trợ 2 configURL cho "developer""hr" tương ứng:

  • Tệp cấu hình nhà phát triển https://idp.example/developer/fedcm.json có nhãn "developer" và tệp cấu hình doanh nghiệp https://idp.example/hr/fedcm.json có nhãn "hr" như sau:
  // The developer config file at `https://idp.example/developer/fedcm.json`
  {
    "accounts_endpoint": "https://idp.example/accounts",
    "client_metadata_endpoint": "/client_metadata",
    "login_url": "https://idp.example/login",
    "id_assertion_endpoint": "/assertion",
    "account_label": "developer"
  }
  // The hr config file at `https://idp.example/hr/fedcm.json`
  {
    "accounts_endpoint": "https://idp.example/accounts",
    "client_metadata_endpoint": "/client_metadata",
    "login_url": "https://idp.example/login",
    "id_assertion_endpoint": "/assertion",
    "account_label": "hr"
  }
  • Với chế độ thiết lập như vậy, tệp well-known phải bao gồm accounts_endpointlogin_url để cho phép nhiều configURL:
  {
    "provider_urls": [ "https://idp.example/fedcm.json" ],
    "accounts_endpoint": "https://idp.example/accounts",
    "login_url": "https://idp.example/login"
  }
  • Điểm cuối tài khoản IdP chung (trong ví dụ này là https://idp.example/accounts) trả về danh sách tài khoản bao gồm một thuộc tính label_hints có nhãn được chỉ định trong một mảng cho mỗi tài khoản:
  {
  "accounts": [{
    "id": "123",
    "given_name": "John",
    "name": "John Doe",
    "email": "john_doe@idp.example",
    "picture": "https://idp.example/profile/123",
    "label_hints": ["developer"]
    }], [{
    "id": "4567",
    "given_name": "Jane",
    "name": "Jane Doe",
    "email": "jane_doe@idp.example",
    "picture": "https://idp.example/profile/4567",
    "label_hints": ["hr"]
    }]
  }

Khi muốn cho phép người dùng "hr" đăng nhập, RP có thể chỉ định configURL https://idp.example/hr/fedcm.json trong lệnh gọi navigator.credentials.get():

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

Do đó, người dùng chỉ có thể đăng nhập bằng mã nhận dạng tài khoản của 4567. Trình duyệt sẽ tự động ẩn mã nhận dạng tài khoản 123 để người dùng không được cung cấp tài khoản không được IdP hỗ trợ trên trang web này.

Các yếu tố khác cần cân nhắc:

  • Nhãn là các chuỗi. Nếu mảng label_hints hoặc trường account_label sử dụng một giá trị không phải là chuỗi, thì giá trị đó sẽ bị bỏ qua.
  • Nếu không có nhãn nào được chỉ định trong configURL, tất cả tài khoản sẽ xuất hiện trong bộ chọn tài khoản FedCM.
  • Nếu không có nhãn nào được chỉ định cho một tài khoản, thì tài khoản này sẽ chỉ xuất hiện trong bộ chọn tài khoản nếu configURL cũng không chỉ định nhãn.
  • Nếu không có tài khoản nào khớp với nhãn được yêu cầu ở chế độ thụ động (tương tự như tính năng Gợi ý về miền), hộp thoại FedCM sẽ hiển thị lời nhắc đăng nhập, cho phép người dùng đăng nhập vào tài khoản IdP. Đối với chế độ đang hoạt động, cửa sổ đăng nhập bật lên sẽ mở trực tiếp.

Ngắt kết nối điểm cuối

Bằng cách gọi IdentityCredential.disconnect(), trình duyệt sẽ gửi một yêu cầu POST trên nhiều nguồn gốc có cookie với SameSite=None và loại nội dung application/x-www-form-urlencoded đến điểm cuối ngắt kết nối này cùng với thông tin sau:

Thuộc tính Mô tả
account_hint Gợi ý về tài khoản IdP.
client_id Giá trị nhận dạng ứng dụng của RP.
  POST /disconnect.example HTTP/1.1
  Host: idp.example
  Origin: rp.example
  Content-Type: application/x-www-form-urlencoded
  Cookie: 0x123
  Sec-Fetch-Dest: webidentity

  account_hint=account456&client_id=rp123

Sau khi nhận được yêu cầu, máy chủ phải:

  1. Trả lời yêu cầu bằng CORS (Chia sẻ tài nguyên chéo nguồn gốc).
  2. Xác minh rằng yêu cầu chứa một tiêu đề HTTP Sec-Fetch-Dest: webidentity.
  3. So khớp tiêu đề Origin với nguồn RP do client_id xác định. Từ chối nếu các thông tin không khớp.
  4. So khớp account_hint với mã nhận dạng của các tài khoản đã đăng nhập.
  5. Ngắt kết nối tài khoản người dùng khỏi RP.
  6. Phản hồi trình duyệt bằng thông tin tài khoản người dùng đã xác định ở định dạng JSON.

Một ví dụ về tải trọng JSON phản hồi có dạng như sau:

  {
    "account_id": "account456"
  }

Thay vào đó, nếu IdP muốn trình duyệt ngắt kết nối tất cả các tài khoản được liên kết với RP, hãy truyền một chuỗi không khớp với bất kỳ mã nhận dạng tài khoản nào, ví dụ: "*".

Điểm cuối siêu dữ liệu của ứng dụng

Điểm cuối siêu dữ liệu của ứng dụng khách IdP trả về siêu dữ liệu của bên đáng tin cậy, chẳng hạn như chính sách quyền riêng tư, điều khoản dịch vụ và biểu tượng logo của RP. RP nên cung cấp trước cho IdP các đường liên kết đến chính sách quyền riêng tư và điều khoản dịch vụ của họ. Những đường liên kết này xuất hiện trong hộp thoại đăng nhập khi người dùng chưa đăng ký trên RP bằng IdP.

Trình duyệt gửi yêu cầu GET bằng cách sử dụng client_id navigator.credentials.get mà không có cookie. Ví dụ:

  GET /client_metadata.example?client_id=1234 HTTP/1.1
  Host: accounts.idp.example
  Origin: https://rp.example/
  Accept: application/json
  Sec-Fetch-Dest: webidentity

Sau khi nhận được yêu cầu, máy chủ phải:

  1. Xác định RP cho client_id.
  2. Trả lời bằng siêu dữ liệu của ứng dụng.

Các thuộc tính cho điểm cuối siêu dữ liệu của ứng dụng bao gồm:

Thuộc tính Mô tả
privacy_policy_url (không bắt buộc) URL chính sách quyền riêng tư của RP.
terms_of_service_url (không bắt buộc) URL của điều khoản dịch vụ RP.
icons (không bắt buộc) Mảng đối tượng, chẳng hạn như [{ "url": "https://rp.example/rp-icon.ico", "size": 40}]

Trình duyệt dự kiến nhận được một phản hồi JSON từ điểm cuối:

  {
    "privacy_policy_url": "https://rp.example/privacy_policy.html",
    "terms_of_service_url": "https://rp.example/terms_of_service.html",
    "icons": [{
          "url": "https://rp.example/rp-icon.ico",
          "size": 40
      }]
  }

Trình duyệt sẽ sử dụng siêu dữ liệu ứng dụng khách được trả về và RP sẽ không có siêu dữ liệu này.

URL đăng nhập

Điểm cuối này được dùng để cho phép người dùng đăng nhập vào IdP.

Với Login Status API, IdP phải thông báo trạng thái đăng nhập của người dùng cho trình duyệt. Tuy nhiên, trạng thái có thể không đồng bộ, chẳng hạn như khi phiên hết hạn. Trong trường hợp như vậy, trình duyệt có thể cho phép người dùng đăng nhập vào IdP một cách linh hoạt thông qua URL trang đăng nhập được chỉ định bằng login_url của tệp cấu hình idp.

Hộp thoại FedCM hiển thị một thông báo đề xuất đăng nhập, như minh hoạ trong hình sau.

Một hộp thoại FedCM đề xuất đăng nhập vào IdP.
Một hộp thoại FedCM đề xuất đăng nhập vào IdP.

Khi người dùng nhấp vào nút Tiếp tục, trình duyệt sẽ mở một cửa sổ bật lên cho trang đăng nhập của IdP.

Ví dụ về hộp thoại FedCM.
Ví dụ về hộp thoại xuất hiện sau khi nhấp vào nút đăng nhập vào IdP.

Hộp thoại này là một cửa sổ trình duyệt thông thường có cookie của bên thứ nhất. Bất cứ điều gì xảy ra trong hộp thoại đều do IdP quyết định và không có cửa sổ nào xử lý yêu cầu liên lạc trên nhiều nguồn gốc đến trang RP. Sau khi người dùng đăng nhập, IdP phải:

  • Gửi tiêu đề Set-Login: logged-in hoặc gọi API navigator.login.setStatus("logged-in") để thông báo cho trình duyệt rằng người dùng đã đăng nhập.
  • Gọi IdentityProvider.close() để đóng hộp thoại.
Người dùng đăng nhập vào một RP sau khi đăng nhập vào IdP bằng FedCM.

Thông báo cho trình duyệt về trạng thái đăng nhập của người dùng

Login Status API là một cơ chế mà qua đó một trang web (đặc biệt là một IdP) thông báo cho trình duyệt về trạng thái đăng nhập của người dùng trên IdP. Với API này, trình duyệt có thể giảm các yêu cầu không cần thiết đối với IdP và giảm thiểu các cuộc tấn công có thể xảy ra theo thời gian.

IdP có thể báo hiệu trạng thái đăng nhập của người dùng cho trình duyệt bằng cách gửi tiêu đề HTTP hoặc bằng cách gọi API JavaScript khi người dùng đăng nhập vào IdP hoặc khi người dùng đăng xuất khỏi tất cả các tài khoản IdP của họ. Đối với mỗi IdP (được xác định bằng URL cấu hình), trình duyệt sẽ giữ một biến có 3 trạng thái đại diện cho trạng thái đăng nhập với các giá trị có thể có:

  • logged-in
  • logged-out
  • unknown (mặc định)
Trạng thái đăng nhập Nội dung mô tả
logged-in Khi trạng thái đăng nhập của người dùng được đặt thành logged-in, RP gọi FedCM sẽ gửi yêu cầu đến điểm cuối tài khoản của IdP và hiển thị các tài khoản có sẵn cho người dùng trong hộp thoại FedCM.
logged-out Khi trạng thái đăng nhập của người dùng là logged-out, việc gọi FedCM sẽ âm thầm không thành công mà không đưa ra yêu cầu đến điểm cuối tài khoản của IdP.
unknown (mặc định) Trạng thái unknown được đặt trước khi IdP gửi tín hiệu bằng Login Status API. Khi trạng thái là unknown, trình duyệt sẽ đưa ra yêu cầu đến điểm cuối tài khoản của IdP và cập nhật trạng thái dựa trên phản hồi từ điểm cuối tài khoản.

Để báo hiệu rằng người dùng đã đăng nhập, hãy gửi tiêu đề HTTP Set-Login: logged-in trong một yêu cầu điều hướng cấp cao nhất hoặc yêu cầu tài nguyên phụ cùng trang tại nguồn gốc IdP:

  Set-Login: logged-in

Ngoài ra, hãy gọi phương thức JavaScript navigator.login.setStatus('logged-in') từ nguồn gốc IdP trong một thao tác điều hướng cấp cao nhất:

  navigator.login.setStatus('logged-in')

Trạng thái đăng nhập của người dùng sẽ được đặt là logged-in.

Để báo hiệu rằng người dùng đã đăng xuất khỏi tất cả tài khoản của họ, hãy gửi tiêu đề HTTP Set-Login: logged-out trong một yêu cầu điều hướng cấp cao nhất hoặc yêu cầu tài nguyên phụ cùng trang web tại nguồn IdP:

  Set-Login: logged-out

Ngoài ra, hãy gọi API JavaScript navigator.login.setStatus('logged-out') từ nguồn IdP trong một thao tác điều hướng cấp cao nhất:

  navigator.login.setStatus('logged-out')

Trạng thái đăng nhập của người dùng sẽ được đặt là logged-out.

Trạng thái unknown được đặt trước khi IdP gửi tín hiệu bằng Login Status API. Trình duyệt gửi yêu cầu đến điểm cuối tài khoản của IdP và cập nhật trạng thái dựa trên phản hồi từ điểm cuối tài khoản:

  • Nếu điểm cuối trả về danh sách các tài khoản đang hoạt động, hãy cập nhật trạng thái thành logged-in và mở hộp thoại FedCM để hiện các tài khoản đó.
  • Nếu điểm cuối không trả về tài khoản nào, hãy cập nhật trạng thái thành logged-out và không thực hiện được lệnh gọi FedCM.

Cho phép người dùng đăng nhập thông qua một quy trình đăng nhập linh động

Mặc dù IdP liên tục thông báo trạng thái đăng nhập của người dùng cho trình duyệt, nhưng trạng thái này có thể không đồng bộ, chẳng hạn như khi phiên hết hạn. Trình duyệt cố gắng gửi một yêu cầu có thông tin đăng nhập đến điểm cuối tài khoản khi trạng thái đăng nhập là logged-in, nhưng máy chủ không trả về tài khoản nào vì phiên không còn hoạt động. Trong trường hợp như vậy, trình duyệt có thể cho phép người dùng đăng nhập vào IdP một cách linh hoạt thông qua một cửa sổ bật lên.

Các bước tiếp theo

Triển khai FedCM cho RP và phân phối SDK JavaScript. Luôn cập nhật RP mà không cần tự triển khai.
Tìm hiểu cách thiết lập môi trường và gỡ lỗi quá trình triển khai.