# How to Configure Custom Authentication for the LiteLLM Proxy

> Learn to configure custom authentication for LiteLLM proxy using Python functions. Implement bespoke security policies easily without altering core code.

- Repository: [Berri AI/litellm](https://github.com/BerriAI/litellm)
- Tags: how-to-guide
- Published: 2026-03-26

---

**LiteLLM proxy delegates API key validation to user-defined Python functions via the `custom_auth` configuration setting, enabling bespoke security policies without modifying core code.**

The BerriAI/litellm repository provides a proxy server that can offload authentication logic to custom code. By configuring the `custom_auth` parameter in your proxy YAML file, you implement tenant-specific rate limits, IP allow-lists, or external identity provider integrations while preserving the default request pipeline.

## How Custom Authentication Works in LiteLLM

LiteLLM’s proxy server resolves the custom authentication hook at startup and invokes it during every incoming request. The mechanism relies on dynamic function resolution and middleware integration.

### Configuration Loading and Resolution

When the proxy starts, [`litellm/proxy/proxy_server.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/proxy_server.py) reads the `general_settings` section of your configuration file. If `custom_auth` is present, the server uses `get_instance_fn` from [`litellm/proxy/types_utils/utils.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/types_utils/utils.py) to resolve the dotted path into a callable object:

```python

# litellm/proxy/proxy_server.py (lines 44-51)

custom_auth = general_settings.get("custom_auth", None)
if custom_auth is not None:
    user_custom_auth = get_instance_fn(
        value=custom_auth, config_file_path=config_file_path
    )

```

The resulting function is stored in the global `user_custom_auth` variable for later use.

### Request-Time Execution

During request processing, the authentication middleware in [`litellm/proxy/auth/user_api_key_auth.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/auth/user_api_key_auth.py) executes after the built-in enterprise hooks. If `user_custom_auth` is defined, the middleware invokes your function with the FastAPI `Request` object and the extracted API key:

```python

# litellm/proxy/auth/user_api_key_auth.py (lines 606-608)

elif user_custom_auth is not None:
    response = await user_custom_auth(request=request, api_key=api_key)

```

Your custom function must follow these semantics:

- Return `None` or any truthy object to indicate authentication succeeded
- Raise an `HTTPException` (or any exception caught by `UserAPIKeyAuthExceptionHandler`) to trigger a **401 Unauthorized** response with the exception's detail message

## Implementing a Custom Authentication Function

Create a Python module accessible from your `PYTHONPATH`. The function must accept `(request: Request, api_key: str)` and be async-compatible.

```python

# custom_auth/user_api_key_auth.py

import os
from fastapi import Request, HTTPException, status

async def user_api_key_auth(request: Request, api_key: str):
    """
    Reject keys that do not start with a required prefix defined in ENV.
    """
    required_prefix = os.getenv("CUSTOM_AUTH_PREFIX", "my-prefix-")
    if not api_key.startswith(required_prefix):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication Error, API key missing required prefix",
        )
    # Optionally attach data to request.state for downstream handlers

    request.state.custom_user = {"prefix_ok": True}
    return None  # Authentication succeeded

```

### Function Signature and Return Values

The callable registered in `custom_auth` must handle two parameters:

- **`request`**: The FastAPI `Request` object, allowing access to headers, client IP, and state
- **`api_key`**: The string extracted from the request's authorization header

Successful validation should return `None` or a truthy value. Failed validation must raise `HTTPException` with an appropriate status code and detail message.

## Configuring the Proxy to Use Custom Auth

Add the `custom_auth` key to your proxy configuration file under `general_settings`. The value must be the dotted import path to your callable.

```yaml

# config.yaml

general_settings:
  master_key: "$PROXY_MASTER_KEY"
  custom_auth: custom_auth.user_api_key_auth

```

Start the proxy server with your configuration:

```bash
export PROXY_MASTER_KEY=sk-...
export CUSTOM_AUTH_PREFIX=my-prefix-
poetry run litellm --config config.yaml

```

The proxy resolves `custom_auth.user_api_key_auth` at startup using the same `get_instance_fn` utility found in [`litellm/proxy/types_utils/utils.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/types_utils/utils.py).

## Testing and Validation

When properly configured, requests behave according to the logic in [`tests/proxy_unit_tests/test_proxy_custom_auth.py`](https://github.com/BerriAI/litellm/blob/main/tests/proxy_unit_tests/test_proxy_custom_auth.py):

- **Valid requests**: Keys matching your custom logic proceed to model routing
- **Invalid requests**: Return HTTP 401 with your custom error detail:

```json
{
  "detail": "Authentication Error, API key missing required prefix"
}

```

This behavior applies to both HTTP and WebSocket endpoints, allowing consistent authentication across all proxy interfaces.

## Summary

- **Configuration**: Set `custom_auth` in `general_settings` to a dotted Python path
- **Resolution**: [`litellm/proxy/proxy_server.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/proxy_server.py) loads the function via `get_instance_fn` at startup
- **Execution**: [`litellm/proxy/auth/user_api_key_auth.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/auth/user_api_key_auth.py) invokes your function after default checks
- **Contract**: Return `None` for success, raise `HTTPException` for 401 failures
- **Use cases**: IP allow-lists, tenant-specific rate limits, SSO/OAuth integration, and audit logging

## Frequently Asked Questions

### What is the exact function signature required for custom authentication?

Your function must accept `(request: Request, api_key: str)` and return an awaitable. According to the source code in [`litellm/proxy/auth/user_api_key_auth.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/auth/user_api_key_auth.py), the middleware calls `await user_custom_auth(request=request, api_key=api_key)`. The function should return `None` or a truthy object on success, or raise `HTTPException` to reject the request.

### Can I use custom authentication alongside the default API key validation?

Yes. The custom auth function executes **after** the built-in enterprise hook and default validation steps in [`user_api_key_auth.py`](https://github.com/BerriAI/litellm/blob/main/user_api_key_auth.py). This means you can layer additional checks—such as IP restrictions or external identity verification—on top of LiteLLM's standard API key validation without replacing it.

### How does the proxy locate my custom authentication module?

The proxy uses `get_instance_fn` from [`litellm/proxy/types_utils/utils.py`](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/types_utils/utils.py) to resolve the dotted path provided in `custom_auth`. The module containing your function must be importable from the Python environment where you launch the proxy. Typically, you place the module in your project directory or install it as a package before starting the server.

### What happens if my custom authentication function raises an exception?

If your function raises `HTTPException` or any exception caught by `UserAPIKeyAuthExceptionHandler`, the proxy returns a **401 Unauthorized** response with the exception's detail message. This matches the test cases in [`tests/proxy_unit_tests/test_proxy_custom_auth.py`](https://github.com/BerriAI/litellm/blob/main/tests/proxy_unit_tests/test_proxy_custom_auth.py), ensuring consistent error handling for invalid authentication attempts.