<!-- review: finished -->

<a id="oidc-config"></a>

# Настройка аутентификации OIDC

В этом руководстве описано, как настроить аутентификацию OpenID Connect (OIDC),
используя Google в качестве провайдера идентификации
и веб-сервер Angie со скриптами на Lua.

Эта реализация защищает внутренние конечные точки с помощью аутентификации OAuth2/OIDC
и демонстрирует один из способов ограничить доступ по доменам электронной почты.
Это лишь один из возможных подходов; вы можете реализовать контроль доступа
любым удобным вам способом — например, поддерживать списки разрешённых пользователей,
проверять принадлежность к домену или группам в ответе провайдера
или использовать произвольные claims вашего внутреннего IAM.

<a id="architecture"></a>

## Архитектура

Предлагаемая конфигурация OIDC включает:

- Angie — с поддержкой Lua-модуля для обработки OIDC
- [lua-resty-openidc](https://github.com/zmartzone/lua-resty-openidc) — Lua-библиотека OpenResty для аутентификации через OIDC
- Google OAuth2 — провайдер идентификации
- Docker Compose — используется в примере только для быстрого запуска;
  в продакшене используйте подходящий вариант развертывания

<a id="prerequisites"></a>

## Требования

Перед настройкой OIDC убедитесь, что у вас есть:

1. Веб-сервер Angie с поддержкой [Lua-модуля](https://angie.software//angie/docs/installation/external-modules/lua.md#external-lua)
2. Docker и Docker Compose (для развертывания)
3. Проект в Google Cloud Console
4. OAuth2-учётные данные от Google

<a id="google-oauth2-setup"></a>

## Настройка Google OAuth2

Чтобы настроить Google как OIDC-провайдера:

1. Перейдите в [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
2. Создайте новый проект или выберите существующий
3. Настройте экран согласия OAuth (External или Internal) и опубликуйте его
4. Создайте OAuth2-учётные данные:
   - Тип приложения: Web application
   - Разрешённые URI для редиректа: `http://localhost/auth/callback`
5. Сохраните свои `client_id` и `client_secret`

#### NOTE
Стандартные Google Identity Services уже поддерживают OIDC;
устаревший Google+ API не требуется.
Включайте дополнительные Google API только при необходимости.

<a id="configuration-setup"></a>

## Настройка конфигурации

Начнём с необходимых конфигурационных файлов.

<a id="docker-compose-configuration"></a>

### Конфигурация Docker Compose

Развёртывание через Docker использует следующий конфиг:

```yaml
services:
  angie:
    image: docker.angie.software/angie:templated
    environment:
      ANGIE_LOAD_MODULES: "lua"
    ports:
      - 80:80
    volumes:
      - ./files/etc/angie/http.d:/etc/angie/http.d
```

Эта конфигурация:

- Использует [templated-образ Angie](https://angie.software//angie/docs/installation/docker.md#docker-templated) с поддержкой Lua
- Загружает [Lua-модуль](https://angie.software//angie/docs/installation/external-modules/lua.md#external-lua) для OIDC
- Пробрасывает порт 80
- Монтирует локальные конфигурационные файлы

Для запуска «из коробки» скачайте
[`OIDC quick-start bundle`](https://angie.software//angie/docs/configuration/oidc-sample-config.zip),
установите `client_id` и `client_secret` в
`files/etc/angie/http.d/oidc.lua`,
и всё сразу заработает.

<a id="oidc-authentication-script"></a>

### Скрипт аутентификации OIDC

Создайте скрипт OIDC,
который обрабатывает логику аутентификации с использованием библиотеки
`lua-resty-openidc`:

```lua
access_by_lua_block {
    local res, err = require("resty.openidc").authenticate({
        redirect_uri = "http://localhost/auth/callback",
        discovery = "https://accounts.google.com/.well-known/openid-configuration",
        logout_path = "/auth/logout",
        redirect_after_logout_uri = "/auth/logged-out",
        revoke_tokens_on_logout = true,
        client_id = "YOUR_CLIENT_ID",
        client_secret = "YOUR_CLIENT_SECRET"
    })
}
```

Параметры конфигурации:

- `redirect_uri`: URL-адрес обратного вызова после успешной аутентификации
- `discovery`: discovery-endpoint Google OIDC
- `logout_path`: путь выхода пользователя
- `redirect_after_logout_uri`: куда перенаправлять после выхода
- `revoke_tokens_on_logout`: отзывать токены при logout
- `client_id` и `client_secret`: учётные данные OAuth2

<a id="angie-configuration"></a>

### Конфигурация Angie

Настройте Angie с необходимыми блоками [location](https://angie.software//angie/docs/configuration/modules/http/index.md#location)
для аутентификации OIDC.

<a id="protected-resources"></a>

#### Защищённые ресурсы

Чтобы защитить ресурсы:

```nginx
location /internal/ {
    include /etc/angie/http.d/oidc.lua;
    proxy_pass http://127.0.0.1/status/;
}
```

Здесь:

- Путь `/internal/` защищён OIDC
- Запросы проксируются к [внутреннему API](https://angie.software//angie/docs/configuration/modules/http/http_api.md#http-api) на `/status/`

<a id="authentication-endpoints"></a>

#### Конечные точки аутентификации

Настройка OAuth2-эндпоинтов:

```nginx
location /auth/callback {
    include /etc/angie/http.d/oidc.lua;
}

location /auth/logout {
    include /etc/angie/http.d/oidc.lua;
}

location /auth/logged-out {
    default_type text/plain;
    return 200 "You have been logged out. Bye!";
}
```

Назначение маршрутов:

- `/auth/callback`: обработка callback от Google
- `/auth/logout`: инициирует выход
- `/auth/logged-out`: страница после выхода

<a id="internal-api-access"></a>

#### Доступ к внутреннему API

Настройте ограниченный доступ к API:

```nginx
location /status/ {
    api     /status/;
    allow   127.0.0.1;
    deny    all;
}
```

Это обеспечивает:

- Доступ к [status API Angie](https://angie.software//angie/docs/configuration/modules/http/http_api.md#http-api)
- Доступ только с localhost
- OIDC-защиту при доступе через `/internal/`

<a id="deployment-steps"></a>

## Этапы развертывания

<a id="configuration-update"></a>

### Обновление конфигурации

1. Обновите учетные данные OAuth2 в файле Lua:

   Замените значения в `oidc.lua`:
   - `client_id` на ваш Google OAuth2 Client ID
   - `client_secret` на ваш Client Secret

<a id="service-startup"></a>

### Запуск сервисов

1. Запустите Docker-сервисы:
   ```console
   $ docker-compose up -d
   ```
2. Проверьте работу:
   - Откройте `http://localhost/internal/`
   - Должна появиться переадресация на Google
   - После логина вы попадёте в защищённый раздел

<a id="security-configuration"></a>

## Настройки безопасности

<a id="email-domain-restriction"></a>

### Ограничение по домену e-mail

Реализуйте проверку домена:

```lua
if not string.match(res.user.email, "gmail.com$") then
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
```

В продакшене:

- замените `gmail.com` на домен вашей компании
- используйте whitelist пользователей
- добавьте контроль ролей

<a id="token-management"></a>

### Управление токенами

В реализации OIDC предусмотрено:

- автоматический отзыв токенов при logout (`revoke_tokens_on_logout = true`)
- безопасное управление сессией в lua-resty-openidc
- следование лучшим практикам OAuth2/OIDC

#### WARNING
Для продакшена:

- Всегда используйте HTTPS для callback-адресов
- Храните client secrets безопасно, не в репозитории
- Настройте корректные таймауты и обновление сессий
- Мониторьте логи аутентификации

<a id="authentication-flow"></a>

## Поток аутентификации

Стандартный поток OIDC выглядит так:

1. Пользователь запрашивает защищённый URL (например `http://localhost/internal/`)
2. При отсутствии сессии — перенаправление на Google OAuth2
3. Пользователь входит в аккаунт Google
4. Google возвращает код авторизации на `/auth/callback`
5. Сервер запрашивает access token и ID token
6. Проверяет данные пользователя (включая домен e-mail)
7. Предоставляет доступ к ресурсу

Процесс logout:

1. Пользователь переходит по `http://localhost/auth/logout`
2. Токены отзываются у Google
3. Локальная сессия очищается
4. Переход на `/auth/logged-out`

<a id="advanced-configuration"></a>

## Расширенная конфигурация

Ограничить доступ одним доменом:

```lua
if not string.match(res.user.email, "yourcompany.com$") then
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
```

Несколько разрешённых доменов:

```lua
local allowed_domains = {"company1.com", "company2.com", "gmail.com"}
local email_valid = false

for _, domain in ipairs(allowed_domains) do
    if string.match(res.user.email, domain .. "$") then
        email_valid = true
        break
    end
end

if not email_valid then
    ngx.exit(ngx.HTTP_FORBIDDEN)
end
```

<a id="user-information-access"></a>

### Доступ к данным пользователя

Получить дополнительные claims:

```lua
-- Доступ к данным из ID token
local user_name = res.user.name
local user_picture = res.user.picture
local user_locale = res.user.locale
local user_email = res.user.email
```

Эти данные можно использовать для логирования,
персонализации или дополнительных решений по контролю доступа.
