Skip to main content

POST /auth/devices/register

Registers a device's verifying key (public key from Secure Enclave) for MPC operations.

Used in flow: Initial device setup after user registration, device recovery/switch

Authentication: JWT (Access Token) with write:devices + Device Signature + a successful match session when a face is enrolled

Security Model

ScenarioFace Enrolled?Behavior
Same user, same device keyN/ANo-op, return 200
Same user, NEW device key❌ NoAllow registration (201)
Same user, NEW device key✅ YesRequire face_session_id (403 → 201)
Different user, existing device keyN/A409 Conflict

Request

Authorization: Bearer <access_token>
Content-Type: application/json
Without face verification
{
"device_id": "<hex>",
"signature": "<hex>"
}
With face verification
{
"device_id": "<hex>",
"signature": "<hex>",
"face_session_id": "uuid_of_successful_match_scan"
}
FieldTypeRequiredDescription
device_idstringYesHex-encoded uncompressed device public key (04 || X || Y)
signaturestringYesHex-encoded DER ECDSA P-256 signature
face_session_idstringConditionalRequired when the user has a face enrolled. face_session_id of a match scan that completed with succeeded: true. See POST /v2/auth/face/process.

The challenge and device signature are checked before the match session is spent. A stale challenge, a bad signature, or a conflicting device key is rejected with the session still usable, so the app can retry without a new face scan.

Signature Generation

JSON_MESSAGE = { "challenge": "<hex_challenge_from_get_challenge>" }

Signature = DER-encoded ECDSA (P-256) over SHA256(canonicalize(JSON_MESSAGE).to_utf8_bytes())
// Canonicalization: RFC 8785

Response

201 Created - new device
{
"success": true,
"message": "Device registered successfully"
}
200 OK - device already registered by same user
{
"success": true,
"message": "Device already registered"
}
403 - face_session_id required but missing
{
"error": {
"code": 100702,
"message": "Face verification required for new device registration",
"requires_face": true
}
}
409 - session already spent, expired, or not a successful match
{
"error": {
"code": 100606,
"message": "FaceTec session is no longer active",
"requires_face": true
}
}
409 - device owned by different user
{
"error": {
"code": 100603,
"message": "Device already registered"
}
}