Перейти к основному содержимому
CodeAlive 3.0: от context engine к агенту для исследования кода
CodeAlive

Ко всем статьям

Как мы автоматизировали тестирование MCP OAuth 2.1

Rodion Mostovoi

Rodion Mostovoi

https://www.linkedin.com/in/rodion-mostovoi/

Интеграция OAuth 2.1 для MCP сама по себе нетривиальна. Клиент должен найти authorization server, зарегистрироваться или предъявить известный серверу client_id, подготовить PKCE, открыть браузер, провести пользователя через login и consent, а затем получить управление обратно через redirect.

Тестировать этот flow ещё сложнее. Большую часть протокола можно пройти через HttpClient: отправить запросы, разобрать HTML, проверить cookies, заголовки и параметры redirect. Но такой тест не отвечает на один важный вопрос: разрешит ли настоящий браузер переход с consent page на callback локального MCP-клиента?

У нас этот переход защищён Content Security Policy. Login-форма может отправлять данные только на собственный origin. Consent-форма дополнительно получает точный origin проверенного callback. Ошибка в form-action может остаться незаметной для HTTP-теста: строка в заголовке выглядит правильно, сервер возвращает 302, но браузер блокирует отправку формы.

Поэтому мы добавили отдельный Playwright-тест для этой границы.

Локальный клиент внутри теста

Desktop-приложения и CLI обычно принимают OAuth callback через временный HTTP listener на loopback-интерфейсе. Тест делает то же самое.

Сначала он запускает TcpListener на 127.0.0.1 и передаёт порт 0. Операционная система выбирает свободный порт, поэтому параллельные прогоны не спорят за заранее заданный адрес. Listener начинает ждать запрос ещё до регистрации OAuth-клиента.

Полученный адрес вида http://127.0.0.1:49152/callback тест передаёт в Dynamic Client Registration. Клиент регистрируется как native public client без секрета. Затем тест генерирует случайные code_verifier и state, вычисляет PKCE challenge методом S256 и собирает authorization request со scope mcp:tools и нужным resource.

После этого управление получает Playwright. Браузер открывает настоящий HTTPS endpoint авторизации CodeAlive, вводит email и пароль, проходит login и нажимает кнопку consent. Если тестовые credentials не заданы, сценарий сначала создаёт пользователя через обычную форму регистрации и подтверждает его email.

Callback должен получить браузерный запрос

Главная проверка происходит не по заголовку Location. Тест ждёт, пока запрос действительно придёт в TcpListener:

csharp
using TcpListener listener = new(IPAddress.Loopback, 0);listener.Start(1);
int port = ((IPEndPoint)listener.LocalEndpoint).Port;string callbackUrl = $"http://127.0.0.1:{port}/callback";Task<Uri> receivedCallback = ReceiveCallbackAsync(listener, cancellationToken);
// Playwright completes login and consent in the browser.Uri callback = await receivedCallback.WaitAsync(TimeSpan.FromSeconds(30));Dictionary<string, string> parameters = ParseQuery(callback.Query);
Assert.That(parameters["code"], Is.Not.Empty);Assert.That(parameters["state"], Is.EqualTo(state));Assert.That(parameters["iss"], Is.EqualTo(oauthIssuer));

Listener разбирает реальный GET /callback?..., отвечает короткой страницей Authentication complete и возвращает URI тесту. Мы проверяем три вещи: authorization code не пуст, state не изменился, а iss точно совпадает с нашим authorization server. Таймаут превращает заблокированный CSP redirect в понятный сбой теста, а не в зависший CI job.

Этот browser test намеренно не обменивает code на access token. Полный DCR → authorization code → PKCE token exchange → refresh rotation → revocation уже проходит через HTTP integration suite. Там же мы отдельно проверяем точный состав CSP: только 'self' на login и 'self' плюс callback origin на consent.

Playwright-тест добавляет одну проверку, которую HTTP-клиент выполнить не может: браузер принял нашу политику и доставил authorization response настоящему loopback listener. Именно на этой границе нам и нужен настоящий браузер.

Спецификация: Authorization в Model Context Protocol.

Model Context Protocol (MCP)

Дайте агентам всю кодовую базу

Проиндексируйте первый репозиторий за минуты — или попробуйте демо без регистрации.