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
| Status | Body | Meaning |
|---|---|---|
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.