Skip to main content

Authentication-Based Recovery API (Email / SMS)

A Safe guardian service that uses email and phone verification to facilitate account recovery. It can be used as a default recovery method or combined with other guardians (such as hardware wallets or trusted contacts) to create a customized recovery threshold.

info

The following API is in a pre-release phase. Request access here.

/auth (Registration)

  • /auth/register
    • POST: Submit a registration request
  • /auth/submit
    • POST: Submits the receive OTP
  • /auth/registrations
    • GET: Fetch active registration
  • /auth/delete
    • POST: Delete a registration

/auth/signature (Recovery)

  • /auth/signature/request
    • POST: Request to recover an account
  • /auth/signature/submit
    • POST: Confirm recovery with OTP challenge

Auth Registration

Email / SMS

Submit a registration request with the target smart account to protect using the choice of channel (email or sms). The user will then receive a OTP code to later submits the challenge in /auth/submit

POST /auth/register

curl -X POST \
https://api.candide.dev/recoveries/$version/$network/$your_api_key/auth/register \
-H 'Content-Type: application/json' \
-d '{
"account":"0x...",
"chainId": 1,
"channel":"email",
"target":"user@example.com",
"message": "siwe(chainId, statement(channel, target))",
"signature": "sign(message)"
}'
  • account: The smart account address requesting registration.
  • chainId: The chain id in which your account resides (for multi-chain wallets, users will need to register per chain)
  • channel: Either "email" or "sms" (defines the authentication type).
  • target: The email or phone number for authentication.
  • message: SIWE (EIP-4361) message statement. Statement:
I authorize Safe Recovery Service to sign a recovery request for my account after I authenticate using {{target}} via {{channel}}
  • signature: signature proving the request is initiated from the account

See example guide how to construct the message and signature using Sign in With Ethereum (SIWE)

Submits confirmation using OTP

Submits the receive OTP code to confirm ownership of the target channel

POST /auth/submit

curl -X POST \
https://api.candide.dev/recoveries/$version/$network/$your_api_key/auth/submit \
-H 'Content-Type: application/json' \
-d '{
"challengeId":"unique-challenge-id",
"challenge":"123456"
}'
  • challengeId: The unique challenge ID received in the registration response.
  • challenge: The code received via email/SMS.

Get active registration

Fetch the registration of the protected smart account

GET /auth/registrations

curl -G "https://api.candide.dev/recoveries/$version/$network/$your_api_key/auth/registrations" \
--data-urlencode "account=0x...",
--data-urlencode "chainId=0x1",
--data-urlencode "message=siwe(chainId, statement)",
--data-urlencode "signature=sign(message)"
  • account: The safe account address.
  • chainId: The chain id in which your account resides.
  • message: SIWE (EIP-4361) message statement. Statement:
I request to retrieve all authentication methods currently registered to my account with Safe Recovery Service
  • signature: signature proving the request is initiated from the account

See example guide how to construct the message and signature using Sign in With Ethereum (SIWE)

Delete

Deletes a registration

POST /auth/delete

curl -X POST \
https://api.candide.dev/recoveries/$version/$network/$your_api_key/auth/delete \
-H 'Content-Type: application/json' \
-d '{
"registrationId":"unique-registration-id",
"message": "siwe(chainId, statement(registrationId))",
"signature": "sign(registrationId, timestamp)"
}'
  • registrationId: The registration id.
  • message: SIWE (EIP-4361) message statement. Statement:
I request to remove the authentication method with registration ID {{id}} from my account on Safe Recovery Service
  • signature: signature proving the request is initiated from the account

See example guide how to construct the message and signature using Sign in With Ethereum (SIWE)

Auth Recovery

Request to recover an account

Request a siganture from the service to recover an account given the new owners and threshold

POST /auth/signature/request

curl -X POST \
https://api.candide.dev/recoveries/$version/$network/$your_api_key/execute \
-H 'Content-Type: application/json' \
-d '{
"account":"0x...",
"newOwners": ["0x...", "0x..."],
"newThreshold": 2,
"chainId": 1
}'
  • account: The smart account address to be recovered.
  • newOwners: The new owners to the safe account
  • newThreshold: The new threshold to the safe account
  • chainId: chain id for the recovery request signature

Confirm recovery with OTP challenge

Request to submits the signature with the provided OTP code challenge and id

POST /auth/signature/submit

curl -X POST \
https://api.candide.dev/recoveries/$version/$network/$your_api_key/auth/signature/submit \
-H 'Content-Type: application/json' \
-d '{
"requestId": "unique-signature-request-id",
"challengeId": "unique-challenge-id",
"challenge": "123456"
}'
  • requestId: The unique ID received in the signature request response.
  • challengeId: The unique challenge ID specific to a authentication method, received in the signature request response.
  • challenge: The code received via email/SMS.

How to Sign Messages (SIWE EIP-4361)

import { SiweMessage } from "siwe";
import { hexlify, randomBytes } from "ethers";

import { personalSign, getMessageHashForSafe } from "./safe-utils"

function generateSIWEMessageSignaturePair(safeAccountAddress: string, statement: string, chainId: string): [string, string] {
const siweMessage = new SiweMessage({
version: "1",
address: ethers.getAddress(accountAddress),
domain: "service://safe-recovery-safeAccountAddress",
uri: "service://safe-recovery-service",
statement,
chainId: Number(chainId),
nonce: hexlify(randomBytes(24)),
});
const message = siweMessage.prepareMessage();
const signature = personalSign(safeAccountAddress, message, BigInt(chainId));
return [message, signature];
}