Quickstart

This tutorial walks you through creating your first OAuth2 integration with litestar-oauth. In about 15 minutes, you’ll build a complete authentication flow that lets users sign in with GitHub.

What We’re Building

We’ll create a simple web application that:

  1. Redirects users to GitHub for authentication

  2. Handles the OAuth callback with authorization code

  3. Exchanges the code for an access token

  4. Retrieves and displays user information

[Your App]                    [GitHub]                    [User]
    |                            |                           |
    |----> Login button click ---|                           |
    |      (redirect to GitHub)  |<---- User sees login -----|
    |                            |      page                 |
    |                            |<---- User approves -------|
    |<---- Callback with code ---|                           |
    |----> Exchange code ------->|                           |
    |<---- Access token ---------|                           |
    |----> Get user info ------->|                           |
    |<---- User data ------------|                           |
    |----> Show profile ---------|-------------------------->|

Prerequisites

Before starting, you’ll need:

  1. Python 3.9+ installed

  2. A GitHub OAuth App - Create one at https://github.com/settings/developers

    • Set the callback URL to http://localhost:8000/auth/github/callback

    • Note your Client ID and Client Secret

  3. Dependencies installed:

    pip install litestar-oauth[litestar] uvicorn
    

Option 1: Standalone Usage

Let’s start with the simplest approach - using litestar-oauth without any web framework.

Step 1: Configure the Provider

# standalone_example.py
import asyncio
from litestar_oauth import OAuthService
from litestar_oauth.providers import GitHubOAuthProvider

# Create the GitHub provider with your credentials
github = GitHubOAuthProvider(
    client_id="your-github-client-id",      # Replace with your Client ID
    client_secret="your-github-client-secret",  # Replace with your Client Secret
)

# Create the service and register the provider
oauth_service = OAuthService()
oauth_service.register(github)

Step 2: Generate Authorization URL

async def start_oauth_flow():
    """Generate the URL to send users to GitHub."""
    auth_url = await oauth_service.get_authorization_url(
        provider_name="github",
        redirect_uri="http://localhost:8000/auth/github/callback",
        next_url="/dashboard",  # Where to redirect after successful auth
    )
    print(f"Redirect user to: {auth_url}")
    return auth_url

Step 3: Handle the Callback

async def handle_callback(code: str, state: str):
    """Process the OAuth callback from GitHub."""
    # Validate the state parameter (CSRF protection)
    oauth_state = oauth_service.state_manager.consume_state(
        state=state,
        provider="github",
    )

    # Get the provider
    provider = oauth_service.get_provider("github")

    # Exchange the authorization code for an access token
    token = await provider.exchange_code(
        code=code,
        redirect_uri=oauth_state.redirect_uri,
    )

    print(f"Access Token: {token.access_token[:20]}...")
    print(f"Token Type: {token.token_type}")

    # Get user information
    user_info = await provider.get_user_info(token.access_token)

    print(f"User ID: {user_info.oauth_id}")
    print(f"Username: {user_info.username}")
    print(f"Email: {user_info.email}")
    print(f"Avatar: {user_info.avatar_url}")

    return user_info

Step 4: Complete Example

# standalone_example.py
import asyncio
from litestar_oauth import OAuthService
from litestar_oauth.providers import GitHubOAuthProvider

# Setup
github = GitHubOAuthProvider(
    client_id="your-github-client-id",
    client_secret="your-github-client-secret",
)

oauth_service = OAuthService()
oauth_service.register(github)


async def main():
    # Generate the authorization URL
    auth_url = await oauth_service.get_authorization_url(
        provider_name="github",
        redirect_uri="http://localhost:8000/auth/github/callback",
    )
    print(f"\n1. Open this URL in your browser:\n   {auth_url}\n")
    print("2. Authorize the application")
    print("3. Copy the 'code' parameter from the callback URL\n")

    # In a real app, this would come from the callback request
    code = input("Enter the authorization code: ").strip()

    # Get the state from our storage
    # (In a real app, this comes from the callback URL too)
    states = list(oauth_service.state_manager._states.keys())
    if not states:
        print("Error: No state found")
        return

    state = states[0]

    # Process the callback
    oauth_state = oauth_service.state_manager.consume_state(state, "github")
    provider = oauth_service.get_provider("github")

    token = await provider.exchange_code(
        code=code,
        redirect_uri=oauth_state.redirect_uri,
    )

    user_info = await provider.get_user_info(token.access_token)

    print(f"\nWelcome, {user_info.username}!")
    print(f"Email: {user_info.email}")
    print(f"Profile: {user_info.profile_url}")


if __name__ == "__main__":
    asyncio.run(main())

Option 2: With Litestar Plugin

For web applications, the Litestar plugin provides a much smoother experience with automatic route registration and dependency injection.

Step 1: Create the Application

# app.py
from litestar import Litestar, get
from litestar.response import Redirect

from litestar_oauth.contrib.litestar import OAuthPlugin, OAuthConfig
from litestar_oauth import OAuthService, OAuthUserInfo


@get("/")
async def home() -> dict:
    """Home page with login link."""
    return {
        "message": "Welcome! Click below to sign in.",
        "login_url": "/auth/github/login",
    }


@get("/dashboard")
async def dashboard(oauth_user_info: OAuthUserInfo) -> dict:
    """Protected dashboard showing user info."""
    return {
        "message": f"Welcome back, {oauth_user_info.username}!",
        "user": {
            "id": oauth_user_info.oauth_id,
            "username": oauth_user_info.username,
            "email": oauth_user_info.email,
            "avatar": oauth_user_info.avatar_url,
            "provider": oauth_user_info.provider,
        },
    }


# Configure the OAuth plugin
oauth_config = OAuthConfig(
    redirect_base_url="http://localhost:8000",
    route_prefix="/auth",
    success_redirect="/dashboard",
    failure_redirect="/?error=auth_failed",

    # GitHub credentials
    github_client_id="your-github-client-id",
    github_client_secret="your-github-client-secret",

    # Optional: Add more providers
    # google_client_id="your-google-client-id",
    # google_client_secret="your-google-client-secret",
)


app = Litestar(
    route_handlers=[home, dashboard],
    plugins=[OAuthPlugin(config=oauth_config)],
)

Step 2: Run the Application

uvicorn app:app --reload

Step 3: Test the Flow

  1. Open http://localhost:8000 in your browser

  2. Click the login link to go to /auth/github/login

  3. Authorize the app on GitHub

  4. You’ll be redirected to /dashboard with your user info

Understanding the Plugin

The OAuthPlugin automatically registers these routes:

GET /auth/{provider}/login

Redirects the user to the OAuth provider’s authorization page. The {provider} can be github, google, discord, etc.

GET /auth/{provider}/callback

Handles the callback from the OAuth provider. Validates the state, exchanges the code for a token, and redirects to success_redirect.

The plugin also provides these dependencies:

oauth_service: OAuthService

The configured OAuth service with all registered providers.

oauth_user_info: OAuthUserInfo

The authenticated user’s information (populated after successful OAuth).

Adding Multiple Providers

You can easily support multiple OAuth providers:

oauth_config = OAuthConfig(
    redirect_base_url="http://localhost:8000",

    # GitHub
    github_client_id="your-github-client-id",
    github_client_secret="your-github-client-secret",

    # Google
    google_client_id="your-google-client-id",
    google_client_secret="your-google-client-secret",

    # Discord
    discord_client_id="your-discord-client-id",
    discord_client_secret="your-discord-client-secret",

    # Only enable specific providers
    enabled_providers=["github", "google"],
)

Users can then authenticate via:

  • /auth/github/login - Sign in with GitHub

  • /auth/google/login - Sign in with Google

Handling Errors

OAuth can fail for various reasons. Here’s how to handle common errors:

from litestar import get
from litestar.exceptions import HTTPException

from litestar_oauth.exceptions import (
    OAuthError,
    TokenExchangeError,
    InvalidStateError,
    ExpiredStateError,
)


@get("/auth/callback")
async def custom_callback(code: str, state: str, oauth_service: OAuthService) -> dict:
    try:
        # Validate state
        oauth_state = oauth_service.state_manager.consume_state(state)

        # Exchange code
        provider = oauth_service.get_provider(oauth_state.provider)
        token = await provider.exchange_code(code, oauth_state.redirect_uri)

        # Get user info
        user_info = await provider.get_user_info(token.access_token)

        return {"user": user_info.username}

    except InvalidStateError:
        raise HTTPException(status_code=400, detail="Invalid or missing state parameter")

    except ExpiredStateError:
        raise HTTPException(status_code=400, detail="Authorization request expired. Please try again.")

    except TokenExchangeError as e:
        raise HTTPException(status_code=400, detail=f"Failed to exchange authorization code: {e}")

    except OAuthError as e:
        raise HTTPException(status_code=500, detail=f"OAuth error: {e}")

Storing User Sessions

After successful authentication, you’ll typically want to create a session:

from litestar import get, post
from litestar.datastructures import Cookie
from litestar.response import Response

@get("/auth/github/callback")
async def github_callback(
    code: str,
    state: str,
    oauth_service: OAuthService,
) -> Response:
    # Validate and get user info
    oauth_state = oauth_service.state_manager.consume_state(state, "github")
    provider = oauth_service.get_provider("github")
    token = await provider.exchange_code(code, oauth_state.redirect_uri)
    user_info = await provider.get_user_info(token.access_token)

    # Create or update user in your database
    # user = await create_or_update_user(user_info)

    # Create a session (simplified example)
    session_token = create_session_token(user_info.oauth_id)

    response = Response(
        content={"message": "Login successful"},
        status_code=302,
        headers={"Location": oauth_state.next_url or "/dashboard"},
    )
    response.set_cookie(
        key="session",
        value=session_token,
        httponly=True,
        secure=True,
        samesite="lax",
    )
    return response

Next Steps

Congratulations! You’ve built your first OAuth2 integration. Here’s where to go next: