# How to Configure OAuth or Integrate with Existing Authentication Systems in Local Deep Research

> Learn to configure OAuth or integrate with existing authentication systems in Local Deep Research. Extend LDR's session model with Google GitHub or Azure AD for seamless user access.

- Repository: [learningcircuit/local-deep-research](https://github.com/learningcircuit/local-deep-research)
- Tags: how-to-guide
- Published: 2026-03-05

---

**Local Deep Research (LDR) relies on a session-based authentication model that you can extend to support external OAuth providers like Google, GitHub, or Azure AD by implementing a custom callback endpoint in [`src/local_deep_research/mcp/server.py`](https://github.com/learningcircuit/local-deep-research/blob/main/src/local_deep_research/mcp/server.py) that exchanges provider tokens for LDR session cookies.**

Local Deep Research uses secure session cookies as the single source of truth for all authenticated web and API traffic. When integrating external identity providers, you bridge the OAuth flow into this existing architecture rather than replacing it, ensuring per-user encrypted databases and rate-limiting continue to function unchanged.

## Understanding LDR's Session-Based Architecture

LDR authenticates every request to `/api/*` endpoints using a session cookie established at login. According to the source code in [`src/local_deep_research/mcp/server.py`](https://github.com/learningcircuit/local-deep-research/blob/main/src/local_deep_research/mcp/server.py), the application uses a **SessionManager** class to create and validate these sessions, with explicit comments highlighting the requirement for OAuth, rate limiting, and input validation controls at the top of the file.

The standard login flow documented in [`docs/api-quickstart.md`](https://github.com/learningcircuit/local-deep-research/blob/main/docs/api-quickstart.md) follows three steps: fetch a CSRF token from `/auth/csrf-token`, submit credentials to `/auth/login`, and receive a `session` cookie for subsequent requests. Your OAuth integration must produce the same cookie outcome so that existing research pipelines and encrypted per-user databases require zero modifications.

## Prerequisites for OAuth Integration

Before modifying code, register your application with your chosen identity provider to obtain a **client ID** and **client secret**. Then expose these credentials to LDR via environment variables following the pattern established in [`docs/CONFIGURATION.md`](https://github.com/learningcircuit/local-deep-research/blob/main/docs/CONFIGURATION.md):

- `LDR_OAUTH_CLIENT_ID`
- `LDR_OAUTH_CLIENT_SECRET`

LDR reads configuration from the environment at startup using the same mechanism that handles variables like `LDR_NEWS_SCHEDULER_ALLOW_API_CONTROL`, ensuring your OAuth credentials are available to the auth routes at runtime.

## Implementing the OAuth Callback Endpoint

### Add the OAuth Route in server.py

Extend [`src/local_deep_research/mcp/server.py`](https://github.com/learningcircuit/local-deep-research/blob/main/src/local_deep_research/mcp/server.py) with a new route—typically `GET /auth/oauth/callback`—that receives the authorization code from your provider. This endpoint must exchange the code for an access token, retrieve the user's profile, and invoke the internal session creation logic.

```python

# Inside src/local_deep_research/mcp/server.py

import os
import httpx
from fastapi import Request, Query
from fastapi.responses import RedirectResponse

@router.get("/auth/oauth/callback")
async def oauth_callback(request: Request, code: str = Query(...)):
    # Exchange code for token with the provider

    token_resp = await httpx.post(
        "https://provider.com/oauth/token",
        data={
            "client_id": os.getenv("LDR_OAUTH_CLIENT_ID"),
            "client_secret": os.getenv("LDR_OAUTH_CLIENT_SECRET"),
            "code": code,
            "grant_type": "authorization_code"
        },
    )
    token_resp.raise_for_status()
    access_token = token_resp.json()["access_token"]
    
    # Fetch user profile from provider

    provider_user = await fetch_provider_profile(access_token)
    
    # Continue with local user mapping and session creation...

```

### Map External Users to Local Records

After retrieving the provider's user profile (email or unique ID), check for an existing entry in LDR's central **auth.db**. The schema documented in [`docs/architecture/DATABASE_SCHEMA.md`](https://github.com/learningcircuit/local-deep-research/blob/main/docs/architecture/DATABASE_SCHEMA.md) shows that LDR stores only usernames in the `auth` table and never stores passwords locally. If no matching user exists, create a new row to establish the local identity.

```python
def get_or_create_local_user(email: str):
    # Query auth.db for existing user

    user = db.query("SELECT id FROM auth WHERE username = ?", (email,)).fetchone()
    if not user:
        # Create minimal local record

        cursor = db.execute("INSERT INTO auth (username) VALUES (?)", (email,))
        user_id = cursor.lastrowid
    else:
        user_id = user["id"]
    return user_id

```

### Issue the Session Cookie

Reuse the existing `SessionManager.login_user()` or `SessionManager.create_session()` methods to generate the session object. This guarantees that the encrypted per-user databases and API rate-limiting logic treat OAuth users identically to password-based users.

```python
from local_deep_research.mcp.server import SessionManager

# After mapping the user...

session = SessionManager.create_session(user_id)

response = RedirectResponse(url="/")
response.set_cookie(
    key="session", 
    value=session.id, 
    httponly=True, 
    secure=True, 
    samesite="lax"
)
return response

```

## Securing the Integration

Protect the OAuth callback endpoint against cross-site request forgery by mirroring the CSRF token behavior used in the standard login flow. The endpoint `/auth/csrf-token` documented in [`docs/api-quickstart.md`](https://github.com/learningcircuit/local-deep-research/blob/main/docs/api-quickstart.md) generates tokens that should be validated in your OAuth callback or included in the state parameter during the initial OAuth redirect.

Additionally, ensure your OAuth callback validates the `state` parameter to prevent CSRF attacks and strictly enforces HTTPS for the cookie `secure` flag, as the security controls noted in [`server.py`](https://github.com/learningcircuit/local-deep-research/blob/main/server.py) require.

## Optional: Enabling Stateless API Tokens

For CLI or service-to-service authentication that cannot use cookies, implement an endpoint that exchanges the provider's OAuth access token for a short-lived LDR API token. The test suite in [`tests/news/test_api_client_patterns_behavior.py`](https://github.com/learningcircuit/local-deep-research/blob/main/tests/news/test_api_client_patterns_behavior.py) demonstrates the expected Bearer token refresh logic and validation patterns you should follow for stateless authentication.

## Summary

- **Session cookies are the core requirement**: All LDR features expect the `session` cookie; OAuth integration must produce this same artifact.
- **Extend [`src/local_deep_research/mcp/server.py`](https://github.com/learningcircuit/local-deep-research/blob/main/src/local_deep_research/mcp/server.py)**: Add `/auth/oauth/callback` to handle the provider's authorization code exchange.
- **Map to local users**: Use the `auth.db` schema to link external identities to local rows, preserving per-user encryption boundaries.
- **Reuse SessionManager**: Call `create_session()` to ensure OAuth users inherit existing rate-limiting and database isolation features.
- **Protect against CSRF**: Validate CSRF tokens or state parameters in the OAuth flow using the existing `/auth/csrf-token` endpoint.

## Frequently Asked Questions

### Can I integrate multiple OAuth providers simultaneously?

Yes. You can implement multiple callback endpoints (e.g., `/auth/oauth/github/callback` and `/auth/oauth/google/callback`) in [`server.py`](https://github.com/learningcircuit/local-deep-research/blob/main/server.py), each normalizing the provider's unique user identifier to the local `auth.db` username field. The session cookie issued is provider-agnostic, so the rest of LDR functions identically regardless of the authentication source.

### Where are user credentials stored when using OAuth?

Local Deep Research never stores passwords or OAuth tokens locally. As shown in [`docs/architecture/DATABASE_SCHEMA.md`](https://github.com/learningcircuit/local-deep-research/blob/main/docs/architecture/DATABASE_SCHEMA.md), the `auth` table contains only the username. The OAuth access token exists only transiently during the callback exchange, and the persistent authentication mechanism remains the LDR session cookie managed by `SessionManager`.

### How do I protect the OAuth callback from CSRF attacks?

Include a CSRF token in the OAuth `state` parameter when redirecting to the provider, then validate this token in your `/auth/oauth/callback` handler. The existing endpoint `/auth/csrf-token` documented in [`docs/api-quickstart.md`](https://github.com/learningcircuit/local-deep-research/blob/main/docs/api-quickstart.md) generates tokens using the same secret key as the session manager, ensuring cryptographic consistency across authentication methods.

### Does OAuth integration affect per-user encrypted databases?

No. Because OAuth users are mapped to records in `auth.db` and issued standard session cookies through `SessionManager.create_session()`, the existing logic that derives SQLCipher encryption keys from the user ID remains unchanged. Each OAuth user receives the same isolated, encrypted database instance that password users receive.