Repository metrics
- Stars
- (260 stars)
- PR merge metrics
- (PR metrics pending)
Description
Version: v1.4.1 (e47ddce)
Environment:
- Deployed behind Traefik reverse proxy (Docker Swarm), HTTPS
- Basic auth applied at Traefik level (unrelated to Ivory's own auth, ruled out via testing)
Description
After a successful login (Basic auth), /api/basic/login correctly returns a valid JWT in the response body and sets it as an HttpOnly token cookie. However, the browser UI never re-authenticates the user: on page refresh, /api/info always reports "authorised": false, "error": "username cannot be empty", and the login screen reappears — even though the session is fully valid on the backend.
Root cause (confirmed via isolated curl testing, no browser/cache/extensions involved)
POST /api/basic/login→200 OK, returns valid JWT in body + setstokencookie (HttpOnly, SameSite=Strict)GET /api/infosending only the cookies from step 1 →"authorised": falseGET /api/infosendingAuthorization: Bearer <same token>header instead →"authorised": true, full permission set returned correctly
So the backend session validation only works via the Authorization: Bearer header — not via the token cookie it itself sets.
Checking the browser's Local Storage and Session Storage after a successful login shows the token is never persisted anywhere accessible to frontend JS (only an unrelated appearance key exists in Local Storage). This means the SPA has no token available to attach as a Bearer header on subsequent requests, so every session appears unauthenticated immediately after login/refresh.
Steps to reproduce
- Deploy Ivory behind any reverse proxy with HTTPS
- Log in via Basic auth (username/password) — see "LOGIN - DONE" toast
- Refresh the page → redirected back to login screen
/api/inforeportsauthorised: false
Expected behavior
Either:
- The frontend persists the token (e.g. sessionStorage) and attaches it as
Authorization: Bearer <token>on all subsequent API requests, or - The backend also accepts the
tokencookie for session validation (so a plain cookie-based flow works without frontend JS involvement)
curl reproduction (sanitized)
POST /api/basic/login
< HTTP/1.1 200 OK
< set-cookie: token=eyJhbGci...; Path=/; Max-Age=3659; HttpOnly; SameSite=Strict
{"response":{"expire":"...","token":"eyJhbGci..."}}
GET /api/info
Cookie: token=eyJhbGci...
< HTTP/1.1 200 OK
{"response":{"auth":{"supported":[0],"authorised":false,"user":{"username":"","permissions":null},"error":"username cannot be empty"}, ...}}
GET /api/info
Authorization: Bearer eyJhbGci...
< HTTP/1.1 200 OK
{"response":{"auth":{"supported":[0],"authorised":true,"user":{"username":"admin","permissions":{...}}}, ...}}