Skip to content

Auth in development

Francisca Portugal edited this page Nov 2, 2026 · 1 revision

How login works locally: the dev username/password flow, seeded roles, optional real OIDC, and the compatibility routes. Status: current. Wire contract: Auth API.

Two flows

Flow When Backend Frontend
Dev login Local development POST /v1/auth/dev-login button when VITE_APP_FEDERATED_AUTH=false
OIDC Production / federated testing GET /v1/auth/login → Sigarra → /callback link when VITE_APP_FEDERATED_AUTH=true

Both end with the same opaque session cookie and the same Session row. There is no “dev session”.

Dev login

The default. LoginButton posts without a body; the backend authenticates with the account in apps/backend/.env:

DEV_LOGIN_ENABLED=true
SIGARRA_USERNAME=up202100000
SIGARRA_PASSWORD=…
  • If the request carries { username, password }, those are used instead.
  • DEV_LOGIN_ENABLED is forced off when NODE_ENV=production; the route then answers 404 not-found.
  • No configured account and no body → 503 dev-login-not-configured.
  • Wrong credentials → 401 invalid-credentials.
  • On success the user is upserted by NMEC, so the seeded placeholder name is replaced by the real one.

The credentials are real SIGARRA credentials. Use your own; do not commit them.

Roles and the admin account

Roles are seeded, never assigned by login:

ADMIN_UP=202207150        # your NMEC (up prefix optional)
ADMIN_ROLE=ADMIN          # USER | ADMIN | SUPERUSER

Then re-seed:

cd apps/backend
bun run db:seed:demo

db:seed:demo creates that account with the role and, for ADMIN/SUPERUSER, full AdminCourse/AdminOccurrence scope. ADMIN_ROLE=USER creates the account with no admin scope. Logging in as that NMEC does not elevate it; the role comes from the row.

Re-seeding truncates every table. Do it before you have data you care about.

Real OIDC locally

To test the production flow:

# apps/backend/.env
OIDC_CLIENT_ID=…
OIDC_CLIENT_SECRET=…
OIDC_REDIRECT_URI=http://localhost:3000/v1/auth/callback
FRONTEND_URL=http://localhost:3100
ALLOWED_ORIGINS=http://localhost:3100
COOKIE_SECURE=false
# apps/frontend/.env
VITE_APP_FEDERATED_AUTH=true
  1. Register http://localhost:3000/v1/auth/callback as an allowed redirect URI on the Keycloak client.
  2. Restart both dev servers.
  3. Click Entrar → Sigarra → back to the callback → redirect to FRONTEND_URL.

Notes:

  • No /etc/hosts entry is required for localhost; use the redirect URI registered on the client.
  • FRONTEND_URL is both the default post-login destination and the return_to allowlist.
  • Without client id/secret the routes answer 503 oidc-not-configured; the rest of the app still boots.
  • The callback sets HttpOnly, SameSite=Lax cookies; Secure is off locally because the dev origin is HTTP.

Compatibility routes

For the un-migrated frontend, mounted at the root (not under /v1):

Route Purpose
GET /oidc-auth/authenticate/ legacy login link → /v1/auth/login
POST /oidc-auth/logout/ legacy logout; ignores the CSRF header
GET /auth/info/ legacy session shape (username, eligible_exchange, is_admin)
POST /sigarra_login/ legacy dev login; same gating as /v1/auth/dev-login

/auth/info/ always reports eligible_exchange: false on the new backend. They are 🔁 aliases, not a second auth system.

Checking it from the terminal

# dev login; store cookies in a jar
curl -i -c /tmp/tts-cookies -X POST http://localhost:3000/v1/auth/dev-login

# who am I?
curl -s -b /tmp/tts-cookies http://localhost:3000/v1/auth/me

# catalog requires the session
curl -s -b /tmp/tts-cookies http://localhost:3000/v1/faculties
curl -s http://localhost:3000/v1/faculties          # → 401

The session cookie is opaque and HttpOnly; do not try to read it from JavaScript.

Troubleshooting

Symptom Cause / fix
404 on dev login DEV_LOGIN_ENABLED=false, or NODE_ENV=production
503 dev-login-not-configured no request body and no SIGARRA_USERNAME/PASSWORD
401 invalid-credentials wrong SIGARRA username/password
Button opens OIDC instead of dev login VITE_APP_FEDERATED_AUTH=true in the frontend .env
503 oidc-not-configured missing OIDC_CLIENT_ID / OIDC_CLIENT_SECRET
400 oidc-state-mismatch stale/blocked oidc_state cookie, or two tabs mid-flow
Login succeeds, /me says anonymous cookie not sent: wrong origin, Secure on HTTP, or CORS
Logged in but no admin UI role is USER, or you logged in as another NMEC

After changing auth env vars, restart the backend (and rebuild the frontend if a VITE_* value changed).

See also


Last verified: 2026-11-02

Clone this wiki locally