Skip to main content

Client library for utpd-oauth service with framework integrations

Project description

utpd-oauth-client

Client library for the utpd-oauth service, with framework integrations for FastAPI and NiceGUI.

Installation

uv add utpd-oauth-client

The only dependency is httpx. Framework integrations assume you already have the relevant framework installed.

Usage

Core Client (Framework-Agnostic)

The core client works anywhere - scripts, CLI tools, any web framework:

from utpd_oauth_client import OAuthClient

client = OAuthClient("https://utpd-oauth.ward.au")

# Step 1: Get the URL to redirect users to
login_url = client.login_url("https://myapp.com/auth/callback")
# -> "https://utpd-oauth.ward.au/login?next_url=https%3A%2F%2Fmyapp.com%2Fauth%2Fcallback"

# Step 2: After OAuth completes, exchange the token_code for an access_token
access_token = await client.exchange(token_code)

FastAPI Integration

The FastAPI integration provides a router factory that handles the OAuth redirect flow:

from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
from starlette.responses import Response

from utpd_oauth_client import OAuthClient
from utpd_oauth_client.fastapi import create_auth_router

app = FastAPI()
client = OAuthClient(settings.oauth_service_url)


async def handle_token(token: str, request: Request) -> Response:
    """Called after successful OAuth with the access_token."""
    # Store token, create session, look up user, etc.
    request.session["access_token"] = token
    return RedirectResponse("/")


router = create_auth_router(
    client=client,
    public_base_url=settings.public_base_url,  # e.g. "https://myapp.com"
    on_token=handle_token,
)
app.include_router(router)

This creates two endpoints:

  • GET /auth/start - Redirects user to utpd-oauth to begin the flow
  • GET /auth/callback - Receives the token_code, exchanges it, calls your handler

NiceGUI Integration

The NiceGUI integration adds OAuth routes to your NiceGUI app:

import os

from nicegui import app, ui
from starlette.requests import Request
from starlette.responses import RedirectResponse, Response

from utpd_oauth_client import OAuthClient
from utpd_oauth_client.nicegui import setup_oauth_routes

client = OAuthClient(os.getenv("OAUTH_SERVICE_URL"))


async def handle_token(token: str, request: Request) -> Response:
    """Called after successful OAuth with the access_token."""
    app.storage.user["access_token"] = token
    app.storage.user["authenticated"] = True
    return RedirectResponse("/")


setup_oauth_routes(
    app=app,
    client=client,
    public_base_url=os.getenv("PUBLIC_BASE_URL"),
    on_token=handle_token,
)


@ui.page("/")
def main_page() -> None:
    if not app.storage.user.get("authenticated"):
        ui.link("Login with Untappd", "/auth/start")
    else:
        ui.label(f"Welcome! Your token: {app.storage.user.get('access_token', '')[:10]}...")

Configuration

Both integrations accept these parameters:

Parameter Default Description
client required OAuthClient instance
public_base_url required Your app's public URL (for callback)
on_token required Async callback receiving (token, request)
start_path /auth/start Path for the "begin OAuth" endpoint
callback_path /auth/callback Path for the callback endpoint
error_handler None Optional async callback for handling errors

Error Handling

The client raises TokenExchangeError when the exchange fails:

from utpd_oauth_client import OAuthClient, TokenExchangeError

client = OAuthClient("https://utpd-oauth.ward.au")

try:
    token = await client.exchange(token_code)
except TokenExchangeError as e:
    print(f"Exchange failed: {e}")
    print(f"Status code: {e.status_code}")
    print(f"Detail: {e.detail}")

For framework integrations, you can provide a custom error handler:

async def handle_error(exc: TokenExchangeError, request: Request) -> Response:
    # Log the error, show a friendly message, etc.
    return RedirectResponse("/login?error=oauth_failed")


router = create_auth_router(
    client=client,
    public_base_url=settings.public_base_url,
    on_token=handle_token,
    error_handler=handle_error,
)

Development

cd utpd-oauth-client
uv sync --all-groups
uv run pytest

Sequence Diagram

sequenceDiagram
    participant User
    participant YourApp
    participant utpd-oauth-client
    participant utpd-oauth
    participant Untappd

    User->>YourApp: GET /auth/start
    YourApp->>utpd-oauth-client: client.login_url(callback_url)
    utpd-oauth-client-->>YourApp: redirect URL
    YourApp->>utpd-oauth: Redirect to /login
    utpd-oauth->>Untappd: Redirect to OAuth
    Untappd->>utpd-oauth: Callback with code
    utpd-oauth->>YourApp: Redirect to /auth/callback?token_code=...
    YourApp->>utpd-oauth-client: client.exchange(token_code)
    utpd-oauth-client->>utpd-oauth: POST /get-token
    utpd-oauth-->>utpd-oauth-client: { access_token }
    utpd-oauth-client-->>YourApp: access_token
    YourApp->>YourApp: on_token(access_token, request)
    YourApp-->>User: Redirect to app

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

utpd_oauth_client-0.1.0.tar.gz (6.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

utpd_oauth_client-0.1.0-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file utpd_oauth_client-0.1.0.tar.gz.

File metadata

  • Download URL: utpd_oauth_client-0.1.0.tar.gz
  • Upload date:
  • Size: 6.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.23 {"installer":{"name":"uv","version":"0.9.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for utpd_oauth_client-0.1.0.tar.gz
Algorithm Hash digest
SHA256 41e90deaadf635d67bed25df2078830d040884eaeac96c4cfb4c25c7b4b4e22c
MD5 94077971f5be64fa9dac2f6593e74d8b
BLAKE2b-256 907ae0e81eb03f280f64c6451d7289d3172538ed8bae19cbbca443a3d72f6bc9

See more details on using hashes here.

File details

Details for the file utpd_oauth_client-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: utpd_oauth_client-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.23 {"installer":{"name":"uv","version":"0.9.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for utpd_oauth_client-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e1233e46393a60aa297147a79f077f1f3101c78ffdec714b58c7a89261909a26
MD5 7976126cf081af8422a1aec63d4f7fc4
BLAKE2b-256 2f388469fd06586fb5d28008d2b292ce2292a3c92612f84282a5f9b5f953ca48

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page