bloonio_auth_relay_client
Backend SDK for the bloonio_auth relay (auth-relay.example.com).
Integrate push-based sudo approval (TOTP / golden number / biometric via the bloonio_auth authenticator app) into any Python backend in ~10 lines. Framework-agnostic core, thin FastAPI + Django adapters.
Install
pip install "bloonio-auth-relay-client[fastapi,redis]"
# or
pip install "bloonio-auth-relay-client[django,redis]"
Available extras: fastapi, django, redis, mongo, all.
Concepts
RelayClient/AsyncRelayClient— HMAC-signed HTTP wrapper around the relay. Sync version for Django, async for FastAPI; same surface.SudoInstructionStore— short-lived (~180 s default). Tracks "this instruction passed sudo, here's the re-call window withX-Sudo-Instruction-Key". Default: Redis.PendingOpStore— longer-lived (10–30 min). Holds the original mutation for server-side replay after approval — required for v2 group quorum + v3 cross-org modes. v1 single-actor doesn't use it but the interface ships now to avoid future API breakage.@sudo_required— decorator. Same arguments work in FastAPI and Django.DataType+ValidationField— structured display blocks the auth app renders (currency, IBAN, date, entity ref, etc.). Replaces unstructured description strings.
FastAPI quick start — pick the style that fits your codebase
Style A — declarative (greenfield, named routes)
For backends where each sudo-protected route is named and the developer knows up-front what fields to display:
from fastapi import FastAPI
from bloonio_auth_relay_client import DataType, SudoActionType, ValidationField
from bloonio_auth_relay_client.adapters.fastapi import BloonioAuthAdapter
app = FastAPI()
bloonio = BloonioAuthAdapter.from_env(app) # reads BLOONIO_RELAY_* env
@app.post(
"/transfer/execute",
dependencies=[bloonio.sudo_required(
expected_action="transfer_funds",
custom_type=SudoActionType.LOCAL_AUTH,
user_socket_hash=lambda req: req.state.user.socket_hash,
title="Confirm wire transfer",
fields=lambda req: [
ValidationField(key="amount", title="Amount",
value=str(req.state.body["amount"]),
data_type=DataType.CURRENCY_USD),
ValidationField(key="to", title="Recipient",
value=req.state.body["recipient"],
data_type=DataType.PARTY_NAME),
],
)],
)
async def transfer_execute(...): ...
BloonioAuthAdapter.from_env(app) does all of:
- builds
RelaySettingsfromBLOONIO_RELAY_*env vars - builds an
AsyncRelayClient - builds Redis-backed instruction + pending-op stores
- mounts the dispatch middleware
- mounts the auto submit-response router at
/_bloonio/submit-response - mounts the relay callback router at
settings.callback_path
Style B — RBAC-driven (dynamic / generic routes)
For backends where which-endpoint-needs-sudo is determined at runtime by querying
RBAC config (e.g. routes like /generic/add/{collection_name}), pass a
sudo_resolver callable. The resolver returns a SudoInfo describing what to
challenge with — the SDK takes it from there.
from bloonio_auth_relay_client import (
DataType, SudoActionType, SudoInfo, ValidationField, ValidationMode, Validator,
)
from bloonio_auth_relay_client.adapters.fastapi import BloonioAuthAdapter
from starlette.requests import Request
async def my_rbac_resolver(request: Request) -> SudoInfo | None:
"""Backend writes only this — the rest is the SDK."""
rbac = await fetch_rbac_for_path(request.url.path)
if not rbac or not rbac.is_sudo_action:
return None
user = request.state.user
return SudoInfo(
required=True,
mode=ValidationMode.SINGLE_ACTOR,
custom_type=pick_random_confirmation_type(rbac),
expected_action=rbac.expected_action,
description=rbac.totp_app_description_str,
actor=Validator(
socket_hash=user.user_account_socket_hash,
display_name=f"{user.first_name} {user.last_name}",
),
display_title="Confirm action",
display_fields=build_fields_from_request(request),
)
bloonio = BloonioAuthAdapter.from_env(app, sudo_resolver=my_rbac_resolver)
# All routes — including generic ones — are now sudo-aware.
# When required=True, the SDK:
# • Creates an instruction_id
# • Writes Redis state (180s TTL by default)
# • Calls relay.send_auth_challenge(...) → FCM push
# • Returns 403 with {error: "SUDO_INSTRUCTION_KEY_REQUIRED", instruction_id}
# When the device approves (POST /_bloonio/submit-response), state flips to
# "validated". The next call with X-Sudo-Instruction-Key passes through.
The two styles can coexist in the same app — declarative for explicit routes, resolver for catch-all ones.
Django quick start
# settings.py
INSTALLED_APPS = [..., "bloonio_auth_relay_client.adapters.django"]
MIDDLEWARE = [..., "bloonio_auth_relay_client.adapters.django.middleware.SudoActionMiddleware"]
BLOONIO_AUTH_RELAY = {
"BASE_URL": "https://auth-relay.example.com",
"TENANT_ID": os.environ["RELAY_TENANT_ID"],
"TENANT_SECRET": os.environ["RELAY_TENANT_SECRET"],
"STATE_BACKEND": "redis",
"STATE_BACKEND_URL": os.environ["REDIS_URL"],
"CALLBACK_PATH": "/api/sudo-callback/",
}
# urls.py
from bloonio_auth_relay_client.adapters.django import urls as relay_urls
urlpatterns = [..., path("", include(relay_urls))]
# views.py
from bloonio_auth_relay_client.adapters.django import sudo_required
from bloonio_auth_relay_client import DataType, SudoActionType, ValidationField
@sudo_required(
expected_action="transfer_funds",
custom_type=SudoActionType.LOCAL_AUTH,
user_socket_hash=lambda req: req.user.socket_hash,
title="Confirm wire transfer",
fields=lambda req: [
ValidationField(key="amount", title="Amount",
value=str(req.POST["amount"]),
data_type=DataType.CURRENCY_USD),
],
)
def transfer_execute(request):
...
Works on plain views, DRF @api_view, DRF APIView/ViewSet methods, and async views (Django 4.1+). Same decorator, same arguments — just req.user instead of req.state.user, req.POST instead of req.state.body.
Pairing handshake (called once per device pairing)
from bloonio_auth_relay_client import AsyncRelayClient, RelaySettings
relay = AsyncRelayClient(RelaySettings())
# In your QR pairing handler, after the user is authenticated:
result = await relay.prepare_pairing(
user_socket_hash=user.socket_hash,
backend_user_id=str(user.id),
user_email=user.email,
user_phone=user.phone,
first_name=user.first_name,
last_name=user.last_name,
display_name="bloonio_apps_api",
display_logo_url="https://cdn.example.com/logo.png",
)
pairing_proof = result["pairing_proof"] # forward to device along with the rest of /auth/get-pairing-data
Two-call protocol (preserved from existing flow)
- Client
POST /transfer/executewith noX-Sudo-Instruction-Key. → 403{"error": "SUDO_INSTRUCTION_KEY_REQUIRED", "instruction_id": "abc..."} - Device receives push → user approves → relay POSTs ApprovalEvent to your callback (HMAC-verified) →
SudoInstructionStoremarksinstruction_idasvalidated. - Client re-issues
POST /transfer/executewith headerX-Sudo-Instruction-Key: abc.... → request goes through.
v2 / v3 (deferred)
The decorator accepts mode=ValidationMode.GROUP_QUORUM etc. but raises NotImplementedError until v2/v3 land. The data-model and types are stable.
Configuration via env
All settings can be passed to RelaySettings(...) directly or sourced from env (BLOONIO_RELAY_BASE_URL, BLOONIO_RELAY_TENANT_ID, BLOONIO_RELAY_TENANT_SECRET, BLOONIO_RELAY_STATE_BACKEND_URL, etc.).
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 bloonio_auth_relay_client-0.3.1.tar.gz.
File metadata
- Download URL: bloonio_auth_relay_client-0.3.1.tar.gz
- Upload date:
- Size: 46.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b2a713a15572ba97df60b9c752cf2c45abafa1f74f7ec1bac4f18aac802fb18
|
|
| MD5 |
e76a84cbb1c676004355cf3fd2831a83
|
|
| BLAKE2b-256 |
539eac388df9819d0197b5528c395e9d29ba360c37d64f069fe69dc2afb5598e
|
File details
Details for the file bloonio_auth_relay_client-0.3.1-py3-none-any.whl.
File metadata
- Download URL: bloonio_auth_relay_client-0.3.1-py3-none-any.whl
- Upload date:
- Size: 53.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
715cfe7b5466c5c2ac3240e8cb4fb2ffdb300b36390336ad6dcec1695798757c
|
|
| MD5 |
1092160b28fc82397f5b5608944625f4
|
|
| BLAKE2b-256 |
c3ae2108e6c7b7dec4e971a08db0e9db392b803dfb35ffb500b1ec9ece16f633
|