veegres/ivory

Add fallback from Header auth to Session auth

Open

#727 opened on Jul 7, 2026

View on GitHub
 (6 comments) (0 reactions) (0 assignees)TypeScript (19 forks)auto 404
enhancementgogood first issue

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)

  1. POST /api/basic/login200 OK, returns valid JWT in body + sets token cookie (HttpOnly, SameSite=Strict)
  2. GET /api/info sending only the cookies from step 1 → "authorised": false
  3. GET /api/info sending Authorization: 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

  1. Deploy Ivory behind any reverse proxy with HTTPS
  2. Log in via Basic auth (username/password) — see "LOGIN - DONE" toast
  3. Refresh the page → redirected back to login screen
  4. /api/info reports authorised: 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 token cookie 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":{...}}}, ...}}

Contributor guide