Authentication
The Qpher API supports two authentication modes. Which one applies depends on who is calling and from where.
Two Auth Modes
| Aspect | API key (X-API-Key) | Portal JWT (qpher_access_token cookie) |
|---|---|---|
| Who uses it | SDKs, CLI, backend integrations | Human users in the Portal or Vault iOS app |
| Credential | A long-lived API key scoped to a tenant | Short-lived access token + refresh token |
| Rotation | Self-service via the Portal | Automatic on refresh; password change revokes all |
| MFA step-up applies | No | Yes — see below |
| Typical call | X-API-Key: qph_live_… | Cookie sent automatically by the browser |
Both modes are terminated by the same API Gateway, which authenticates the caller, applies rate limits, and evaluates the Policy Engine before routing the request.
API Key Authentication
Every programmatic call to api.qpher.ai uses an API key:
POST /api/v1/kem/encrypt HTTP/1.1
Host: api.qpher.ai
X-API-Key: qph_live_example
Content-Type: application/json
Obtain a key from the Portal (Settings → API Keys → Create Key). The key is displayed once at creation time and then hashed at rest — it cannot be recovered. Rotate from the same page. See Key Management API for automation.
API keys are bearer credentials. Put them in a secrets manager or environment variable. Do not commit them to source control or paste them into logs.
Portal JWT Sessions
Portal users authenticate by email and password. On a successful login the
server sets two httpOnly cookies:
qpher_access_token— short-lived (~15 min) session token.qpher_refresh_token— long-lived (~7 days) refresh token, rotated on use.
The browser sends these automatically; no application code needs to handle them. Third-party integrations should not attempt to use Portal JWTs — use an API key instead.
See ADR-0024 for the full Portal auth model.
Step-Up Re-Verification
Some actions require the caller to re-prove presence even inside a valid session. The server does not trust the session cookie alone for these routes — it requires a short-lived, single-use step-up token generated immediately before the action.
Which endpoints require step-up
| Endpoint | What it does |
|---|---|
POST /api/v1/billing/subscriptions/checkout | Start a paid subscription |
POST /api/v1/billing/subscriptions/cancel | Cancel the subscription |
POST /api/v1/tenants/{id}/api-keys/rotate | Rotate an API key |
DELETE /api/v1/team/members/{id} | Remove a team member |
POST /api/v1/auth/change-password | Change the account password |
POST /api/v1/kms/keys/rotate | Rotate a PQC key (Portal calls only; internal service-to-service calls are exempt) |
How to obtain a step-up token
POST /api/v1/auth/step-up HTTP/1.1
Host: api.qpher.ai
Content-Type: application/json
Cookie: qpher_access_token=eyJ...example
{
"totp_code": "123456"
}
If MFA is not enabled on the account, send { "password": "…" } instead.
Response:
{
"step_up_token": "eyJ...example",
"expires_in": 300
}
How to use a step-up token
Set the X-Step-Up-Token header on the sensitive request:
POST /api/v1/tenants/abc123/api-keys/rotate HTTP/1.1
Host: api.qpher.ai
Cookie: qpher_access_token=eyJ...example
X-Step-Up-Token: eyJ...example
Content-Type: application/json
{ "key_id": "key_xyz" }
Properties of a step-up token:
- TTL — 5 minutes.
- Single-use — consumed on first successful action; a second attempt with
the same token returns
ERR_MFA_013. - User-scoped — valid only for the user who obtained it. Forging or replaying another user's token is rejected with the same error.
Step-up applies only to Portal JWT sessions. Requests authenticated with
X-API-Key do not see the X-Step-Up-Token requirement
and never return ERR_MFA_013. API keys are the highest-trust
credential; re-verification at the API-key tier does not meaningfully improve
the threat model.
Error Codes (auth-related)
| Code | HTTP | Meaning |
|---|---|---|
ERR_AUTH_001 | 401 | Missing or invalid credential (X-API-Key or Portal JWT) |
ERR_AUTH_MFA_001 | 401 | MFA verification required to complete login |
ERR_MFA_001 | 401 | Invalid code during MFA or step-up verification |
ERR_MFA_011 | 403 | Plan gate — organization-enforced MFA policy is Enterprise+ only |
ERR_MFA_013 | 401 | Re-verification required (missing or expired step-up token) |
The Error Codes page has the full catalogue.
Example: Rotate an API Key with Step-Up
Two requests. The first exchanges the user's TOTP code for a step-up token; the second uses that token on the rotate call. Values are illustrative — do not copy them verbatim.
# Step 1: obtain step-up token (illustrative only)
curl -X POST https://api.qpher.ai/api/v1/auth/step-up \
-H "Content-Type: application/json" \
-b "qpher_access_token=eyJ...example" \
-d '{"totp_code": "123456"}'
# → { "step_up_token": "eyJ...stepup", "expires_in": 300 }
# Step 2: rotate the API key (illustrative only)
curl -X POST https://api.qpher.ai/api/v1/tenants/abc123/api-keys/rotate \
-H "Content-Type: application/json" \
-H "X-Step-Up-Token: eyJ...stepup" \
-b "qpher_access_token=eyJ...example" \
-d '{"key_id": "key_xyz"}'
If the step-up token is missing, expired, or already consumed, the rotate call
returns 401 ERR_MFA_013. The client should discard the token and prompt the
user to re-verify.