# 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'
```
