یک راه حل هویت با FedCM در سمت ارائه دهنده هویت پیاده سازی کنید

پیاده‌سازی FedCM شامل چندین مرحله اصلی برای هر دو طرف ارائه‌دهنده هویت (IdP) و طرف متکی (RP) است. برای آشنایی با نحوه پیاده‌سازی FedCM در سمت RP ، به مستندات مراجعه کنید.

IdPها باید مراحل زیر را برای پیاده‌سازی FedCM انجام دهند:

  1. یک فایل شناخته شده ایجاد کنید.
  2. یک فایل پیکربندی ایجاد کنید.
  3. نقاط پایانی زیر را ایجاد کنید:
  4. وضعیت ورود کاربر را به مرورگر اطلاع دهید.

یک فایل شناخته شده ایجاد کنید

برای جلوگیری از سوءاستفاده ردیاب‌ها از API ، باید یک فایل شناخته‌شده از /.well-known/web-identity مربوط به eTLD+1 مربوط به IdP ارائه شود.

فایل شناخته شده می‌تواند شامل ویژگی‌های زیر باشد:

ملک مورد نیاز توضیحات
provider_urls مورد نیاز آرایه‌ای از مسیرهای فایل پیکربندی IdP. در صورت مشخص شدن accounts_endpoint و login_url ، نادیده گرفته می‌شود (اما همچنان لازم است).
accounts_endpoint توصیه می‌شود، نیاز به login_url دارد
URL برای نقطه پایانی حساب‌ها. این امکان پشتیبانی از پیکربندی چندگانه را فراهم می‌کند، مادامی که هر فایل پیکربندی از همان login_url و accounts_endpoint URL استفاده کند.

توجه: این پارامتر از Chrome 132 پشتیبانی می‌شود.
login_url توصیه می‌شود، نیاز به accounts_endpoint دارد آدرس اینترنتی صفحه ورود برای کاربر جهت ورود به IdP. این امکان پشتیبانی از پیکربندی‌های متعدد را فراهم می‌کند، مادامی که هر فایل پیکربندی login_url و accounts_endpoint یکسانی استفاده کند.

توجه: این پارامتر از کروم ۱۳۲ به بعد پشتیبانی می‌شود.

برای مثال، اگر نقاط پایانی IdP تحت https://accounts.idp.example/ ارائه می‌شوند، باید علاوه بر یک فایل پیکربندی IdP ، یک فایل شناخته‌شده در https://idp.example/.well-known/web-identity را نیز ارائه دهند. در اینجا نمونه‌ای از محتوای فایل شناخته‌شده آورده شده است:

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

IdPها می‌توانند با مشخص کردن accounts_endpoint و login_url در فایل شناخته‌شده، چندین فایل پیکربندی را برای یک IdP در خود جای دهند. این ویژگی می‌تواند در این موارد مفید باشد:

  • یک IdP باید از چندین پیکربندی مختلف تست و تولید پشتیبانی کند.
  • یک IdP باید از پیکربندی‌های مختلف در هر منطقه پشتیبانی کند (برای مثال، eu-idp.example و us-idp.example ).

برای پشتیبانی از پیکربندی‌های چندگانه (برای مثال، برای تمایز قائل شدن بین محیط تست و محیط عملیاتی)، IdP باید accounts_endpoint و login_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"
  }

ایجاد فایل پیکربندی IdP و نقاط پایانی

فایل پیکربندی IdP فهرستی از نقاط پایانی مورد نیاز برای مرورگر را ارائه می‌دهد. IdPها باید میزبان یک یا چند فایل پیکربندی و نقاط پایانی و URLهای مورد نیاز باشند. تمام پاسخ‌های JSON باید با نوع محتوای application/json ارائه شوند.

آدرس اینترنتی (URL) فایل پیکربندی توسط مقادیر ارائه شده به فراخوانی navigator.credentials.get() که روی یک RP اجرا می‌شود، تعیین می‌شود. RP آدرس اینترنتی فایل پیکربندی را برای هر ارائه‌دهنده هویت ارسال می‌کند:

  // 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
  }

مرورگر فایل پیکربندی را با یک درخواست GET بدون هدر Origin یا هدر Referer دریافت می‌کند. این درخواست کوکی ندارد و از ریدایرکت‌ها پیروی نمی‌کند. این امر عملاً مانع از آن می‌شود که IdP متوجه شود چه کسی درخواست را ارسال کرده و کدام RP سعی در اتصال دارد. به عنوان مثال:

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

IdP باید یک نقطه پایانی پیکربندی پیاده‌سازی کند که با JSON پاسخ دهد. JSON شامل ویژگی‌های زیر است:

ملک توضیحات
accounts_endpoint (الزامی) آدرس اینترنتی (URL) برای نقطه پایانی حساب‌ها .
account_label (اختیاری) رشته برچسب حساب سفارشی، تعیین می‌کند که کدام حساب‌ها هنگام استفاده از این فایل پیکربندی باید بازگردانده شوند، برای مثال: "account_label": "developer" .
یک IdP می‌تواند برچسب‌گذاری حساب سفارشی را به صورت زیر پیاده‌سازی کند:

برای مثال، یک IdP فایل پیکربندی "https://idp.example/developer-config.json" را با مشخص کردن "account_label": "developer" پیاده‌سازی می‌کند. IdP همچنین برخی از حساب‌ها را با استفاده از پارامتر label_hints در نقطه پایانی حساب‌ها ، با برچسب "developer" علامت‌گذاری می‌کند. هنگامی که یک RP تابع navigator.credentials.get() را با مشخص کردن فایل پیکربندی "https://idp.example/developer-config.json" فراخوانی می‌کند، فقط حساب‌هایی با برچسب "developer" بازگردانده می‌شوند.

توجه: برچسب‌های حساب کاربری سفارشی از کروم ۱۳۲ پشتیبانی می‌شوند.
supports_use_other_account (اختیاری) مقدار بولی که مشخص می‌کند آیا کاربر می‌تواند با حسابی غیر از حسابی که با آن وارد سیستم شده است، وارد سیستم شود یا خیر (اگر IdP از چندین حساب پشتیبانی کند). این فقط در حالت فعال اعمال می‌شود.
client_metadata_endpoint (اختیاری) URL برای نقطه پایانی فراداده کلاینت .
id_assertion_endpoint (الزامی) آدرس اینترنتی (URL) برای نقطه پایانیِ ادعای شناسه .
disconnect (اختیاری) نشانی اینترنتی (URL) برای نقطه پایانی قطع ارتباط .
login_url (الزامی) آدرس اینترنتی صفحه ورود برای کاربر جهت ورود به IdP.
branding (اختیاری) شیء که شامل گزینه‌های مختلف برندسازی است.
branding.background_color (اختیاری) گزینه‌ی Branding که رنگ پس‌زمینه‌ی دکمه‌ی «ادامه به عنوان...» را تنظیم می‌کند. از سینتکس CSS مربوطه، یعنی hex-color ، hsl() ، rgb() یا named-color استفاده کنید.
branding.color (اختیاری) گزینه‌ی برندسازی که رنگ متن دکمه‌ی «ادامه به عنوان...» را تنظیم می‌کند. از سینتکس CSS مربوطه، یعنی hex-color ، hsl() ، rgb() یا named-color استفاده کنید.
branding.icons (اختیاری) آرایه‌ای از اشیاء آیکون. این آیکون‌ها در پنجره‌ی ورود نمایش داده می‌شوند. شیء آیکون دو پارامتر دارد:
  • url (الزامی): آدرس اینترنتی تصویر آیکون. این از تصاویر SVG پشتیبانی نمی‌کند.
  • size (اختیاری): ابعاد آیکون، که توسط برنامه به صورت مربع و با وضوح تصویر تکی فرض می‌شود. این عدد باید در حالت غیرفعال بزرگتر یا مساوی ۲۵ پیکسل و در حالت فعال بزرگتر یا مساوی ۴۰ پیکسل باشد.

در اینجا یک نمونه از بدنه پاسخ از 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
      }]
    }
  }

هنگامی که مرورگر فایل پیکربندی را دریافت می‌کند، درخواست‌های بعدی را به نقاط انتهایی IdP ارسال می‌کند:

نقاط پایانی IdP
نقاط پایانی IdP

استفاده از حساب کاربری دیگر

اگر IdP از چندین حساب کاربری یا جایگزینی حساب موجود پشتیبانی کند، کاربران می‌توانند به حسابی غیر از حسابی که با آن وارد سیستم شده‌اند، تغییر دهند.

برای اینکه کاربر بتواند حساب‌های کاربری دیگری را انتخاب کند، IdP باید این ویژگی را در فایل پیکربندی مشخص کند:

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

نقطه پایانی حساب‌ها

نقطه پایانی حساب‌های IdP فهرستی از حساب‌هایی را که کاربر در IdP وارد آنها شده است، برمی‌گرداند. اگر IdP از چندین حساب پشتیبانی کند، این نقطه پایانی تمام حساب‌های وارد شده را برمی‌گرداند.

مرورگر یک درخواست GET با کوکی‌هایی با SameSite=None ارسال می‌کند، اما بدون پارامتر client_id ، هدر Origin یا هدر Referer . این امر عملاً مانع از آن می‌شود که IdP بفهمد کاربر در کدام RP قصد ورود به سیستم را دارد. برای مثال:

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

پس از دریافت درخواست، سرور باید:

  1. تأیید کنید که درخواست حاوی سرآیند HTTP Sec-Fetch-Dest: webidentity باشد.
  2. کوکی‌های جلسه را با شناسه‌های حساب‌های کاربری که از قبل وارد سیستم شده‌اند، مطابقت دهید.
  3. با لیست حساب‌ها پاسخ دهید.

مرورگر انتظار یک پاسخ JSON را دارد که شامل یک ویژگی accounts با آرایه‌ای از اطلاعات حساب با ویژگی‌های زیر است:

ملک توضیحات
id (الزامی) شناسه منحصر به فرد کاربر.
name نام کامل کاربر بر اساس زبان و تنظیمات برگزیده او.

توجه: از کروم ۱۴۱، حداقل یکی از پارامترهای name ، email ، username یا tel مورد نیاز است. در نسخه‌های قبلی کروم، هم name و هم email مورد نیاز هستند.
username نام کاربری که توسط کاربر انتخاب می‌شود.

توجه: از کروم ۱۴۱، حداقل یکی از پارامترهای name ، email ، username یا tel الزامی است.
email آدرس ایمیل کاربر.

توجه: از کروم ۱۴۱، حداقل یکی از پارامترهای name ، email ، username یا tel مورد نیاز است. در نسخه‌های قبلی کروم، هم name و هم email مورد نیاز هستند.
tel شماره تلفن کاربر.

توجه: از کروم ۱۴۱، حداقل یکی از پارامترهای name ، email ، username یا tel الزامی است.
picture (اختیاری) آدرس اینترنتی (URL) تصویر آواتار کاربر.
given_name (اختیاری) نام داده شده به کاربر.
approved_clients (اختیاری) آرایه‌ای از شناسه‌های کلاینت RP که کاربر با آنها ثبت‌نام کرده است.
login_hints (اختیاری) آرایه‌ای از تمام انواع فیلترهای ممکن که IdP برای مشخص کردن یک حساب کاربری پشتیبانی می‌کند. RP می‌تواند navigator.credentials.get() را با ویژگی loginHint فراخوانی کند تا به صورت انتخابی حساب کاربری مشخص شده را نمایش دهد.
domain_hints (اختیاری) آرایه‌ای از تمام دامنه‌هایی که حساب به آنها مرتبط است. RP می‌تواند navigator.credentials.get() را با یک ویژگی domainHint برای فیلتر کردن حساب‌ها فراخوانی کند.
label_hints (اختیاری) آرایه‌ای از رشته‌ها، برچسب‌های حساب سفارشی که یک حساب به آنها مرتبط است.
یک IdP می‌تواند برچسب‌گذاری حساب سفارشی را به صورت زیر پیاده‌سازی کند:
  • برچسب‌های حساب را در نقطه پایانی حساب‌ها (با استفاده از این پارامتر label_hints ) مشخص کنید.
  • برای هر برچسب خاص، یک فایل پیکربندی ایجاد کنید.

برای مثال، یک IdP فایل پیکربندی https://idp.example/developer-config.json را با مشخص کردن "account_label": "developer" پیاده‌سازی می‌کند. IdP همچنین برخی از حساب‌ها را با استفاده از پارامتر label_hints در نقطه پایانی حساب‌ها با برچسب "developer" علامت‌گذاری می‌کند. هنگامی که یک RP تابع navigator.credentials.get() را با فایل پیکربندی https://idp.example/developer-config.json مشخص شده فراخوانی می‌کند، فقط حساب‌هایی با برچسب "developer" بازگردانده می‌شوند.

برچسب‌های حساب کاربری سفارشی با Login Hint و Domain Hint متفاوت هستند، به این صورت که کاملاً توسط سرور IdP نگهداری می‌شوند و RP فقط فایل پیکربندی مورد استفاده را مشخص می‌کند.

توجه: برچسب‌های حساب کاربری سفارشی از کروم ۱۳۲ پشتیبانی می‌شوند.

نمونه بدنه پاسخ:

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

اگر کاربر وارد سیستم نشده باشد، با HTTP 401 (غیرمجاز) پاسخ دهید.

لیست حساب‌های برگردانده شده توسط مرورگر مصرف می‌شود و برای RP در دسترس نخواهد بود.

نقطه پایانی ادعای شناسه

نقطه پایانیِ ادعای شناسه‌ی IdP، یک ادعا برای کاربر وارد شده به سیستم برمی‌گرداند. وقتی کاربر با استفاده از فراخوانی navigator.credentials.get() وارد یک وب‌سایت RP می‌شود، مرورگر یک درخواست POST با کوکی‌هایی با SameSite=None و یک نوع محتوا از application/x-www-form-urlencoded به این نقطه پایانی با اطلاعات زیر ارسال می‌کند:

ملک توضیحات
client_id (الزامی) شناسه کلاینت RP.
account_id (الزامی) شناسه منحصر به فرد کاربر در حال ورود.
disclosure_text_shown نتیجه رشته‌ای از "true" یا "false" (به جای یک مقدار بولی) است. در این موارد نتیجه "false" است:
  • اگر متن افشا به دلیل وجود شناسه کلاینت RP در لیست ویژگی approved_clients پاسخ از نقطه پایانی accounts نمایش داده نشده باشد.
  • اگر متن افشا نشان داده نشده باشد، زیرا مرورگر در گذشته لحظه ثبت نامی را در غیاب approved_clients مشاهده کرده است.
  • اگر پارامتر fields شامل هر سه فیلد ("name"، "email" و "picture") نباشد، برای مثال، fields=[ ] یا fields=['name', 'picture'] . این برای سازگاری با پیاده‌سازی‌های قدیمی‌تر مورد نیاز است.

    توجه: از کروم ۱۴۱، متن افشا ممکن است نمایش داده شود، حتی اگر مقدار disclosure_text_shown برابر با "false" باشد. برای تأیید اینکه آیا متن افشا نمایش داده شده است، به جای آن مقدار disclosure_shown_for بررسی کنید.
disclosure_shown_for فیلدهایی را که مرورگر در متن افشا به کاربر نشان داده است، فهرست می‌کند تا به کاربر اطلاع دهد که RP کدام داده‌ها را از IdP درخواست می‌کند.
is_auto_selected اگر احراز هویت مجدد خودکار روی RP انجام شود، is_auto_selected "true" را نشان می‌دهد. در غیر این صورت "false" . این برای پشتیبانی از ویژگی‌های امنیتی بیشتر مفید است. به عنوان مثال، برخی از کاربران ممکن است سطح امنیتی بالاتری را ترجیح دهند که نیاز به میانجیگری صریح کاربر در احراز هویت دارد. اگر یک IdP درخواست توکن را بدون چنین میانجیگری دریافت کند، می‌تواند درخواست را به طور متفاوتی مدیریت کند. به عنوان مثال، یک کد خطا را طوری برگرداند که RP بتواند API FedCM را دوباره با mediation: required .
fields (اختیاری) آرایه‌ای از رشته‌ها که اطلاعات کاربری را که RP از IdP درخواست اشتراک‌گذاری آن را کرده است، مشخص می‌کند. فیلدهای زیر می‌توانند به صورت اختیاری مشخص شوند:
  • "name"
  • "username"
  • "email"
  • "tel"
  • "picture"
مرورگر، fields ، disclosure_text_shown و disclosure_shown_for را برای فهرست کردن فیلدهای مشخص شده در درخواست POST، مانند مثال زیر ، ارسال خواهد کرد.

نکته: رابط برنامه‌نویسی کاربردی فیلدها (Fields API) توسط کروم ۱۳۲ و بالاتر پشتیبانی می‌شود. فیلدهای «نام کاربری» (username) و «تلفن» (tel) از کروم ۱۴۱ پشتیبانی می‌شوند.
params (اختیاری) هر شیء JSON معتبری که امکان تعیین پارامترهای کلید-مقدار سفارشی اضافی را فراهم می‌کند، برای مثال:
  • scope : یک مقدار رشته‌ای حاوی مجوزهای اضافی که RP باید درخواست کند، برای مثال "drive.readonly calendar.readonly"
  • nonce : یک رشته تصادفی که توسط RP ارائه می‌شود تا اطمینان حاصل شود که پاسخ برای این درخواست خاص صادر می‌شود. از حملات بازپخش جلوگیری می‌کند.
  • سایر پارامترهای کلید-مقدار سفارشی.
وقتی مرورگر یک درخواست POST ارسال می‌کند، مقدار params به JSON سریالایز شده و سپس به صورت percent-encoded نمایش داده می‌شود.

توجه: API پارامترها توسط کروم ۱۳۲ و بالاتر پشتیبانی می‌شود.

مثال هدر 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

پس از دریافت درخواست، سرور باید:

  1. با CORS (اشتراک‌گذاری منابع بین مبدائی) به درخواست پاسخ دهید.
  2. تأیید کنید که درخواست حاوی سرآیند HTTP Sec-Fetch-Dest: webidentity باشد.
  3. هدر Origin را با مبدا RP تعیین شده توسط client_id مطابقت دهید. در صورت عدم تطابق، رد کنید.
  4. شناسه account_id با شناسه حساب کاربری که از قبل وارد سیستم شده است، مطابقت دهید. در صورت عدم تطابق، آن را رد کنید.
  5. با یک توکن پاسخ دهید. اگر درخواست رد شد، با یک پاسخ خطا پاسخ دهید.

IdP می‌تواند تصمیم بگیرد که چگونه توکن را صادر کند. به طور کلی، با اطلاعاتی مانند شناسه حساب، شناسه مشتری، مبدا صادرکننده و nonce امضا می‌شود تا RP بتواند اصالت توکن را تأیید کند.

مرورگر انتظار یک پاسخ JSON شامل ویژگی زیر را دارد:

ملک توضیحات
token توکن رشته‌ای است که شامل ادعاهایی در مورد احراز هویت است.
continue_on آدرس اینترنتی (URL) ریدایرکت که امکان ورود چند مرحله‌ای را فراهم می‌کند.

توکن برگردانده شده توسط مرورگر به RP ارسال می‌شود تا RP بتواند احراز هویت را تأیید کند.

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

ادامه در ویژگی

IdP می‌تواند یک URL تغییر مسیر در پاسخ نقطه پایانی ادعای شناسه ارائه دهد تا جریان ورود چند مرحله‌ای را فعال کند. این زمانی مفید است که IdP نیاز به درخواست اطلاعات یا مجوزهای اضافی داشته باشد، به عنوان مثال:

  • اجازه دسترسی به منابع سمت سرور کاربر.
  • تأیید به‌روز بودن اطلاعات تماس.
  • کنترل‌های والدین.

نقطه پایانیِ ادعای شناسه می‌تواند یک ویژگی 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 dialog window:
    "continue_on": "https://idp.example/continue_on_url"
  }

اگر پاسخ شامل پارامتر continue_on باشد، یک پنجره محاوره‌ای جدید باز می‌شود و کاربر را به مسیر مشخص شده هدایت می‌کند. پس از تعامل کاربر با صفحه continue_on ، IdP باید IdentityProvider.resolve() با توکن ارسالی به عنوان آرگومان فراخوانی کند تا promise حاصل از فراخوانی navigator.credentials.get() اصلی بتواند حل شود:

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

سپس مرورگر به طور خودکار پنجره گفتگو را می‌بندد و توکن را به فراخواننده API برمی‌گرداند. فراخوانی یک‌باره IdentityProvider.resolve() تنها راه ارتباط پنجره والد (RP) و پنجره گفتگو (IdP) است.
اگر کاربر درخواست را رد کند، IdP می‌تواند با فراخوانی IdentityProvider.close() پنجره را ببندد.

  IdentityProvider.close();

API Continuation برای عملکرد خود به تعامل صریح کاربر (کلیک) نیاز دارد. در اینجا نحوه عملکرد API Continuation با حالت‌های مختلف میانجیگری آمده است:

  • در حالت passive :
    • mediation: 'optional' (پیش‌فرض): API Continuation فقط با یک حرکت کاربر، مانند کلیک روی یک دکمه در صفحه یا در رابط کاربری FedCM، کار خواهد کرد. هنگامی که احراز هویت مجدد خودکار بدون حرکت کاربر فعال می‌شود، هیچ پنجره محاوره‌ای باز نمی‌شود و promise رد می‌شود.
    • mediation: 'required' : همیشه از کاربر می‌خواهد که تعامل داشته باشد، بنابراین API Continuation همیشه کار می‌کند.
  • در حالت فعال :
    • فعال‌سازی کاربر همیشه الزامی است. API Continuation همیشه سازگار است.

اگر به هر دلیلی کاربر حساب کاربری خود را در کادر محاوره‌ای تغییر داده باشد (برای مثال، IdP تابع "استفاده از حساب کاربری دیگر" را ارائه می‌دهد، یا در موارد واگذاری اختیار)، فراخوانی resolve یک آرگومان دوم اختیاری می‌گیرد که امکان انجام کاری مانند موارد زیر را فراهم می‌کند:

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

پاسخ خطا را برگردانید

id_assertion_endpoint همچنین می‌تواند یک پاسخ "خطا" را برگرداند که دارای دو فیلد اختیاری است:

  • code : IdP می‌تواند یکی از خطاهای شناخته‌شده را از لیست خطاهای مشخص‌شده توسط OAuth 2.0 ( invalid_request ، unauthorized_client ، access_denied ، server_error و temporarily_unavailable ) انتخاب کند یا از هر رشته دلخواهی استفاده کند. در صورت انتخاب مورد دوم، کروم رابط کاربری خطا را با یک پیام خطای عمومی رندر کرده و کد را به RP ارسال می‌کند.
  • url : این فیلد یک صفحه وب قابل خواندن توسط انسان را با اطلاعاتی در مورد خطا شناسایی می‌کند تا اطلاعات بیشتری در مورد خطا به کاربران ارائه دهد. این فیلد برای کاربران مفید است زیرا مرورگرها نمی‌توانند پیام‌های خطای غنی را در یک رابط کاربری داخلی ارائه دهند. به عنوان مثال: لینک‌هایی برای مراحل بعدی یا اطلاعات تماس خدمات مشتری. اگر کاربری بخواهد در مورد جزئیات خطا و نحوه رفع آن اطلاعات بیشتری کسب کند، می‌تواند برای جزئیات بیشتر به صفحه ارائه شده از رابط کاربری مرورگر مراجعه کند. URL باید از همان سایت IdP configURL باشد.
  // id_assertion_endpoint response
  {
    "error" : {
      "code": "access_denied",
      "url" : "https://idp.example/error?type=access_denied"
    }
  }

برچسب‌های حساب سفارشی

با برچسب‌های حساب کاربری سفارشی ، IdP می‌تواند حساب‌های کاربری را با برچسب‌ها حاشیه‌نویسی کند و RP می‌تواند با مشخص کردن configURL برای آن برچسب خاص، فقط حساب‌هایی با برچسب‌های خاص را دریافت کند. این می‌تواند زمانی مفید باشد که یک RP نیاز به فیلتر کردن حساب‌ها بر اساس معیارهای خاص داشته باشد، به عنوان مثال، فقط حساب‌های خاص نقش مانند "developer" یا "hr" را نمایش دهد.

فیلترینگ مشابهی با استفاده از ویژگی‌های Domain Hint و Login Hint ، با مشخص کردن آنها در فراخوانی navigator.credentials.get() امکان‌پذیر است. با این حال، برچسب‌های حساب کاربری سفارشی می‌توانند کاربران را با مشخص کردن فایل پیکربندی فیلتر کنند، که به ویژه هنگامی که از چندین configURL استفاده می‌شود، مفید است. برچسب‌های حساب کاربری سفارشی همچنین از این نظر متفاوت هستند که از سرور IdP ارائه می‌شوند، برخلاف RP، مانند login یا domain hints.

یک IdP را در نظر بگیرید که می‌خواهد بین حساب‌های "developer" و "hr" تمایز قائل شود. برای دستیابی به این هدف، IdP باید از دو configURL به ترتیب برای "developer" و "hr" پشتیبانی کند:

  • فایل پیکربندی توسعه‌دهنده https://idp.example/developer/fedcm.json دارای برچسب "developer" و فایل پیکربندی سازمانی https://idp.example/hr/fedcm.json دارای برچسب "hr" به شرح زیر است:
  // 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"
  }
  • با چنین تنظیماتی، فایل شناخته‌شده باید شامل accounts_endpoint و login_url باشد تا امکان پیکربندی چندین configURL فراهم شود:
  {
    "provider_urls": [ "https://idp.example/fedcm.json" ],
    "accounts_endpoint": "https://idp.example/accounts",
    "login_url": "https://idp.example/login"
  }
  • نقطه پایانی مشترک حساب‌های IdP (در این مثال https://idp.example/accounts ) لیستی از حساب‌ها را برمی‌گرداند که شامل یک ویژگی label_hints با برچسب‌های اختصاص داده شده در یک آرایه برای هر حساب است:
  {
  "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"]
    }]
  }

وقتی یک RP می‌خواهد به کاربران "hr" اجازه ورود به سیستم را بدهد، می‌تواند configURL https://idp.example/hr/fedcm.json را در فراخوانی navigator.credentials.get() مشخص کند:

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

در نتیجه، فقط شناسه حساب 4567 برای ورود کاربر در دسترس است. شناسه حساب 123 به طور مخفیانه توسط مرورگر پنهان می‌شود تا کاربر حسابی که توسط IdP در این سایت پشتیبانی نمی‌شود، دریافت نکند.

ملاحظات اضافی:

  • برچسب‌ها رشته هستند. اگر آرایه label_hints یا فیلد account_label از مقداری غیر از رشته استفاده کند، آن مقدار نادیده گرفته می‌شود.
  • اگر هیچ برچسبی در configURL مشخص نشده باشد، همه حساب‌ها در انتخابگر حساب FedCM نمایش داده می‌شوند.
  • اگر هیچ برچسبی برای یک حساب کاربری مشخص نشده باشد، این حساب کاربری فقط در انتخابگر حساب کاربری نمایش داده می‌شود، در صورتی که configURL نیز برچسبی مشخص نکرده باشد.
  • اگر در حالت غیرفعال (مشابه ویژگی Domain Hint) هیچ حسابی با برچسب درخواستی مطابقت نداشته باشد، پنجره‌ی FedCM یک اعلان ورود به سیستم نشان می‌دهد که به کاربر اجازه می‌دهد وارد یک حساب IdP شود. برای حالت فعال، پنجره‌ی ورود به سیستم مستقیماً باز می‌شود.

قطع اتصال نقطه پایانی

با فراخوانی IdentityCredential.disconnect() ، مرورگر یک درخواست POST از نوع cross-origin با کوکی‌هایی با SameSite=None و نوع محتوای application/x-www-form-urlencoded به این نقطه پایانی disconnect با اطلاعات زیر ارسال می‌کند:

ملک توضیحات
account_hint یک راهنمایی برای حساب IdP..
client_id شناسه کلاینت 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

پس از دریافت درخواست، سرور باید:

  1. با CORS (اشتراک‌گذاری منابع بین مبدائی) به درخواست پاسخ دهید.
  2. تأیید کنید که درخواست حاوی سرآیند HTTP Sec-Fetch-Dest: webidentity باشد.
  3. هدر Origin را با مبدا RP تعیین شده توسط client_id مطابقت دهید. در صورت عدم تطابق، رد کنید.
  4. account_hint با شناسه‌های حساب‌های کاربری که از قبل وارد سیستم شده‌اند، مطابقت بده.
  5. حساب کاربری را از RP جدا کنید.
  6. اطلاعات حساب کاربری شناسایی‌شده را با فرمت JSON به مرورگر پاسخ دهید.

یک نمونه از پاسخ JSON payload به این شکل است:

  {
    "account_id": "account456"
  }

در عوض، اگر IdP بخواهد مرورگر تمام حساب‌های مرتبط با RP را قطع کند، رشته‌ای را ارسال کنید که با هیچ شناسه حسابی مطابقت نداشته باشد، به عنوان مثال "*" .

نقطه پایانی فراداده کلاینت

نقطه پایانی فراداده کلاینت IdP، فراداده‌های طرف اتکاکننده مانند سیاست حفظ حریم خصوصی، شرایط خدمات و نمادهای لوگوی RP را برمی‌گرداند. RPها باید از قبل پیوندهایی به سیاست حفظ حریم خصوصی و شرایط خدمات خود را به IdP ارائه دهند. این پیوندها در پنجره ورود به سیستم نمایش داده می‌شوند، زمانی که کاربر هنوز در RP با IdP ثبت نام نکرده باشد.

مرورگر یک درخواست GET با استفاده از client_id navigator.credentials.get بدون کوکی‌ها ارسال می‌کند. برای مثال:

  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

پس از دریافت درخواست، سرور باید:

  1. RP مربوط به client_id را تعیین کنید.
  2. با استفاده از فراداده‌های کلاینت پاسخ دهید.

ویژگی‌های مربوط به نقطه پایانی فراداده کلاینت عبارتند از:

ملک توضیحات
privacy_policy_url (اختیاری) آدرس اینترنتی سیاست حفظ حریم خصوصی RP.
terms_of_service_url (اختیاری) آدرس اینترنتی شرایط خدمات RP.
icons (اختیاری) آرایه‌ای از اشیاء، مانند [{ "url": "https://rp.example/rp-icon.ico", "size": 40}]

مرورگر انتظار یک پاسخ JSON از نقطه پایانی را دارد:

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

متادیتای کلاینت بازگردانده شده توسط مرورگر مصرف می‌شود و برای RP در دسترس نخواهد بود.

آدرس ورود

این نقطه پایانی برای ورود کاربر به IdP استفاده می‌شود.

با استفاده از API وضعیت ورود ، IdP باید وضعیت ورود کاربر را به مرورگر اطلاع دهد. با این حال، وضعیت می‌تواند ناهمگام باشد، مانند زمانی که جلسه منقضی می‌شود . در چنین سناریویی، مرورگر می‌تواند به صورت پویا به کاربر اجازه دهد از طریق URL صفحه ورود مشخص شده با login_url فایل پیکربندی idp، وارد IdP شود.

همانطور که در تصویر زیر نشان داده شده است، پنجره‌ی FedCM پیامی را نمایش می‌دهد که پیشنهاد ورود به سیستم را می‌دهد.

یک پنجره‌ی محاوره‌ای FedCM که پیشنهاد ورود به IdP را می‌دهد.
یک پنجره‌ی محاوره‌ای FedCM که پیشنهاد ورود به IdP را می‌دهد.

وقتی کاربر روی دکمه ادامه کلیک می‌کند، مرورگر یک پنجره محاوره‌ای برای صفحه ورود IdP باز می‌کند.

یک نمونه پنجره محاوره‌ای FedCM.
یک نمونه پنجره محاوره‌ای که پس از کلیک روی دکمه ورود به سیستم IdP نشان داده شده است.

این پنجره‌ی محاوره‌ای، یک پنجره‌ی مرورگر معمولی است که کوکی‌های شخص اول را در خود جای داده است. هر اتفاقی که در این پنجره رخ دهد، به IdP بستگی دارد و هیچ کنترل پنجره‌ای برای ارسال درخواست ارتباط بین مبدا و مقصد به صفحه‌ی RP در دسترس نیست. پس از ورود کاربر، IdP باید:

  • هدر Set-Login: logged-in ارسال کنید یا API navigator.login.setStatus("logged-in") را فراخوانی کنید تا به مرورگر اطلاع دهید که کاربر وارد سیستم شده است.
  • برای بستن کادر محاوره‌ای، تابع IdentityProvider.close() را فراخوانی کنید.
کاربر پس از ورود به IdP با استفاده از FedCM، وارد یک RP می‌شود.

وضعیت ورود کاربر را به مرورگر اطلاع دهید

API وضعیت ورود، مکانیزمی است که در آن یک وب‌سایت، به خصوص یک IdP، وضعیت ورود کاربر به IdP را به مرورگر اطلاع می‌دهد. با این API، مرورگر می‌تواند درخواست‌های غیرضروری به IdP را کاهش داده و حملات احتمالی زمان‌بندی را کاهش دهد .

IdPها می‌توانند وضعیت ورود کاربر را با ارسال یک هدر HTTP یا با فراخوانی یک API جاوا اسکریپت، هنگامی که کاربر در IdP وارد سیستم می‌شود یا هنگامی که کاربر از تمام حساب‌های IdP خود خارج می‌شود، به مرورگر اطلاع دهند. برای هر IdP (که با URL پیکربندی آن مشخص می‌شود)، مرورگر یک متغیر سه حالته را نگه می‌دارد که وضعیت ورود را با مقادیر ممکن نشان می‌دهد:

  • logged-in
  • logged-out
  • unknown (پیش‌فرض)
وضعیت ورود توضیحات
logged-in وقتی وضعیت ورود کاربر روی logged-in تنظیم شده باشد، RP که FedCM را فراخوانی می‌کند، درخواست‌هایی را به نقطه پایانی حساب‌های IdP ارسال می‌کند و حساب‌های موجود را در کادر محاوره‌ای FedCM به کاربر نمایش می‌دهد.
logged-out وقتی وضعیت ورود کاربر logged-out باشد، فراخوانی بی‌صدای FedCM بدون ارسال درخواست به نقطه پایانی حساب‌های IdP با شکست مواجه می‌شود.
unknown (پیش‌فرض) وضعیت unknown قبل از اینکه IdP با استفاده از API وضعیت ورود، سیگنالی ارسال کند، تنظیم می‌شود. وقتی وضعیت unknown است، مرورگر درخواستی به نقطه پایانی حساب‌های IdP ارسال می‌کند و وضعیت را بر اساس پاسخ از نقطه پایانی حساب‌ها به‌روزرسانی می‌کند.

برای نشان دادن اینکه کاربر وارد سیستم شده است، یک هدر HTTP Set-Login: logged-in در یک ناوبری سطح بالا یا یک درخواست زیرمنبع در همان سایت در مبدا IdP ارسال کنید:

  Set-Login: logged-in

روش دیگر، فراخوانی متد جاوا اسکریپت navigator.login.setStatus('logged-in') از مبدا IdP در یک ناوبری سطح بالا است:

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

وضعیت ورود کاربر به صورت logged-in تنظیم خواهد شد.

برای اینکه نشان دهید کاربر از تمام حساب‌های کاربری خود خارج شده است، یک هدر HTTP Set-Login: logged-out در یک نوار ناوبری سطح بالا یا یک درخواست subresource در همان سایت در مبدا IdP ارسال کنید:

  Set-Login: logged-out

روش دیگر، فراخوانی API جاوا اسکریپت navigator.login.setStatus('logged-out') از مبدا IdP در یک ناوبری سطح بالا است:

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

وضعیت ورود کاربر به صورت logged-out تنظیم خواهد شد.

وضعیت unknown قبل از اینکه IdP با استفاده از API وضعیت ورود، سیگنالی ارسال کند، تنظیم می‌شود. مرورگر درخواستی را به نقطه پایانی حساب‌های IdP ارسال می‌کند و وضعیت را بر اساس پاسخ از نقطه پایانی حساب‌ها به‌روزرسانی می‌کند:

  • اگر نقطه پایانی لیستی از حساب‌های فعال را برگرداند، وضعیت را به logged-in به‌روزرسانی کنید و کادر محاوره‌ای FedCM را باز کنید تا آن حساب‌ها نمایش داده شوند.
  • اگر نقطه پایانی هیچ حسابی را برنمی‌گرداند، وضعیت را به logged-out به‌روزرسانی کنید و فراخوانی FedCM را با شکست مواجه کنید.

اجازه دهید کاربر از طریق یک جریان ورود پویا وارد سیستم شود

اگرچه IdP مدام وضعیت ورود کاربر را به مرورگر اطلاع می‌دهد، اما ممکن است از همگام‌سازی خارج شده باشد، مثلاً زمانی که جلسه منقضی می‌شود. مرورگر سعی می‌کند وقتی وضعیت ورود به سیستم logged-in است، یک درخواست اعتبارسنجی به نقطه پایانی حساب‌ها ارسال کند، اما سرور هیچ حسابی را برنمی‌گرداند زیرا جلسه دیگر در دسترس نیست. در چنین سناریویی، مرورگر می‌تواند به صورت پویا به کاربر اجازه دهد از طریق یک پنجره محاوره‌ای به IdP وارد شود .