Close the last three UI leftovers: shared staff HTML, cookie domain, stock Swagger label.
Some checks are pending
offline / test (push) Waiting to run

verae-staff-ui holds one review template for CS and access-staff.
Staff cookies take STAFF_COOKIE_DOMAIN for a reverse-proxy host.
/docs stays vendor Swagger with an integrator banner only.
This commit is contained in:
George Lambert 2026-09-11 18:22:57 -04:00
parent b68fefdea8
commit d8efffe8be
23 changed files with 368 additions and 44 deletions

View file

@ -7,3 +7,25 @@ Shared cookie login for CS / sales / accounting / access-staff HTML.
Port `:3027`. Set `STAFF_AUTH=1` on the department servers and `STAFF_SESSION_URL=http://127.0.0.1:3027`. Cookie host is the browser host (ports share `127.0.0.1`). JSON APIs stay open unless you also send `x-staff-key`.
Default key: `STAFF_KEY` or `ADMIN_KEY` or `admin-dev-key`.
## Multiple hostnames
Cookies are host-scoped. On one operator box (`127.0.0.1`) that is enough. For several DNS names, put one reverse proxy in front and set `STAFF_COOKIE_DOMAIN`:
```nginx
server {
server_name staff.example.com;
location /session/ { proxy_pass http://127.0.0.1:3027/; }
location /cs/ { proxy_pass http://127.0.0.1:3011/; }
location /sales/ { proxy_pass http://127.0.0.1:3012/; }
location /acct/ { proxy_pass http://127.0.0.1:3013/; }
location /staff/ { proxy_pass http://127.0.0.1:3025/; }
}
```
```bash
STAFF_COOKIE_DOMAIN=.example.com
STAFF_COOKIE_SECURE=1
STAFF_SESSION_URL=https://staff.example.com/session
STAFF_AUTH=1
```

View file

@ -9,7 +9,11 @@ export function sessionToken() {
}
export function cookieHeader() {
return `staff_session=${sessionToken()}; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400`;
let s = `staff_session=${sessionToken()}; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400`;
const domain = process.env.STAFF_COOKIE_DOMAIN;
if (domain) s += `; Domain=${domain}`;
if (process.env.STAFF_COOKIE_SECURE === '1') s += '; Secure';
return s;
}
export function cookieOk(req) {

View file

@ -1,6 +1,6 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { sessionToken, cookieOk, headerOk } from '../src/token.js';
import { sessionToken, cookieOk, headerOk, cookieHeader } from '../src/token.js';
test('cookie matches HMAC of staff key', () => {
const tok = sessionToken();
@ -9,3 +9,11 @@ test('cookie matches HMAC of staff key', () => {
assert.equal(cookieOk({ headers: { cookie: 'staff_session=nope' } }), false);
assert.equal(headerOk({ headers: { 'x-staff-key': process.env.STAFF_KEY || 'admin-dev-key' } }), true);
});
test('cookie Domain is optional', () => {
delete process.env.STAFF_COOKIE_DOMAIN;
assert.equal(cookieHeader().includes('Domain='), false);
process.env.STAFF_COOKIE_DOMAIN = '.example.com';
assert.match(cookieHeader(), /Domain=\.example.com/);
delete process.env.STAFF_COOKIE_DOMAIN;
});