Skip to main content

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

Metadata

Release files for utpd-oauth-client 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for utpd-oauth-client 0.2.1
File Size Uploaded Provenance Actions
utpd_oauth_client-0.2.1.tar.gz 6.7 kB Without provenance Details for utpd_oauth_client-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for utpd-oauth-client 0.2.1
File Interpreter ABI Platform Actions
utpd_oauth_client-0.2.1-py3-none-any.whl Python 3 none any Details for utpd_oauth_client-0.2.1-py3-none-any.whl

Total release size: 16.7 kB

Release files / utpd_oauth_client-0.2.1.tar.gz

Download URL utpd_oauth_client-0.2.1.tar.gz
Size 6.7 kB
Tags Source
SHA-256 checksum
How to use checksums
3b87a57f6ca5977a9344d7eb38b58d17af9eda14fd938d937216132e1dc9f433
BLAKE2b-256 checksum
How to use checksums
ed6020af9554a6f46cdb9ef24283ae474eb66d90f3aaa299aca774311b178cd9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","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}

Release files / utpd_oauth_client-0.2.1-py3-none-any.whl

Download URL utpd_oauth_client-0.2.1-py3-none-any.whl
Size 10.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
99c23ca8ba5b1ae0bb40e55cd2eabf85d15c9ddf62f7c5be78495ecca39a2525
BLAKE2b-256 checksum
How to use checksums
5cc5b855a4a0a83a59a5c05ace754c0d9b2178b6f5014aae31b5a6d75f5b568e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","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}

Security for utpd-oauth-client 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page