# SSO — Google и Microsoft Sprint 20: добавлен скелет SSO. Сейчас можно перейти на consent screen у Google/Microsoft и получить email пользователя, но автоматического создания учётной записи **нет** — это требует invite-flow от администратора организации (см. multi-tenant ниже). ## Как получить keys ### Google 1. Перейти в [Google Cloud Console → APIs & Services → Credentials](https://console.cloud.google.com/apis/credentials). 2. **Create credentials → OAuth client ID**. 3. Application type: **Web application**. 4. **Authorized redirect URIs** — добавить: - `https://admin.food-market.kz/signin-google` (prod) - `https://test.admin.food-market.kz/signin-google` (stage) - `http://localhost:5081/signin-google` (dev) 5. Скопировать **Client ID** и **Client secret**. 6. Положить в `appsettings.Production.json`: ```json { "Authentication": { "Google": { "ClientId": "...", "ClientSecret": "..." } } } ``` ### Microsoft 1. Перейти в [Azure Portal → App registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) → **New registration**. 2. **Supported account types**: «Accounts in any organizational directory and personal Microsoft accounts». 3. **Redirect URI** (Web): добавить три URL аналогично Google (заменив `signin-google` → `signin-microsoft`). 4. После создания: **Certificates & secrets → New client secret**. Скопировать **value**. 5. **Overview → Application (client) ID**. 6. Конфиг: ```json { "Authentication": { "Microsoft": { "ClientId": "...", "ClientSecret": "..." } } } ``` ## Как использовать ### Без настроенных keys `GET /api/auth/external/google` → **503** с подсказкой: ```json { "error": "SSO для Google не настроено.", "hint": "Добавьте в appsettings: Authentication:Google:ClientId и :ClientSecret. См. docs/sso.md." } ``` `GET /api/auth/external/providers` → текущее состояние: ```json { "google": false, "microsoft": false } ``` Web-фронт скрывает кнопки SSO когда оба провайдера = false. ### С настроенными keys 1. Пользователь жмёт «Войти через Google» → фронт делает редирект на `GET /api/auth/external/google`. 2. Сервер возвращает 302 на Google consent screen. 3. После consent — Google редиректит на `/signin-google`, который обрабатывает ASP.NET middleware и сохраняет identity во временный cookie `fm.external`. 4. Middleware вызывает `/api/auth/external/callback?provider=google`. 5. **Sprint 20 scaffold**: callback возвращает **501** с информацией: ```json { "status": "scaffolded", "message": "SSO-callback получен, но автоматическая регистрация ещё не реализована.", "email": "user@example.com", "name": "John Doe", "next": "Попросите администратора организации пригласить вас..." } ``` ## Что осталось доделать (после v1) - **Invite-flow**: org-админ создаёт Employee запись с email'ом, после чего SSO-callback находит этот email и линкует SSO-identity к существующему User'у. - **Выпуск OpenIddict access+refresh токенов** после успешного линка (использовать тот же flow что и в `AuthController.PasswordGrant`). - **Связь identity_provider+sub** для повторных логинов (новая таблица `external_logins` (UserId, Provider, ProviderKey)). - **UI**: на `/login` рендерить кнопки «Войти через Google/Microsoft» только если `/api/auth/external/providers` вернул `true` для провайдера. - **Конфликт email**: если пользователь с этим email уже есть и привязан к другой org → отказ либо choice «выбрать org». ## Multi-tenant специфика SSO **per-organization** — реальный пользователь в системе всегда привязан к конкретной org через Employee. SSO-логин по email не определяет org однозначно (один email может работать в нескольких организациях через Employee-приглашения). Поэтому invite-flow обязателен: SSO не создаёт User'а вслепую, а лишь верифицирует «вы — владелец этого email» и линкует к ранее приглашённому Employee. ## Тестирование Скелет тестируется без реальных keys: ```bash # 503 — провайдер не настроен curl -i https://test.admin.food-market.kz/api/auth/external/google # Список — все false curl -s https://test.admin.food-market.kz/api/auth/external/providers ``` С реальными keys (нужно настроить в `appsettings.Stage.json`): ```bash # Редирект на Google consent curl -i 'https://test.admin.food-market.kz/api/auth/external/google?returnUrl=/dashboard' ```