Skip to main content
Version: 1.0

Authentication API

SyVault uses a multi-step authentication flow that ensures the server never sees your master password. The client derives cryptographic keys locally, sends only a non-reversible hash, and receives encrypted vault keys that only the client can decrypt.

Prelogin

Retrieve the KDF parameters for a given email address. This must be called before login so the client knows how to derive the master key.

POST /api/auth/prelogin

Request:

curl -X POST https://vault.example.com/api/auth/prelogin \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com"
}'

Response:

{
"kdf": "argon2id",
"kdf_iterations": 3,
"kdf_memory": 65536,
"kdf_parallelism": 4
}

The client uses these parameters to derive the master key from the master password, then derives the auth_hash (a separate HKDF-derived value) for the login request.

Login

Exchange the auth hash for access and refresh tokens plus encrypted vault keys.

POST /api/auth/login

Request:

curl -X POST https://vault.example.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"auth_hash": "base64-encoded-auth-hash",
"device_id": "d4e5f6a7-b8c9-0123-4567-890abcdef012",
"device_name": "Chrome on macOS",
"device_type": "browser"
}'

Response (success, no 2FA):

{
"access_token": "eyJhbGciOiJFZERTQSIs...",
"refresh_token": "vfrt_a1b2c3d4e5f6...",
"expires_in": 3600,
"token_type": "Bearer",
"encrypted_master_key": "base64-encoded-encrypted-key",
"encrypted_private_key": "base64-encoded-encrypted-key",
"public_key": "base64-encoded-public-key"
}

The access_token is a JWT containing:

{
"sub": "user-uuid",
"device_id": "d4e5f6a7-b8c9-0123-4567-890abcdef012",
"exp": 1712444400,
"iat": 1712440800
}

Two-Factor Authentication

If the user has 2FA enabled, the login response returns a 2fa_required flag instead of tokens:

{
"2fa_required": true,
"2fa_methods": ["totp", "webauthn"],
"2fa_token": "temporary-2fa-session-token"
}

Complete the 2FA challenge:

POST /api/auth/2fa/verify
curl -X POST https://vault.example.com/api/auth/2fa/verify \
-H "Content-Type: application/json" \
-d '{
"2fa_token": "temporary-2fa-session-token",
"method": "totp",
"code": "482910"
}'

For WebAuthn:

curl -X POST https://vault.example.com/api/auth/2fa/verify \
-H "Content-Type: application/json" \
-d '{
"2fa_token": "temporary-2fa-session-token",
"method": "webauthn",
"credential": {
"id": "base64url-credential-id",
"response": {
"authenticatorData": "base64url-auth-data",
"clientDataJSON": "base64url-client-data",
"signature": "base64url-signature"
}
}
}'

On success, both endpoints return the same token payload as a successful login.

Token Refresh

Access tokens expire after 1 hour. Use the refresh token to obtain a new access token without re-authenticating.

POST /api/auth/refresh
curl -X POST https://vault.example.com/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "vfrt_a1b2c3d4e5f6..."
}'

Response:

{
"access_token": "eyJhbGciOiJFZERTQSIs...",
"refresh_token": "vfrt_new_token_here...",
"expires_in": 3600,
"token_type": "Bearer"
}

The old refresh token is invalidated (rotation). If a refresh token is used twice, all tokens for the device are revoked as a security precaution.

Using Access Tokens

Include the access token in the Authorization header for all subsequent API calls:

curl https://vault.example.com/api/vaults \
-H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..."

Error Responses

StatusBodyMeaning
400{"error": "invalid_request"}Missing or malformed fields
401{"error": "invalid_credentials"}Wrong email or auth hash
401{"error": "2fa_required"}2FA challenge needed
401{"error": "invalid_2fa_code"}Wrong TOTP code or WebAuthn failure
429{"error": "rate_limited", "retry_after": 30}Too many failed attempts

After 5 consecutive failed login attempts, the account is temporarily locked for 30 seconds. The lockout duration doubles with each subsequent block, up to a maximum of 15 minutes.