Программирование

Условная аутентификация в Chainlit для Microsoft Teams и браузеров

Пошаговое руководство по реализации условной аутентификации в Chainlit: header-аутентификация для Teams и OAuth для браузерных пользователей. Избегаем ошибок аутентификации.

4 ответа 1 просмотр

Как условно включить header_auth_callback в Chainlit только для запросов из Microsoft Teams, а для браузерных пользователей использовать OAuth напрямую? Как избежать отображения ошибки аутентификации, когда header-аутентификация не применима?

Условная аутентификация в Chainlit для Microsoft Teams и браузеров требует определения источника запроса и применения соответствующего метода аутентификации. Для этого можно использовать пользовательский middleware или модифицировать существующие callback-функции для динамического выбора между header_auth_callback и OAuth в зависимости от User-Agent или заголовков запроса.


Содержание


Основы аутентификации в Chainlit

Chainlit предоставляет гибкую систему аутентификации, поддерживающую различные методы для удовлетворения разных требований безопасности. По умолчанию Chainlit приложения являются публичными, но для включения аутентификации необходимо определить переменную окружения CHAINLIT_AUTH_SECRET и соответствующие callback-функции.

Платформа поддерживает три основных типа аутентификации:

  1. Password аутентификация - проверка имени пользователя и пароля
  2. OAuth аутентификация - использование сторонних провайдеров
  3. Header аутентификация - проверка заголовков HTTP запросов

Каждый тип аутентификации использует свой собственный callback-механизм, и именно здесь мы можем реализовать условную логику для различения запросов из Microsoft Teams и браузеров. Для доступа к текущему аутентифицированному пользователю в любом месте приложения используется cl.user_session.get("user").


Определение источника запроса

Первым шагом в реализации условной аутентификации является определение источника запроса. Для этого можно использовать различные признаки, присущие запросам из Microsoft Teams:

Проверка User-Agent

python
import re

def is_teams_request(headers):
 user_agent = headers.get('User-Agent', '').lower()
 teams_patterns = [
 'teams',
 'microsoft teams',
 'skype for business'
 ]
 return any(pattern in user_agent for pattern in teams_patterns)

Проверка специфичных заголовков

Microsoft Teams добавляет уникальные заголовки в запросы, которые можно использовать для идентификации:

python
def is_teams_request(headers):
 # Проверка специфичных для Teams заголовков
 teams_headers = [
 'x-ms-teams-device-type',
 'x-ms-teams-client-version',
 'x-ms-teams-request-origin'
 ]
 return any(header in headers for header in teams_headers)

Комбинированный подход

Наиболее надежным будет комбинированный подход, проверяющий несколько признаков одновременно:

python
def detect_request_source(headers):
 """Определяет источник запроса на основе заголовков и User-Agent"""
 user_agent = headers.get('User-Agent', '').lower()
 teams_headers = [
 'x-ms-teams-device-type',
 'x-ms-teams-client-version',
 'x-ms-teams-request-origin'
 ]
 teams_patterns = ['teams', 'microsoft teams', 'skype for business']
 
 has_teams_headers = any(header in headers for header in teams_headers)
 has_teams_ua = any(pattern in user_agent for pattern in teams_patterns)
 
 if has_teams_headers or has_teams_ua:
 return 'teams'
 return 'browser'

Эта функция вернет 'teams' для запросов из Microsoft Teams и 'browser' для обычных браузерных запросов, что позволит нам выбирать соответствующий метод аутентификации.


Реализация условной header аутентификации

Для реализации header аутентификации, активной только для запросов из Microsoft Teams, мы можем создать callback-функцию, которая сначала определяет источник запроса, а затем применяет соответствующую логику.

Базовая реализация

python
import chainlit as cl

async def conditional_auth_callback(headers):
 """Условная аутентификация: header для Teams, OAuth для браузеров"""
 
 # Определяем источник запроса
 source = detect_request_source(headers)
 
 if source == 'teams':
 # Применяем header аутентификацию для Teams
 return await teams_header_auth(headers)
 else:
 # Для браузерных пользователей не аутентифицируем на этом этапе
 # OAuth будет обрабатываться отдельным callback-ом
 return None

async def teams_header_auth(headers):
 """Header аутентификация для Microsoft Teams"""
 
 # Пример: проверка заголовка авторизации
 auth_header = headers.get('Authorization')
 if not auth_header:
 return None
 
 # Здесь должна быть ваша логика валидации токена/заголовка
 # Например, проверка JWT токена или API ключа
 
 # Пример простой проверки заголовка
 if auth_header.startswith('Bearer '):
 token = auth_header[7:]
 # Валидация токена...
 if is_valid_token(token):
 return cl.User(
 identifier="teams_user",
 name="Teams User",
 email="teams@example.com"
 )
 
 return None

Продвинутая реализация с поддержкой fallback

python
async def conditional_auth_callback(headers):
 """Продвинутая условная аутентификация с fallback"""
 
 source = detect_request_source(headers)
 
 try:
 if source == 'teams':
 # Пытаемся применить header аутентификацию
 user = await teams_header_auth(headers)
 if user:
 return user
 
 # Если Teams аутентификация не удалась или это не Teams запрос
 # Возвращаем None, чтобы позволить OAuth обработать запрос
 return None
 
 except Exception as e:
 # Логирование ошибки
 print(f"Ошибка аутентификации: {e}")
 return None

Настройка Chainlit

Для использования этой callback-функции необходимо настроить Chainlit:

python
# В main.py или chainlit.md

@cl.on_chat_start
async def on_chat_start():
 # Ваша логика при старте чата
 pass

# Регистрация callback-а аутентификации
cl.user_session.set("auth_callback", conditional_auth_callback)

Важно отметить, что для header аутентификации нам не нужно устанавливать OAuth переменные окружения, так как мы будем использовать OAuth только для браузерных пользователей через отдельный механизм.


Настройка OAuth для браузерных пользователей

Для браузерных пользователей мы можем использовать стандартную OAuth аутентификацию Chainlit. Для этого необходимо настроить соответствующие переменные окружения и callback-функцию.

Настройка переменных окружения

bash
# Для Google OAuth
OAUTH_GOOGLE_CLIENT_ID=ваш_идентификатор_клиента
OAUTH_GOOGLE_CLIENT_SECRET=ваш_секретный_ключ
OAUTH_GOOGLE_SCOPE=openid email profile

# Для GitHub OAuth
OAUTH_GITHUB_CLIENT_ID=ваш_идентификатор_клиента
OAUTH_GITHUB_CLIENT_SECRET=ваш_секретный_ключ
OAUTH_GITHUB_SCOPE=read:user user:email

# Для Microsoft OAuth
OAUTH_MICROSOFT_CLIENT_ID=ваш_идентификатор_клиента
OAUTH_MICROSOFT_CLIENT_SECRET=ваш_секретный_ключ
OAUTH_MICROSOFT_SCOPE=openid email profile

Реализация OAuth callback

python
import os
from chainlit.oauth_providers import OAuthProvider

# Создание OAuth провайдеров
oauth_providers = {
 "google": OAuthProvider(
 name="Google",
 client_id=os.getenv("OAUTH_GOOGLE_CLIENT_ID"),
 client_secret=os.getenv("OAUTH_GOOGLE_CLIENT_SECRET"),
 authorize_url="https://accounts.google.com/o/oauth2/v2/auth",
 token_url="https://oauth2.googleapis.com/token",
 userinfo_url="https://www.googleapis.com/oauth2/v2/userinfo",
 scope=os.getenv("OAUTH_GOOGLE_SCOPE", "openid email profile"),
 ),
 "github": OAuthProvider(
 name="GitHub",
 client_id=os.getenv("OAUTH_GITHUB_CLIENT_ID"),
 client_secret=os.getenv("OAUTH_GITHUB_CLIENT_SECRET"),
 authorize_url="https://github.com/login/oauth/authorize",
 token_url="https://github.com/login/oauth/access_token",
 userinfo_url="https://api.github.com/user",
 scope=os.getenv("OAUTH_GITHUB_SCOPE", "read:user user:email"),
 )
}

async def oauth_callback(provider: str, token: dict, user_info: dict):
 """Callback функция для OAuth аутентификации"""
 
 # Здесь можно добавить дополнительную логику обработки пользователя
 # Например, проверку домена email или обновление информации в базе данных
 
 return cl.User(
 identifier=user_info.get("id"),
 name=user_info.get("name"),
 email=user_info.get("email"),
 picture=user_info.get("picture"),
 provider=provider
 )

# Настройка Chainlit для OAuth
cl.oauth_providers = oauth_providers
cl.oauth_callback = oauth_callback

Интеграция с условной аутентификацией

Чтобы сделать OAuth доступным только для браузерных пользователей, мы можем модифицировать наш основной подход:

python
async def conditional_auth_callback(headers):
 """Условная аутентификация с поддержкой OAuth для браузеров"""
 
 source = detect_request_source(headers)
 
 if source == 'teams':
 # Только header аутентификация для Teams
 return await teams_header_auth(headers)
 else:
 # Для браузеров не применяем header аутентификацию
 # OAuth будет обрабатываться автоматически через cl.oauth_callback
 return None

Таким образом, браузерные пользователи будут перенаправлены на страницу OAuth входа, а пользователи Teams будут проходить аутентификацию через заголовки.


Обработка ошибок аутентификации

Одной из ключевых задач является avoiding authentication errors when header authentication is not applicable. Для этого необходимо реализовать корректную обработку сценариев, когда header аутентификация не может быть применена.

Базовая обработка ошибок

python
@cl.on_chat_start
async def on_chat_start():
 try:
 # Проверка, аутентифицирован ли пользователь
 user = cl.user_session.get("user")
 if not user:
 # Если пользователь не аутентифицирован, но запрос не из Teams
 # Показываем сообщение о необходимости авторизации
 await cl.Message(
 content="Пожалуйста, авторизуйтесь для продолжения.",
 author="Система"
 ).send()
 except Exception as e:
 # Логирование ошибки
 print(f"Ошибка при обработке чата: {e}")
 await cl.Message(
 content="Произошла ошибка. Пожалуйста, попробуйте еще раз.",
 author="Система"
 ).send()

Перехват ошибок аутентификации

Chainlit позволяет перехватывать ошибки аутентификации через специальный обработчик:

python
@cl.exception_handler
async def handle_auth_error(exc: Exception):
 """Обработчик ошибок аутентификации"""
 
 if isinstance(exc, cl.AuthenticationError):
 # Определяем источник запроса
 headers = cl.context.headers
 source = detect_request_source(headers)
 
 if source == 'teams':
 # Для Teams пользователей показываем сообщение о проблеме с header аутентификацией
 await cl.Message(
 content="Ошибка аутентификации. Пожалуйста, проверьте ваш токен доступа.",
 author="Система"
 ).send()
 else:
 # Для браузерных пользователей перенаправляем на OAuth
 await cl.Message(
 content="Пожалуйста, войдите через ваш аккаунт для продолжения.",
 author="Система"
 ).send()
 # Можно добавить кнопку для перенаправления на OAuth
 await cl.Message(
 content="[Войти](/oauth/google)",
 author="Система"
 ).send()
 
 return True # Ошибка обработана
 
 # Для других ошибок используем стандартную обработку
 return False

Гибридный подход с пользовательским интерфейсом

Для улучшения пользовательского опыта можно реализовать гибридный подход, который автоматически определяет доступные методы аутентификации:

python
async def get_available_auth_methods(headers):
 """Определяет доступные методы аутентификации для текущего запроса"""
 
 source = detect_request_source(headers)
 auth_methods = []
 
 if source == 'teams':
 # Для Teams - только header аутентификация
 auth_methods.append({
 'type': 'header',
 'name': 'Токен доступа',
 'description': 'Введите ваш токен доступа'
 })
 else:
 # Для браузеров - OAuth провайдеры
 for provider_name, provider in cl.oauth_providers.items():
 auth_methods.append({
 'type': 'oauth',
 'name': provider.name,
 'description': f'Войти через {provider.name}',
 'url': f"/oauth/{provider_name}"
 })
 
 return auth_methods

@cl.on_chat_start
async def on_chat_start():
 """Старт чата с динамическим выбором метода аутентификации"""
 
 user = cl.user_session.get("user")
 if not user:
 headers = cl.context.headers
 auth_methods = await get_available_auth_methods(headers)
 
 # Показываем сообщение с доступными методами аутентификации
 message_content = "Выберите метод аутентификации:\n\n"
 for method in auth_methods:
 if method['type'] == 'header':
 message_content += f"• **{method['name']}**: {method['description']}\n"
 else:
 message_content += f"• [{method['name']}]({method['url']}): {method['description']}\n"
 
 await cl.Message(
 content=message_content,
 author="Система"
 ).send()

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


Источники

  1. Chainlit Authentication Overview — Обзор всех методов аутентификации в Chainlit: https://docs.chainlit.io/authentication/overview
  2. Header Authentication — Подробная документация по header аутентификации: https://docs.chainlit.io/authentication/header
  3. OAuth Authentication — Руководство по настройке OAuth провайдеров: https://docs.chainlit.io/authentication/oauth
  4. Chainlit User Session — Работа с пользовательскими сессиями: https://docs.chainlit.io/python-reference/user-session

Заключение

Условная аутентификация в Chainlit для разных источников запросов требует тщательного планирования и реализации. Ключевыми элементами решения являются:

  1. Определение источника запроса - использование User-Agent и специфичных заголовков для идентификации Microsoft Teams
  2. Условный callback аутентификации - динамический выбор между header и OAuth методами
  3. Корректная обработка ошибок - избегание ошибок аутентификации при недоступности header метода
  4. Гибридный пользовательский интерфейс - предоставление пользователю понятных инструкций

Правильно реализованная условная аутентификация обеспечит бесшовный пользовательский опыт как для Microsoft Teams, так и для обычных браузерных пользователей, при этом поддерживая необходимые уровни безопасности для каждого типа запроса.

C

Chainlit приложения по умолчанию являются публичными. Для включения аутентификации необходимо определить переменную окружения CHAINLIT_AUTH_SECRET и добавить callback-функции аутентификации. Платформа поддерживает три типа аутентификации: password auth, OAuth и header auth. Каждый callback принимает разные входные данные и опционально возвращает объект cl.User. Доступ к текущему аутентифицированному пользователю осуществляется через cl.user_session.get("user").

C

Header auth - это простой способ аутентификации с использованием заголовков, обычно для делегирования аутентификации обратному прокси. Функция header_auth_callback вызывается с заголовками запроса и должна вернуть объект cl.User при успешной аутентификации или None при неудаче. Пример реализации: проверка значения заголовка test-header на соответствие test-value с возвратом пользователя только при совпадении. Этот метод особенно подходит для интеграции с системами вроде Microsoft Teams.

C

OAuth позволяет использовать сторонние сервисы для аутентификации. Для активации необходимо определить callback OAuth в коде и переменные окружения для провайдера. Поддерживаются GitHub, GitLab, Google, Azure AD, Okta и Descope с соответствующими callback URL. Для Google требуется установка переменных окружения OAUTH_GOOGLE_CLIENT_ID и OAUTH_GOOGLE_CLIENT_SECRET. Этот метод идеально подходит для браузерных пользователей, обеспечивая безопасную вход через внешние сервисы.

Авторы
C
Разработчики
Источники
Chainlit / Платформа разработки чат-ботов
Платформа разработки чат-ботов
Проверено модерацией
НейроОтветы
Модерация