# How to Implement Custom OAuth Providers Beyond GitHub in LMForge LLMOps

> Integrate custom OAuth providers into LMForge LLMOps effortlessly. Extend the abstract OAuth class, implement key methods, and register your provider for seamless authentication beyond GitHub.

- Repository: [Haohao/lmforge-end-to-end-llmops-platform-for-multi-model-agents](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents)
- Tags: how-to-guide
- Published: 2026-03-03

---

**To implement custom OAuth providers in the LMForge platform, extend the abstract `OAuth` base class in [`api/pkg/oauth/oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/pkg/oauth/oauth.py), implement five required methods, register the provider in `OAuthService.get_all_oauth()`, and configure the corresponding environment variables—no changes to routing or handlers are necessary.**

The LMForge end-to-end LLMOps platform for multi-model agents ships with a pluggable authentication architecture that makes it straightforward to implement custom OAuth providers beyond the default GitHub integration. Whether you need Google, Microsoft, Gitee, or any OAuth-compatible service, the platform's generic routing system automatically exposes new providers once registered. This guide provides the exact file paths, method signatures, and code implementations required to extend the authentication system.

## Understanding the Pluggable OAuth Architecture

The authentication system centers on an abstract base class `OAuth` defined in [`api/pkg/oauth/oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/pkg/oauth/oauth.py). This class defines the contract that all providers must follow, including the `OAuthUserInfo` dataclass that standardizes user data across different identity sources. The `OAuthHandler` in [`api/internal/handler/oauth_handler.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/handler/oauth_handler.py) and the router in [`api/internal/router/router.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/router/router.py) use generic URL patterns—`/oauth/<provider_name>` and `/oauth/authorize/<provider_name>`—meaning new providers work immediately after registration without additional endpoint configuration.

## Step 1: Create the Provider Class

Create a new file under `api/pkg/oauth/` (for example, [`google_oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/google_oauth.py)). Your class must inherit from `OAuth` and implement all abstract methods to handle provider-specific authorization URLs, token exchange, and user data transformation.

### Implementing the Required Methods

Every custom provider must implement five specific methods: `get_provider()`, `get_authorization_url()`, `get_access_token()`, `get_raw_user_info()`, and `_transform_user_info()`. The following example demonstrates a complete Google OAuth implementation:

```python

# api/pkg/oauth/google_oauth.py

import urllib.parse
import requests
from .oauth import OAuth, OAuthUserInfo
import os
import dotenv
import certifi

dotenv.load_dotenv()
os.environ["SSL_CERT_FILE"] = certifi.where()


class GoogleOAuth(OAuth):
    """Google OAuth provider implementation for LMForge"""

    _AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth"
    _ACCESS_TOKEN_URL = "https://oauth2.googleapis.com/token"
    _USER_INFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo"

    def get_provider(self) -> str:
        return "google"

    def get_authorization_url(self) -> str:
        params = {
            "client_id": self.client_id,
            "redirect_uri": self.redirect_uri,
            "response_type": "code",
            "scope": "openid email profile",
            "access_type": "offline",
            "prompt": "consent",
        }
        return f"{self._AUTHORIZE_URL}?{urllib.parse.urlencode(params)}"

    def get_access_token(self, code: str) -> str:
        data = {
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "code": code,
            "grant_type": "authorization_code",
            "redirect_uri": self.redirect_uri,
        }
        resp = requests.post(self._ACCESS_TOKEN_URL, data=data)
        resp.raise_for_status()
        token = resp.json().get("access_token")
        if not token:
            raise ValueError(f"Google OAuth failed: {resp.text}")
        return token

    def get_raw_user_info(self, token: str) -> dict:
        headers = {"Authorization": f"Bearer {token}"}
        resp = requests.get(self._USER_INFO_URL, headers=headers)
        resp.raise_for_status()
        return resp.json()

    def _transform_user_info(self, raw_info: dict) -> OAuthUserInfo:
        return OAuthUserInfo(
            id=str(raw_info.get("id")),
            name=raw_info.get("name") or raw_info.get("email"),
            email=raw_info.get("email"),
        )

```

Key implementation details include storing provider URLs as class constants and ensuring the OAuth scope requests at least `email` and `profile` permissions. The `_transform_user_info` method maps the provider's raw JSON response to the unified `OAuthUserInfo` dataclass, ensuring consistent user data structure regardless of the identity source.

## Step 2: Configure Environment Variables

Add your provider's credentials to `.env` (and `.env.example` for documentation). The `OAuthService` reads these via `os.getenv` at runtime.

```ini

# Google OAuth credentials

GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=http://your-domain.com/oauth/authorize/google

```

## Step 3: Register the Provider in OAuthService

Edit [`api/internal/service/oauth_service.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/service/oauth_service.py) to import your new class and instantiate it within the `get_all_oauth` class method. This method returns a `dict[str, OAuth]` that maps provider names to their implementations.

```python

# api/internal/service/oauth_service.py

from pkg.oauth import OAuth, GithubOAuth, GoogleOAuth  # Import new provider

...

@classmethod
def get_all_oauth(cls) -> dict[str, OAuth]:
    """Return all integrated OAuth providers."""
    github = GithubOAuth(
        client_id=os.getenv("GITHUB_CLIENT_ID"),
        client_secret=os.getenv("GITHUB_CLIENT_SECRET"),
        redirect_uri=os.getenv("GITHUB_REDIRECT_URI"),
    )
    google = GoogleOAuth(
        client_id=os.getenv("GOOGLE_CLIENT_ID"),
        client_secret=os.getenv("GOOGLE_CLIENT_SECRET"),
        redirect_uri=os.getenv("GOOGLE_REDIRECT_URI"),
    )
    return {
        "github": github,
        "google": google,  # New provider registered here

    }

```

Once added to this dictionary, the existing generic router automatically exposes endpoints at `/oauth/google` and `/oauth/authorize/google` without requiring changes to [`api/internal/router/router.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/router/router.py).

## Step 4: Test the Custom OAuth Flow

Verify your implementation using HTTP requests. The flow returns an authorization URL for redirection and accepts the authorization code to exchange for a JWT matching the `AuthorizeResp` schema in [`api/internal/schema/oauth_schema.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/schema/oauth_schema.py).

```bash

# 1. Retrieve the authorization URL

curl http://localhost/oauth/google

# 2. After user authorization, exchange the code for a JWT

curl -X POST http://localhost/oauth/authorize/google \
     -H "Content-Type: application/json" \
     -d '{"code":"authorization_code_from_redirect"}'

```

A successful response contains `access_token` and `expire_at` fields, confirming the provider integration works end-to-end.

## Key Source Files for OAuth Implementation

The following files comprise the authentication architecture according to the LMForge source code:

- **[`api/pkg/oauth/oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/pkg/oauth/oauth.py)** — Defines the abstract `OAuth` base class and `OAuthUserInfo` dataclass.
- **[`api/pkg/oauth/github_oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/pkg/oauth/github_oauth.py)** — Reference implementation demonstrating the required pattern.
- **[`api/internal/service/oauth_service.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/service/oauth_service.py)** — Central registration point for all OAuth providers via `get_all_oauth()`.
- **[`api/internal/handler/oauth_handler.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/handler/oauth_handler.py)** — Generic HTTP handlers that process OAuth flows for any registered provider.
- **[`api/internal/router/router.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/router/router.py)** — Configures generic URL rules that automatically support new providers.
- **`.env.example`** — Template for required environment variables.

## Summary

To implement custom OAuth providers beyond GitHub in LMForge:

- **Extend `OAuth`** in [`api/pkg/oauth/oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/pkg/oauth/oauth.py) with provider-specific logic for authorization URLs, token exchange, and user info transformation.
- **Configure environment variables** for client ID, secret, and redirect URI in `.env`.
- **Register the provider** in `OAuthService.get_all_oauth()` located in [`api/internal/service/oauth_service.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/service/oauth_service.py).
- **Leverage existing endpoints** — the generic router in [`api/internal/router/router.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/router/router.py) automatically handles `/oauth/<provider>` and `/oauth/authorize/<provider>` without modification.

## Frequently Asked Questions

### What methods must I implement when adding a custom OAuth provider?

You must implement five abstract methods defined in [`api/pkg/oauth/oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/pkg/oauth/oauth.py): `get_provider()` returns the provider name string, `get_authorization_url()` constructs the initial redirect URL, `get_access_token()` exchanges the authorization code for a bearer token, `get_raw_user_info()` fetches user data from the provider's API, and `_transform_user_info()` converts that raw data into the standard `OAuthUserInfo` dataclass.

### Do I need to modify the router to add a new OAuth provider?

No. The router in [`api/internal/router/router.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/internal/router/router.py) already defines generic URL rules for `/oauth/<string:provider_name>` and `/oauth/authorize/<string:provider_name>`. Once you register your provider in `OAuthService.get_all_oauth()`, these endpoints automatically handle the new provider using the string key you specified.

### How does LMForge standardize user information across different OAuth providers?

The platform uses the `OAuthUserInfo` dataclass defined in [`api/pkg/oauth/oauth.py`](https://github.com/haohao-end/lmforge-end-to-end-llmops-platform-for-multi-model-agents/blob/main/api/pkg/oauth/oauth.py) to enforce a consistent structure containing `id`, `name`, and `email`. Each provider's `_transform_user_info()` method maps its unique API response format to this standard structure, ensuring the rest of the application receives uniform user data regardless of whether authentication occurred via GitHub, Google, or another service.

### Can I implement multiple custom OAuth providers simultaneously?

Yes. The architecture supports any number of providers. Simply create separate classes for each (for example, `GoogleOAuth`, `MicrosoftOAuth`), add their respective environment variables to `.env`, and include all instances in the dictionary returned by `OAuthService.get_all_oauth()`. Each provider operates under its own URL path segment (e.g., `/oauth/google`, `/oauth/microsoft`) without conflicts.