Sudo (élévation)
Le sudo (élévation / step-up) demande à un ou plusieurs validateurs humains
d’approuver une action sensible depuis leur app Bloonio Authenticator avant que
le backend ne l’exécute. Le relais expose une primitive de dispatch (signée
HMAC #1) ; le protocole en deux appels côté tenant — 403 puis re-appel —
est fourni par le SDK Python.
Le protocole en deux appels
Section intitulée « Le protocole en deux appels »- Le client appelle votre route protégée (ex.
POST /transfer/execute) sans en-têteX-Sudo-Instruction-Key. → le SDK crée uninstruction_id, déclenche le dispatch + le push, et renvoie403 { "error": "SUDO_INSTRUCTION_KEY_REQUIRED", "instruction_id": "abc..." }. - L’appareil reçoit le push → l’utilisateur approuve → le relais POST
l’
ApprovalEventvers votre callback (vérifié HMAC) → le store marque l’instruction_idcommevalidated. - Le client ré-émet la même requête avec l’en-tête
X-Sudo-Instruction-Key: abc.... → la requête passe.
Dispatcher un événement (primitive relais)
Section intitulée « Dispatcher un événement (primitive relais) »POST /relay/sudo/dispatch crée un événement de validation et diffuse les
pushes de challenge aux participants. Le relais le transmet à auth_api et vous
renvoie event_id, status et expires_at.
Règles de validation du corps :
event_type∈{ sudo_action, sudo_group_action, sudo_delegated_action }.action_type∈{ creation, deletion, update, upsert }.- Au moins un de
relay_user_linked_id_list(cibles individuelles) ourelay_group_linked_id_list(cibles de groupe) doit être non vide. data_itemsest requis quanddata_access_type=static;data_fetch_urlquanddata_access_type=dynamic.tenant_idn’est pas dans le corps — le relais le dérive du contexte authentifié.
curl $BASE_URL/api/v1/relay/sudo/dispatch \ -X POST \ -H "X-Bloonio-Tenant-Id: tnt_..." \ -H "X-Bloonio-Timestamp: 1718966400000" \ -H "X-Bloonio-Signature: <hex_hmac_sha256>" \ -H "Content-Type: application/json" \ -d '{ "event_type": "sudo_action", "action_type": "update", "idempotency_key": "idem_abc123", "relay_user_linked_id_list": ["652f1f77bcf86cd799439011"], "title": "Confirmer le virement", "description": "Approuver un virement de 1 000 USD vers ACME Corp.", "data_access_type": "static", "data_items": [ { "display_title": "Montant", "display_value": "1000 USD", "data_type": "CURRENCY_USD" }, { "display_title": "Bénéficiaire", "display_value": "ACME Corp", "data_type": "PARTY_NAME" } ], "on_validate_callback_url": "https://api.example.com/relay-callbacks/sudo-validated", "on_reject_callback_url": "https://api.example.com/relay-callbacks/sudo-rejected" }'Pour un quorum M-de-N, ciblez un groupe et utilisez data_access_type: "dynamic". Le requested_ttl_seconds peut alors dépasser 24 h (jusqu’à 5 mois).
curl $BASE_URL/api/v1/relay/sudo/dispatch \ -X POST \ -H "X-Bloonio-Tenant-Id: tnt_..." \ -H "X-Bloonio-Timestamp: 1718966400000" \ -H "X-Bloonio-Signature: <hex_hmac_sha256>" \ -H "Content-Type: application/json" \ -d '{ "event_type": "sudo_group_action", "action_type": "deletion", "idempotency_key": "idem_def456", "relay_group_linked_id_list": ["652f1f77bcf86cd799439099"], "title": "Approuver la clôture du compte", "description": "M validateurs sur N doivent approuver la clôture.", "data_access_type": "dynamic", "data_fetch_url": "https://api.example.com/sudo/data/idem_def456", "on_validate_callback_url": "https://api.example.com/relay-callbacks/sudo-validated", "on_reject_callback_url": "https://api.example.com/relay-callbacks/sudo-rejected", "requested_ttl_seconds": 604800 }'{ "success": true, "status_code": 201, "message": "Dispatched.", "data": { "event_id": "...", "status": "pending", "expires_at": "2026-06-21T..." }}Cibles individuelles vs groupes
Section intitulée « Cibles individuelles vs groupes »relay_user_linked_id_list— identifiants opaques côté relais de validateurs individuels (relay_user_id).relay_group_linked_id_list— identifiants de groupes ;auth_apiétend l’appartenance et diffuse les pushes aux membres. Le relais ne peut pas étendre les groupes lui-même (l’appartenance vit surauth_api).
Le couple relay_user_id est le validateur universel : voir
Multi-tenant & relay_user_id.
Résoudre les identifiants (lookups d’annuaire)
Section intitulée « Résoudre les identifiants (lookups d’annuaire) »Pour passer des relay_user_linked_id / relay_group_linked_id à dispatch,
résolvez-les d’abord depuis les identifiants publics. Ces GET sont de purs
pass-through (HMAC #1 entrant) ; auth_api applique le périmètre du tenant.
| Méthode | Chemin | Rôle |
|---|---|---|
| GET | /relay/sudo/groups | Lister les groupes de validation visibles par le tenant. |
| GET | /relay/sudo/groups/by-public-id | Résoudre un bgr-… en relay_group_linked_id (?public_id_str=). |
| GET | /relay/sudo/paired-users | Lister les utilisateurs appairés (validateurs potentiels). |
| GET | /relay/sudo/paired-users/by-public-id | Résoudre un bth-… en relay_user_linked_id (?public_id_str=). |
| GET | /relay/sudo/pending-validations | Validations en attente pour un utilisateur (?relay_user_linked_id=). |
| GET | /relay/sudo/paired-users/signature | Image de signature d’un utilisateur (?relay_user_linked_id=). |
curl "$BASE_URL/api/v1/relay/sudo/groups/by-public-id?public_id_str=bgr-abc123" \ -H "X-Bloonio-Tenant-Id: tnt_..." \ -H "X-Bloonio-Timestamp: 1718966400000" \ -H "X-Bloonio-Signature: <hex_hmac_sha256>"Vérifier un TOTP
Section intitulée « Vérifier un TOTP »POST /relay/sudo/verify-totp valide le TOTP émis par Bloonio Authenticator
pour l’appairage identifié par relay_user_linked_id, sans que le tenant ne
détienne le secret par-utilisateur. Renvoie 200 en cas de correspondance,
401 sur un code erroné / inconnu.
curl $BASE_URL/api/v1/relay/sudo/verify-totp \ -X POST \ -H "X-Bloonio-Tenant-Id: tnt_..." \ -H "X-Bloonio-Timestamp: 1718966400000" \ -H "X-Bloonio-Signature: <hex_hmac_sha256>" \ -H "Content-Type: application/json" \ -d '{ "relay_user_linked_id": "652f1f77bcf86cd799439011", "totp": "123456" }'Avec le SDK : @sudo_required
Section intitulée « Avec le SDK : @sudo_required »En FastAPI, le décorateur monte tout le protocole en deux appels sur la route :
@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="Confirmer le virement", fields=lambda req: [ ValidationField(key="amount", title="Montant", value=str(req.state.body["amount"]), data_type=DataType.CURRENCY_USD), ], )],)async def transfer_execute(...): ...Voir le SDK Python pour l’installation, la configuration et l’équivalent Django.