fix and update
This commit is contained in:
@@ -79,19 +79,25 @@ export const apiReference: ApiTagGroup[] = [
|
||||
method: 'GET',
|
||||
path: '/fedcm/config.json',
|
||||
summary: 'Конфигурация FedCM IdP',
|
||||
description: 'accounts_endpoint, id_assertion_endpoint, login_url, branding.'
|
||||
description: 'accounts_endpoint, id_assertion_endpoint, login_url (same-origin с config), branding.name = PROJECT_NAME, branding.icons.'
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/fedcm/discover.json',
|
||||
summary: 'Discovery для виджета',
|
||||
description: 'configUrl, apiBase, frontendUrl, projectName, suggestedFields для RP.'
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/fedcm/accounts',
|
||||
summary: 'Список аккаунтов FedCM',
|
||||
description: 'Credentialed GET по cookie lendry_fedcm_sess. CORS с credentials для RP.'
|
||||
description: 'Credentialed GET по cookie lendry_fedcm_sess. Поля: name, email, picture, tel. CORS с credentials для RP.'
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/fedcm/id_assertion',
|
||||
summary: 'Выдача id_token FedCM',
|
||||
description: 'Тело: client_id, account_id. Возвращает { token } — OIDC id_token.'
|
||||
description: 'Form POST: client_id, account_id, fields, disclosure_shown_for. Возвращает { token } — OIDC id_token.'
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
@@ -105,6 +111,12 @@ export const apiReference: ApiTagGroup[] = [
|
||||
summary: 'Синхронизация FedCM cookie',
|
||||
description: 'Bearer access token → установка HttpOnly cookie lendry_fedcm_sess.',
|
||||
auth: true
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/fedcm/login-status',
|
||||
summary: 'FedCM Login Status bridge',
|
||||
description: 'HTML на API origin для navigator.login.setStatus (origin login_url).'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -964,7 +964,7 @@ $oidc->authenticate(); // client_id, redirect_uri, response_type=code — авт
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Для входа в один клик на сторонних сайтах (FedCM + виджет sso-widget.js) см. отдельный раздел документации.'
|
||||
text: 'Для входа в один клик на сторонних сайтах (FedCM + виджет sso-widget.js) см. раздел «One Tap Login»: split-domain, branding, fields API, popup fallback.'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
@@ -1004,49 +1004,111 @@ $oidc->authenticate(); // client_id, redirect_uri, response_type=code — авт
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'One Tap Login позволяет пользователям вашего сайта войти через Lendry ID без полного редиректа на страницу авторизации. Поддерживаются два режима: Federated Credential Management (FedCM) в современных браузерах и виджет sso-widget.js с popup-окном для остальных.'
|
||||
text: 'One Tap Login позволяет пользователям вашего сайта войти через IdP без полного редиректа на страницу авторизации. Поддерживаются два режима: Federated Credential Management (FedCM) в Chrome/Edge и виджет sso-widget.js с popup-окном для остальных браузеров.'
|
||||
},
|
||||
{
|
||||
type: 'callout',
|
||||
variant: 'info',
|
||||
title: 'Предварительные требования',
|
||||
text: 'Создайте OAuth-приложение в админ-панели (RBAC → OAuth приложения): укажите redirect_uri вашего сайта, scopes openid profile email и сохраните client_id. Подробнее — раздел «OAuth 2.0 / OIDC».'
|
||||
text: 'Создайте OAuth-приложение в админ-панели (RBAC → OAuth приложения): укажите redirect_uri вашего сайта, scopes openid profile email и сохраните client_id. В системных настройках задайте PUBLIC_API_URL, PUBLIC_FRONTEND_URL и PROJECT_NAME. Подробнее — раздел «OAuth 2.0 / OIDC».'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
items: [
|
||||
'FedCM — браузер показывает нативный диалог «Войти как …», если пользователь уже залогинен на домене IdP',
|
||||
'Fallback — скрипт sso-widget.js рисует плашку «Войти через …» в правом верхнем углу и открывает popup с OAuth',
|
||||
'Оба режима возвращают OIDC id_token (FedCM) или authorization code / токены (popup) — проверяйте на backend'
|
||||
'FedCM — браузер показывает нативный диалог «Войти в … через {PROJECT_NAME}», если пользователь уже залогинен на IdP',
|
||||
'Fallback — sso-widget.js рисует плашку «Войти через …» и открывает popup OAuth (display=popup)',
|
||||
'FedCM возвращает OIDC id_token; popup — authorization code или токены через postMessage. Проверяйте на backend'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'fedcm-architecture',
|
||||
title: 'Архитектура FedCM (split-domain)',
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'FedCM endpoints размещаются на PUBLIC_API_URL (issuer), например https://api.idpmvk.lpr. UI авторизации — на PUBLIC_FRONTEND_URL, например https://sso.idpmvk.lpr. Виджет sso-widget.js подключается с frontend-домена, а configURL указывает на API-домен.'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
items: [
|
||||
'configURL для navigator.credentials.get() — всегда {PUBLIC_API_URL}/fedcm/config.json, не путь через frontend',
|
||||
'login_url в config.json обязан быть same-origin с config.json (требование Chrome). У нас: {PUBLIC_API_URL}/auth/login — nginx проксирует UI с frontend',
|
||||
'Cookie lendry_fedcm_sess в production: Domain=.{корневой-домен}, Secure, SameSite=None — чтобы FedCM работал между api.* и sso.*',
|
||||
'Манифест /.well-known/web-identity может отдаваться с apex-домена; provider_urls указывает на config.json API-домена'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'callout',
|
||||
variant: 'warning',
|
||||
title: 'Типичные ошибки',
|
||||
text: 'NetworkError / invalid login_url — login_url на другом origin, чем config.json. 401 на accounts — нет cookie или пользователь не залогинен на IdP. Disclosure показывает только «имя» — RP не передал fields в navigator.credentials.get().'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'fedcm-endpoints',
|
||||
title: 'FedCM endpoints',
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Endpoints размещены на PUBLIC_API_URL (issuer). Локально: http://localhost:3000. При same-origin деплое: https://ваш-домен/idp-api. CORS настроен с credentials: true для запросов с сайтов-клиентов.'
|
||||
text: 'Все URL ниже — относительно PUBLIC_API_URL (issuer). CORS настроен с credentials: true для запросов FedCM с сайтов-клиентов. Запросы config/accounts/id_assertion должны содержать Sec-Fetch-Dest: webidentity (Chrome добавляет автоматически).'
|
||||
},
|
||||
{
|
||||
type: 'table',
|
||||
headers: ['Endpoint', 'Метод', 'Описание'],
|
||||
rows: [
|
||||
['{PUBLIC_API_URL}/.well-known/web-identity', 'GET', 'Манифест FedCM → provider_urls'],
|
||||
['{PUBLIC_API_URL}/fedcm/config.json', 'GET', 'Конфигурация IdP (accounts, id_assertion, branding)'],
|
||||
['{PUBLIC_API_URL}/.well-known/web-identity', 'GET', 'Манифест FedCM → provider_urls, accounts_endpoint, login_url'],
|
||||
['{PUBLIC_API_URL}/fedcm/config.json', 'GET', 'Конфигурация IdP: endpoints, login_url, branding (name, icons, colors)'],
|
||||
['{PUBLIC_API_URL}/fedcm/discover.json', 'GET', 'Публичный discovery для sso-widget.js (configUrl, suggestedFields, projectName)'],
|
||||
['{PUBLIC_API_URL}/fedcm/accounts', 'GET', 'Список аккаунтов по cookie lendry_fedcm_sess'],
|
||||
['{PUBLIC_API_URL}/fedcm/id_assertion', 'POST', 'Выдача id_token для client_id + account_id'],
|
||||
['{PUBLIC_API_URL}/fedcm/client_metadata', 'GET', '?client_id=… — privacy/terms для UI FedCM'],
|
||||
['{PUBLIC_API_URL}/fedcm/session/sync', 'POST', 'Установить FedCM cookie по Bearer access token']
|
||||
['{PUBLIC_API_URL}/fedcm/session/sync', 'POST/GET', 'Установить FedCM cookie по Bearer access token'],
|
||||
['{PUBLIC_API_URL}/fedcm/login-status', 'GET', 'HTML-мост Set-Login на API origin (origin login_url)']
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'callout',
|
||||
variant: 'info',
|
||||
title: 'Branding в config.json',
|
||||
text: 'branding.name берётся из PROJECT_NAME (админка). Иконки: {PUBLIC_FRONTEND_URL}/icon.svg (40px) и favicon API-домена. В диалоге Chrome отображается название проекта вместо технического домена IdP.'
|
||||
},
|
||||
{
|
||||
type: 'callout',
|
||||
variant: 'warning',
|
||||
title: 'FedCM cookie',
|
||||
text: 'FedCM использует HttpOnly cookie lendry_fedcm_sess на домене IdP (SameSite=None; Secure в production). Cookie устанавливается при входе на IdP или через POST /fedcm/session/sync. localStorage JWT с сайта клиента для FedCM не подходит.'
|
||||
text: 'HttpOnly cookie lendry_fedcm_sess на домене IdP. Устанавливается при входе на IdP или через /fedcm/session/sync. JWT из localStorage сайта-клиента для FedCM не подходит — нужна сессия на IdP.'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'fedcm-accounts-fields',
|
||||
title: 'Данные аккаунта и disclosure',
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'GET /fedcm/accounts возвращает массив accounts с полями id, name, given_name, email, picture, tel (если есть у пользователя). picture — публичный URL аватара (legacy avatarUrl или временная ссылка /media/stream/{token}).'
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Текст disclosure («какие данные передаются сайту») контролирует сайт-клиент (RP), а не IdP. RP должен указать fields в navigator.credentials.get():'
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
language: 'javascript',
|
||||
title: 'Fields API (Chrome 132+, tel — Chrome 141+)',
|
||||
code: `fields: ['name', 'email', 'picture', 'tel']`
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
items: [
|
||||
'sso-widget.js передаёт fields автоматически',
|
||||
'discover.json содержит suggestedFields для интеграторов',
|
||||
'Без fields Chrome по умолчанию показывает disclosure только для имени',
|
||||
'id_token после входа содержит claims OAuth scopes (openid profile email)'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1056,16 +1118,16 @@ $oidc->authenticate(); // client_id, redirect_uri, response_type=code — авт
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Скрипт размещён на frontend IdP по адресу {PUBLIC_FRONTEND_URL}/sso-widget.js. Достаточно одного тега script — инициализация выполняется автоматически при загрузке страницы.'
|
||||
text: 'Скрипт размещён на frontend IdP: {PUBLIC_FRONTEND_URL}/sso-widget.js. Достаточно одного тега script — инициализация выполняется автоматически. Виджет сначала запрашивает /fedcm/discover.json на API-домене, затем вызывает FedCM или popup.'
|
||||
},
|
||||
{
|
||||
type: 'table',
|
||||
headers: ['Атрибут data-*', 'Обязательный', 'Описание'],
|
||||
rows: [
|
||||
['data-client-id', 'Да', 'client_id OAuth-приложения из админки'],
|
||||
['data-idp-url', 'Нет', 'PUBLIC_API_URL; по умолчанию origin скрипта + /idp-api'],
|
||||
['data-idp-frontend-url', 'Нет', 'URL frontend IdP для popup; по умолчанию origin скрипта'],
|
||||
['data-provider-name', 'Нет', 'Название в плашке (по умолчанию MVK ID)'],
|
||||
['data-idp-url', 'Да*', 'PUBLIC_API_URL (issuer). Обязателен, если скрипт не на том же домене'],
|
||||
['data-idp-frontend-url', 'Нет', 'PUBLIC_FRONTEND_URL для popup; по умолчанию origin скрипта'],
|
||||
['data-provider-name', 'Нет', 'Название в плашке fallback (по умолчанию PROJECT_NAME)'],
|
||||
['data-redirect-uri', 'Нет', 'redirect_uri OAuth; по умолчанию origin + /auth/callback'],
|
||||
['data-scope', 'Нет', 'Scopes OAuth (по умолчанию openid profile email)'],
|
||||
['data-on-success', 'Нет', 'Имя глобальной функции-callback'],
|
||||
@@ -1076,7 +1138,7 @@ $oidc->authenticate(); // client_id, redirect_uri, response_type=code — авт
|
||||
type: 'callout',
|
||||
variant: 'tip',
|
||||
title: 'Логика выбора режима',
|
||||
text: 'Если браузер поддерживает IdentityCredential — скрипт вызывает FedCM. Если нет — показывается плашка в правом верхнем углу; по клику открывается popup OAuth (параметр display=popup).'
|
||||
text: 'Если браузер поддерживает IdentityCredential — виджет вызывает FedCM с fields: name, email, picture, tel. Иначе — плашка в правом верхнем углу; по клику popup OAuth.'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1086,14 +1148,14 @@ $oidc->authenticate(); // client_id, redirect_uri, response_type=code — авт
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Интерактивный конструктор позволяет настроить кнопку входа под внешний вид вашего сайта. Выберите параметры — справа обновится живое превью, а слева сформируется готовый код виджета. Скопируйте его и вставьте на свою страницу: атрибут data-button-container указывает, в какой элемент встроить кнопку.'
|
||||
text: 'Интерактивный конструктор позволяет настроить кнопку входа под внешний вид вашего сайта. URL в примерах подставляются из PUBLIC_API_URL и PUBLIC_FRONTEND_URL текущего IdP.'
|
||||
},
|
||||
{ type: 'one-tap-builder' },
|
||||
{
|
||||
type: 'callout',
|
||||
variant: 'tip',
|
||||
title: 'Как это работает',
|
||||
text: 'Кнопка рендерится скриптом sso-widget.js внутри контейнера с указанным id. Параметры размера, темы, вида, радиуса, иконки и CSS-цветов передаются через data-button-* атрибуты. Клик по превью открывает реальный popup авторизации для проверки.'
|
||||
text: 'Кнопка рендерится sso-widget.js внутри контейнера с указанным id. Клик по превью открывает реальный popup авторизации для проверки.'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1103,7 +1165,7 @@ $oidc->authenticate(); // client_id, redirect_uri, response_type=code — авт
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Если вы не используете sso-widget.js, можно вызвать FedCM API напрямую. configURL должен указывать на /fedcm/config.json провайдера.'
|
||||
text: 'Если вы не используете sso-widget.js, вызовите FedCM API напрямую. configURL — {PUBLIC_API_URL}/fedcm/config.json. Пользователь должен быть залогинен на IdP (cookie lendry_fedcm_sess).'
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
@@ -1112,8 +1174,9 @@ $oidc->authenticate(); // client_id, redirect_uri, response_type=code — авт
|
||||
code: `const credential = await navigator.credentials.get({
|
||||
identity: {
|
||||
providers: [{
|
||||
configURL: 'https://id.lendry.ru/idp-api/fedcm/config.json',
|
||||
clientId: 'YOUR_CLIENT_ID'
|
||||
configURL: '{PUBLIC_API_URL}/fedcm/config.json',
|
||||
clientId: 'YOUR_CLIENT_ID',
|
||||
fields: ['name', 'email', 'picture', 'tel']
|
||||
}]
|
||||
},
|
||||
mediation: 'optional'
|
||||
@@ -1127,7 +1190,8 @@ const idToken = credential?.token;
|
||||
items: [
|
||||
'mediation: "optional" — показать диалог только при наличии сессии IdP',
|
||||
'mediation: "required" — всегда показывать UI выбора аккаунта',
|
||||
'mediation: "silent" — без UI; вернёт ошибку, если пользователь не залогинен'
|
||||
'mediation: "silent" — без UI; ошибка, если пользователь не залогинен',
|
||||
'data-idp-url в виджете и configURL в ручной интеграции — публичный API-домен, доступный из браузера пользователя'
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1138,7 +1202,7 @@ const idToken = credential?.token;
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'При клике на виджет открывается popup с OAuth authorize. После успешного входа IdP отправляет результат родительскому окну через window.postMessage с типом lendry-sso-onetap.'
|
||||
text: 'Если FedCM недоступен, виджет показывает плашку и по клику открывает popup с OAuth authorize на {PUBLIC_FRONTEND_URL}/auth/oauth/authorize?display=popup. После успешного входа IdP отправляет результат родительскому окну через window.postMessage.'
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
@@ -1146,16 +1210,16 @@ const idToken = credential?.token;
|
||||
title: 'Обработка на сайте клиента',
|
||||
code: `window.addEventListener('message', (event) => {
|
||||
if (event.data?.type !== 'lendry-sso-onetap') return;
|
||||
// Проверяйте event.origin — домен вашего IdP
|
||||
// Проверяйте event.origin — PUBLIC_FRONTEND_URL вашего IdP
|
||||
const { token, idToken, accessToken, code, method } = event.data;
|
||||
console.log('Вход через', method);
|
||||
console.log('Вход через', method); // 'fedcm' | 'popup'
|
||||
});`
|
||||
},
|
||||
{
|
||||
type: 'callout',
|
||||
variant: 'info',
|
||||
title: 'redirect_uri',
|
||||
text: 'redirect_uri должен быть зарегистрирован у OAuth-клиента. Для SPA часто используют https://your-app.com/auth/callback — на этой странице backend обменивает code на токены, либо popup передаёт токен через postMessage.'
|
||||
text: 'redirect_uri должен быть зарегистрирован у OAuth-клиента. Для SPA часто используют https://your-app.com/auth/callback — backend обменивает code на токены, либо popup передаёт токен через postMessage.'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1165,7 +1229,7 @@ const idToken = credential?.token;
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'FedCM возвращает id_token (JWT). Проверьте подпись (HS256, секрет JWT_ACCESS_SECRET IdP), issuer = PUBLIC_API_URL и aud = ваш client_id. Альтернатива — обмен authorization code через POST /oauth/token и запрос GET /oauth/userinfo.'
|
||||
text: 'FedCM возвращает id_token (JWT). Проверьте подпись (HS256, секрет JWT_ACCESS_SECRET IdP), issuer = PUBLIC_API_URL и aud = ваш client_id. Альтернатива — обмен authorization code через POST /oauth/token и GET /oauth/userinfo.'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
@@ -1178,30 +1242,37 @@ const idToken = credential?.token;
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'examples',
|
||||
title: 'Примеры интеграции',
|
||||
blocks: [{ type: 'one-tap-examples' }]
|
||||
},
|
||||
{
|
||||
id: 'local-dev',
|
||||
title: 'Локальная разработка',
|
||||
id: 'testing',
|
||||
title: 'Проверка интеграции',
|
||||
blocks: [
|
||||
{
|
||||
type: 'table',
|
||||
headers: ['Сервис', 'URL'],
|
||||
rows: [
|
||||
['API (FedCM endpoints)', 'http://localhost:3000'],
|
||||
['Frontend (sso-widget.js)', 'http://localhost:3002/sso-widget.js'],
|
||||
['Документация', 'http://localhost:3003/docs/one-tap-login']
|
||||
type: 'list',
|
||||
items: [
|
||||
'Войдите на IdP ({PUBLIC_FRONTEND_URL}) в том же браузере, где тестируете сайт-клиент',
|
||||
'Убедитесь, что PUBLIC_API_URL в настройках = домен, который видит браузер (не внутренний Docker-хост)',
|
||||
'Проверьте login_url в config.json: curl -s {PUBLIC_API_URL}/fedcm/config.json -H "Sec-Fetch-Dest: webidentity" | jq .login_url',
|
||||
'ONE_TAP_ENABLED=true в системных настройках (по умолчанию включено)',
|
||||
'При смене FEDCM_COOKIE_DOMAIN пользователям может потребоваться перелогин на IdP'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'callout',
|
||||
variant: 'tip',
|
||||
title: 'Тест FedCM локально',
|
||||
text: 'FedCM cookie в dev использует SameSite=Lax (без Secure). Войдите на http://localhost:3002, затем откройте тестовую страницу клиента с подключённым виджетом. Chrome может требовать флаг chrome://flags/#identity-credentials-api или HTTPS для FedCM — в этом случае проверяйте popup fallback.'
|
||||
title: 'Диагностика в админке',
|
||||
text: 'В карточке OAuth-приложения (RBAC → OAuth приложения → One Tap Login) отображаются актуальные FedCM URL, примеры кода и чеклист проверки.'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'examples',
|
||||
title: 'Примеры интеграции',
|
||||
blocks: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Примеры ниже автоматически подставляют PUBLIC_API_URL, PUBLIC_FRONTEND_URL и PROJECT_NAME из настроек текущего IdP.'
|
||||
},
|
||||
{ type: 'one-tap-examples' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface OneTapUrls {
|
||||
frontendBase: string;
|
||||
widgetUrl: string;
|
||||
fedcmConfigUrl: string;
|
||||
fedcmDiscoverUrl: string;
|
||||
webIdentityUrl: string;
|
||||
projectName: string;
|
||||
}
|
||||
@@ -21,13 +22,14 @@ export function buildOneTapUrls(
|
||||
frontendBase: front,
|
||||
widgetUrl: `${front}/sso-widget.js`,
|
||||
fedcmConfigUrl: `${base}/fedcm/config.json`,
|
||||
fedcmDiscoverUrl: `${base}/fedcm/discover.json`,
|
||||
webIdentityUrl: `${base}/.well-known/web-identity`,
|
||||
projectName
|
||||
};
|
||||
}
|
||||
|
||||
export function buildOneTapExamples(urls: OneTapUrls, clientIdPlaceholder = 'YOUR_CLIENT_ID'): OAuthExample[] {
|
||||
const { apiBase, frontendBase, widgetUrl, fedcmConfigUrl, projectName } = urls;
|
||||
const { apiBase, frontendBase, widgetUrl, fedcmConfigUrl, fedcmDiscoverUrl, webIdentityUrl, projectName } = urls;
|
||||
const redirectUri = 'https://app.example.com/auth/callback';
|
||||
|
||||
return [
|
||||
@@ -59,8 +61,8 @@ export function buildOneTapExamples(urls: OneTapUrls, clientIdPlaceholder = 'YOU
|
||||
id: 'fedcm-native',
|
||||
label: 'FedCM (нативный API)',
|
||||
language: 'javascript',
|
||||
code: `// Работает в Chrome/Edge при поддержке IdentityCredential.
|
||||
// Пользователь должен быть залогинен на ${frontendBase} (FedCM cookie).
|
||||
code: `// Работает в Chrome/Edge 132+ (поля name/email/picture/tel — с Chrome 141 для tel).
|
||||
// Пользователь должен быть залогинен на ${frontendBase} (cookie lendry_fedcm_sess на домене IdP).
|
||||
|
||||
async function loginWithFedCM() {
|
||||
if (!('IdentityCredential' in window)) {
|
||||
@@ -72,6 +74,7 @@ async function loginWithFedCM() {
|
||||
providers: [{
|
||||
configURL: '${fedcmConfigUrl}',
|
||||
clientId: '${clientIdPlaceholder}',
|
||||
// Chrome 132+: disclosure в диалоге FedCM (имя, email, аватар, телефон)
|
||||
fields: ['name', 'email', 'picture', 'tel']
|
||||
}]
|
||||
},
|
||||
@@ -145,14 +148,20 @@ async function fetchProfile(accessToken) {
|
||||
id: 'curl-fedcm',
|
||||
label: 'FedCM endpoints (curl)',
|
||||
language: 'bash',
|
||||
code: `# Манифест FedCM
|
||||
curl -s ${apiBase}/.well-known/web-identity | jq
|
||||
code: `# Манифест FedCM (заголовок Sec-Fetch-Dest обязателен для Chrome)
|
||||
curl -s ${webIdentityUrl} \\
|
||||
-H "Sec-Fetch-Dest: webidentity" | jq
|
||||
|
||||
# Конфигурация провайдера
|
||||
curl -s ${apiBase}/fedcm/config.json | jq
|
||||
# Конфигурация провайдера (branding.name = PROJECT_NAME из админки)
|
||||
curl -s ${fedcmConfigUrl} \\
|
||||
-H "Sec-Fetch-Dest: webidentity" | jq
|
||||
|
||||
# Список аккаунтов (нужна cookie lendry_fedcm_sess после входа на IdP)
|
||||
# Discovery для виджета и диагностики
|
||||
curl -s ${fedcmDiscoverUrl} | jq
|
||||
|
||||
# Список аккаунтов (cookie lendry_fedcm_sess после входа на IdP)
|
||||
curl -s ${apiBase}/fedcm/accounts \\
|
||||
-H "Sec-Fetch-Dest: webidentity" \\
|
||||
-H "Cookie: lendry_fedcm_sess=..." \\
|
||||
--include
|
||||
|
||||
|
||||
57
apps/docs/lib/use-one-tap-urls.ts
Normal file
57
apps/docs/lib/use-one-tap-urls.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { fetchPublicSettingsClient } from '@/lib/api';
|
||||
import { buildOneTapUrls, type OneTapUrls } from '@/lib/one-tap-examples';
|
||||
import { resolveFrontendBase, resolveOAuthApiBase } from '@/lib/oauth-url';
|
||||
|
||||
export function useOneTapUrls() {
|
||||
const [urls, setUrls] = useState<OneTapUrls | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
void fetchPublicSettingsClient()
|
||||
.then((settings) => {
|
||||
if (cancelled) return;
|
||||
|
||||
const apiBase = resolveOAuthApiBase(settings, '');
|
||||
const frontendBase = resolveFrontendBase(settings, '');
|
||||
|
||||
if (!apiBase || !frontendBase) {
|
||||
setError('Укажите PUBLIC_API_URL и PUBLIC_FRONTEND_URL в настройках IdP — примеры подставят актуальные URL автоматически.');
|
||||
setUrls(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setUrls(buildOneTapUrls(apiBase, frontendBase, settings.PROJECT_NAME?.trim() || 'MVK ID'));
|
||||
setError(null);
|
||||
})
|
||||
.catch(() => {
|
||||
if (cancelled) return;
|
||||
setError('Не удалось загрузить публичные настройки IdP.');
|
||||
setUrls(null);
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
urls,
|
||||
loading,
|
||||
error,
|
||||
apiBase: urls?.apiBase ?? '',
|
||||
frontendBase: urls?.frontendBase ?? '',
|
||||
projectName: urls?.projectName ?? 'MVK ID'
|
||||
}),
|
||||
[error, loading, urls]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user