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
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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.