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 flowGET /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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file utpd_oauth_client-0.2.1.tar.gz.
File metadata
- Download URL: utpd_oauth_client-0.2.1.tar.gz
- Upload date:
- Size: 6.7 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b87a57f6ca5977a9344d7eb38b58d17af9eda14fd938d937216132e1dc9f433
|
|
| MD5 |
b50825529c08d0c05816d82cd5f0ee9d
|
|
| BLAKE2b-256 |
ed6020af9554a6f46cdb9ef24283ae474eb66d90f3aaa299aca774311b178cd9
|
File details
Details for the file utpd_oauth_client-0.2.1-py3-none-any.whl.
File metadata
- Download URL: utpd_oauth_client-0.2.1-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
99c23ca8ba5b1ae0bb40e55cd2eabf85d15c9ddf62f7c5be78495ecca39a2525
|
|
| MD5 |
9b2d75cc0a9fafee2e68bd8040d2e22a
|
|
| BLAKE2b-256 |
5cc5b855a4a0a83a59a5c05ace754c0d9b2178b6f5014aae31b5a6d75f5b568e
|