# How to Integrate Custom Authentication Providers in Claude-Code-Telegram

> Learn how to integrate custom authentication providers in Claude-Code-Telegram. Implement AuthProvider and register it with AuthenticationManager for flexible access control beyond default options.

- Repository: [Richard A/claude-code-telegram](https://github.com/richardatct/claude-code-telegram)
- Tags: how-to-guide
- Published: 2026-02-20

---

**You can integrate custom authentication providers by implementing the `AuthProvider` abstract base class and registering your implementation with the `AuthenticationManager` during bot startup.**

The `RichardAtCT/claude-code-telegram` repository uses a pluggable authentication architecture that supports custom authentication providers beyond the default whitelist and token-based methods. This design allows developers to integrate enterprise identity providers, OAuth2 services, or LDAP directories without modifying core bot logic.

## Understanding the Pluggable Authentication Architecture

The authentication system is built around three core components defined in [`src/security/auth.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/auth.py): an abstract base class that defines the provider contract, concrete implementations for common methods, and a manager that orchestrates authentication attempts.

### The AuthProvider Abstract Base Class

Every authentication provider must inherit from `AuthProvider`, which defines the required interface at lines 50-60 of [`src/security/auth.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/auth.py):

```python
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional

class AuthProvider(ABC):
    @abstractmethod
    async def authenticate(self, user_id: int, credentials: Dict[str, Any]) -> bool:
        """Validate credentials and return True if authentication succeeds."""
        pass

    @abstractmethod
    async def get_user_info(self, user_id: int) -> Optional[Dict[str, Any]]:
        """Return user metadata (permissions, roles, etc.) after authentication."""
        pass

```

### Built-in Providers

The repository includes two reference implementations in [`src/security/auth.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/auth.py):

- **`WhitelistAuthProvider`** (lines 62-84): Validates users against a static list of allowed Telegram IDs
- **`TokenAuthProvider`** (lines 92-151): Validates cryptographically signed tokens using a shared secret

These implementations demonstrate the asynchronous pattern required for custom providers.

### The AuthenticationManager Orchestrator

The `AuthenticationManager` class (lines 163-226 of [`src/security/auth.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/auth.py)) maintains a list of providers and iterates through them during authentication attempts. It handles session creation, refresh, and expiration without requiring knowledge of specific provider implementations.

## Implementing a Custom Authentication Provider

To add a custom authentication provider such as OAuth2, LDAP, or a proprietary enterprise SSO, you need to create a concrete implementation, instantiate it during bot startup, and register it with the authentication manager.

### Step 1: Create the Provider Class

Create a new file (e.g., [`src/security/custom_oauth.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/custom_oauth.py)) that subclasses `AuthProvider` and implements the required asynchronous methods:

```python

# src/security/custom_oauth.py

from typing import Any, Dict, Optional
from src.security.auth import AuthProvider
import httpx
import structlog

log = structlog.get_logger()

class OAuthProvider(AuthProvider):
    """
    Example OAuth2 provider that validates a JWT issued by an external IdP.
    """

    def __init__(self, jwks_url: str, allowed_aud: str):
        self.jwks_url = jwks_url
        self.allowed_aud = allowed_aud

    async def _fetch_jwks(self) -> Dict[str, Any]:
        async with httpx.AsyncClient() as client:
            resp = await client.get(self.jwks_url)
            resp.raise_for_status()
            return resp.json()

    async def authenticate(self, user_id: int, credentials: Dict[str, Any]) -> bool:
        token = credentials.get("oauth_token")
        if not token:
            log.warning("OAuth auth failed – no token", user_id=user_id)
            return False

        # Verify JWT (pseudo-code – real verification would use pyjwt / jose)

        try:
            jwks = await self._fetch_jwks()
            # ... decode JWT, check signature with jwks, verify `aud` and `sub`

            # For brevity we assume a helper `verify_jwt` exists.

            payload = verify_jwt(token, jwks, audience=self.allowed_aud)
        except Exception as exc:
            log.error("OAuth token validation error", user_id=user_id, error=str(exc))
            return False

        # The token's `sub` claim should match the Telegram user ID (optional)

        return payload.get("sub") == str(user_id)

    async def get_user_info(self, user_id: int) -> Optional[Dict[str, Any]]:
        # Return whatever info the IdP supplies; here we expose name & email.

        # In a real implementation you would call a user-info endpoint.

        return {
            "user_id": user_id,
            "auth_type": "oauth",
            "permissions": ["basic"],
        }

```

### Step 2: Wire the Provider into the Bot

Modify [`src/bot/middleware/security.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/bot/middleware/security.py) to instantiate your custom provider and append it to the providers list passed to `AuthenticationManager`:

```python

# src/bot/middleware/security.py (excerpt)

from src.security.auth import AuthenticationManager, WhitelistAuthProvider, TokenAuthProvider
from src.security.custom_oauth import OAuthProvider
from src.config.settings import settings

# Existing providers

providers = [
    WhitelistAuthProvider(allowed_users=settings.allowed_users, allow_all_dev=settings.dev_mode),
]

# Optional token auth (controlled by feature flag)

if settings.enable_token_auth and settings.auth_token_secret:
    providers.append(
        TokenAuthProvider(
            secret=settings.auth_token_secret,
            storage=TokenAuthProvider.InMemoryTokenStorage(),
        )
    )

# Add your custom provider here

providers.append(
    OAuthProvider(
        jwks_url="https://example-idp.com/.well-known/jwks.json",
        allowed_aud="my-telegram-bot",
    )
)

# Build the manager once; the same instance is injected into the bot's context.

auth_manager = AuthenticationManager(providers=providers)

```

### Step 3: Use the Provider in Handlers

Telegram handlers interact with the authentication system through the manager, not individual providers. Pass credentials containing your custom provider's expected keys:

```python

# src/bot/handlers/message.py (simplified)

async def handle_message(update, context):
    user_id = update.effective_user.id
    # Example: the client sends an OAuth token in the message text prefixed with "token:"

    if update.message.text.startswith("token:"):
        token = update.message.text.removeprefix("token:").strip()
        credentials = {"oauth_token": token}
        ok = await context.bot_data["auth_manager"].authenticate_user(
            user_id=user_id, credentials=credentials
        )
        if ok:
            await update.message.reply_text("✅ Authenticated via OAuth!")
        else:
            await update.message.reply_text("❌ OAuth token invalid.")
    else:
        # fallback to whitelist / token auth as before

        pass

```

## Configuration and Feature Flags

The repository uses Pydantic settings to control authentication behavior. In [`src/config/settings.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/settings.py) (lines 45-49), you can enable or disable the built-in token authentication:

```python

# src/config/settings.py

class Settings(BaseSettings):
    # ... other settings ...

    enable_token_auth: bool = False
    auth_token_secret: Optional[str] = None
    allowed_users: List[int] = []

```

The [`src/config/features.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/features.py) file exposes runtime feature detection, including `token_auth_enabled`, which the middleware checks before instantiating the `TokenAuthProvider`.

## Summary

- **Pluggable Architecture**: The `AuthProvider` abstract base class in [`src/security/auth.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/auth.py) defines a clear contract for all authentication methods.
- **Simple Integration**: Create a subclass implementing `authenticate()` and `get_user_info()`, then append it to the providers list in [`src/bot/middleware/security.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/bot/middleware/security.py).
- **No Core Changes**: The `AuthenticationManager` orchestrates providers without requiring modifications to handlers or session management.
- **Feature Flags**: Built-in providers respect settings in [`src/config/settings.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/settings.py), allowing you to mix custom and default authentication methods.

## Frequently Asked Questions

### What is the AuthProvider interface?

The `AuthProvider` interface is an abstract base class defined in [`src/security/auth.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/security/auth.py) (lines 50-60) that requires two asynchronous methods: `authenticate(user_id, credentials)` which returns a boolean indicating success, and `get_user_info(user_id)` which returns a dictionary containing user metadata such as permissions and roles.

### How do I enable or disable specific providers?

You control built-in providers through Pydantic settings in [`src/config/settings.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/settings.py). Set `enable_token_auth` to `True` or `False` to toggle token authentication, or modify the `allowed_users` list for the whitelist provider. Custom providers can be conditionally appended to the providers list in [`src/bot/middleware/security.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/bot/middleware/security.py) based on your own configuration logic.

### Can I chain multiple authentication providers?

Yes, the `AuthenticationManager` accepts a list of providers and iterates through them in order during authentication attempts. The first provider that returns `True` from its `authenticate` method determines the successful authentication method. This allows you to implement fallback chains, such as checking a whitelist first, then falling back to OAuth or token validation.

### Where should I store sensitive credentials for custom providers?

Store sensitive configuration such as JWKS URLs, client secrets, or API keys in environment variables and load them through [`src/config/settings.py`](https://github.com/RichardAtCT/claude-code-telegram/blob/main/src/config/settings.py) using Pydantic's `BaseSettings`. Never hardcode secrets in your provider implementation. For runtime secrets like OAuth tokens, use the `credentials` dictionary passed to the `authenticate` method rather than storing them in the provider instance.