Authentication
Access Token, Refresh Cookie and the server-side session control plane.
Mirava panel authentication combines a short-lived Access Token, an HttpOnly Refresh Cookie and a server-side session control plane.
Auth Model
| Credential | Form | Lifetime | Stored in |
|---|---|---|---|
| Access Token | JWT | 15 minutes | Browser memory only |
| Refresh Token | Opaque random value | Up to 30 days | HttpOnly Cookie (SameSite=Strict) |
new_api_has_session | Constant 1 | Same as Refresh Cookie | Plain cookie (hint only, no credential) |
- The Access Token is sent via
Authorization: Bearer <token>and kept in browser memory only. - For the Refresh Token, the server stores only an HMAC digest and rotates it on every refresh.
new_api_has_sessiononly declares that a Refresh Cookie was once issued. It plays no part in any auth decision.
Server-Side Authority
user_sessions is the login session control plane, recording device, IP, login method, last active time, expiry and revocation state.
Session state in the database is the final authority. Redis session cache is an acceleration layer only; on a cache miss or with Redis disabled, validation falls back to the database.
- When a user's password, status, role or security factor changes,
auth_versionincrements and invalidates old sessions. - Group upgrades from a subscription only refresh the authorization cache and do not log out any device.
Secret Configuration
| Env var | Purpose |
|---|---|
SESSION_SECRET | Derives purpose-specific keys for Access Token, Security Proof, Refresh Token digest and AuthFlow digest |
CRYPTO_SECRET | Cache key digests when sharing Redis across nodes |
Production and multi-node deployments must configure the same high-strength random value on all nodes. Changing SESSION_SECRET invalidates all existing logins, temporary auth flows and Security Proofs.
Multi-Node Redis Topology
Multi-node deployments must share the same primary database. Login sessions, per-account active session limits and issuance window counters are all database-authoritative.
| Redis setup | Session propagation | Rate limit semantics |
|---|---|---|
| Shared Redis | Revocation and version release propagate immediately | Quota shared across nodes |
| Per-node Redis | Converges after session cache TTL (at most SYNC_FREQUENCY) | Counted per node |
| No Redis | Every check hits the database | Per-node in-memory limiter |
SYNC_FREQUENCY defaults to, and falls back to, 60 seconds.
Browser API
All successful logins (password, 2FA, Passkey, OAuth, WeChat, Telegram) return the same structure:
{
"success": true,
"data": {
"access_token": "...",
"token_type": "Bearer",
"access_expires_at": 1730000000,
"user": {},
"session": {
"sid": "...",
"current": true,
"login_method": "password",
"ip": "...",
"user_agent": "...",
"created_at": 1730000000,
"last_active_at": 1730000000,
"expires_at": 1732592000
}
}
}Calling the API
External APIs always use an Access Token:
curl https://mirava.art/v1/models \
-H "Authorization: Bearer sk-xxxxxxxx"Panel requests no longer depend on Gin session and no longer require the New-Api-User header. Third-party callers should use an API token (the sk- prefix).
How is this guide?