Skip to content

Authentication

Registration (4 Steps)

Registration requires email verification, a password, and a display name:

Step 1: Send Verification Code

Terminal window
POST /auth/send-code
{
"email": "user@example.com",
"scene": "register",
"turnstile_token": "..."
}

Step 2: Register

Terminal window
POST /auth/register
{
"email": "user@example.com",
"code": "123456",
"account_password": "your-password",
"display_name": "Your Name",
"device_locale": "en-US"
}

Returns access token and refresh token (as HttpOnly cookie).

Step 3: Client Generates Keys

After registration, the client:

  1. Generates an X25519 key pair
  2. Derives an encryption key: Argon2id(protection_password, random_salt)
  3. Encrypts the private key: AES-GCM(derived_key, private_key)

Step 4: Upload Key Backup

Terminal window
POST /keys/backup
{
"backup_a": "base64-encrypted-private-key",
"argon2_salt": "base64-salt",
"public_key": "base64-public-key",
"device_id": "device-uuid"
}

Login

Three login methods are supported:

Password Login

Terminal window
POST /auth/login
{
"email": "user@example.com",
"account_password": "your-password",
"turnstile_token": "..."
}

Lockout: After 5 failed attempts, the account is locked for 15 minutes. Returns 423 ACCOUNT_LOCKED with retry_after in seconds.

Verification Code Login

Terminal window
POST /auth/send-code
{ "email": "user@example.com", "scene": "login", "turnstile_token": "..." }
POST /auth/login-code
{ "email": "user@example.com", "code": "123456" }

Passkey Login

Passkey replaces the account password in the authentication step. The protection password (for key decryption) is still needed on new devices.

Terminal window
POST /auth/passkey/begin # Returns challenge (public endpoint)
POST /auth/passkey/complete # Verifies signature, issues tokens (public endpoint)

Passkey login uses WebAuthn Conditional UI — no explicit button on the login screen. The system Passkey prompt appears automatically on supported devices.

Token Refresh

Access tokens expire in 5 minutes. Use the refresh token to get a new pair:

Terminal window
POST /auth/refresh

The refresh token is stored as an HttpOnly cookie with these attributes:

Set-Cookie: refresh_token=...; HttpOnly; Secure; SameSite=Strict; Path=/auth; Max-Age=2592000

Password Reset

Account password reset does not affect encryption keys:

Terminal window
POST /auth/send-code
{ "email": "user@example.com", "scene": "reset", "turnstile_token": "..." }
POST /auth/reset-password
{ "email": "user@example.com", "code": "123456", "new_account_password": "new-password" }

Agent Authentication

Agents use long-lived tokens instead of JWT:

Authorization: Bearer hsk_your-agent-token

The token format is hsk_ + 40 base62 characters. Server stores the bcrypt hash.

WebSocket Auth

For WebSocket connections, the token is sent in the first frame (never in the URL):

{ "type": "auth", "token": "hsk_...", "agent_id": "your-agent-id" }

Server responds with { "type": "auth.ok" } or { "type": "auth.error", "reason": "..." }.

Token Regeneration

Terminal window
POST /agents/:id/token/regenerate
{ "emergency": false }
ModeOld TokenOld Public Key
Normal (emergency: false)Valid for 7 daysValid for 7 days
Emergency (emergency: true)Immediately revokedImmediately revoked

Next Steps