- JavaScript 67.4%
- HTML 32.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| docs | ||
| public | ||
| scripts | ||
| src | ||
| test | ||
| .gitignore | ||
| ENROLL.md | ||
| NATS.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SUMMARY.md | ||
| TOTP.md | ||
verae-staff-iam
Internal staff user management: named accounts, roles, permissions, audit.
Forgejo: https://git.georgelambert.org/marchon/verae-staff-iam
Port :3028. UI: sign-in, people, roles, audit. Source of truth for who may use CS, sales, and other back-office doors. Not customer portal accounts.
Table of Contents
- Install
- Usage
- Docs
- Seed lab users
- Staff security gates
- Wire other doors
- Roles
- Active role interface
- First login
- Account security UI
- Contributing
- License
Install
git clone ssh://git@git.georgelambert.org:2223/marchon/verae-staff-iam.git
cd verae-staff-iam
npm install
Usage
npm start
# optional gates:
# STAFF_TOTP_REQUIRED=1 npm start
# STAFF_MTLS_ENFORCE=1 npm start
# LAB ONLY (never production): init lab-pki then:
# ./scripts/lab-test-ca.sh init --dir ./lab-pki
# LAB_CA_ENABLE=1 npm start # POST /enroll/csr → 201 + X-Lab-CA:1
npm test
Open http://127.0.0.1:3028/login. Persist store with STAFF_IAM_PATH. Login is rate-limited (8 failures / 10 minutes / IP+username). JSON APIs return 401/403; HTML doors 302 to /login.
Docs
| Doc | Topic |
|---|---|
| ENROLL.md | Device cert (mTLS): status + CSR schema; default POST /enroll/csr → 501; opt-in LAB ONLY LAB_CA_ENABLE=1 → 201 + X-Lab-CA:1 (never production) |
| docs/TEST-CA-DESIGN.md | Lab CA script + gated HTTP path; production mint still backlog |
| docs/PKI-MINT-BACKLOG.md | Live production PKI checklist |
| TOTP.md | Authenticator 2FA + STAFF_TOTP_REQUIRED enroll path; how login / app UI use flags |
| NATS.md | NATS notes (if used) |
| SUMMARY.md | One-line service summary |
Seed lab users
| Username | Password | Roles |
|---|---|---|
admin |
admin-dev-key |
owner (all) |
cs |
cs-dev-key |
cs |
sales |
sales-dev-key |
sales |
accounting |
acct-dev-key |
accounting |
operator |
fleet-dev-key |
operator |
Override with IAM_OWNER_PASSWORD, IAM_CS_PASSWORD, etc.
Staff security gates
| Gate | Behavior |
|---|---|
| TOTP | Optional per user by default. POST /2fa/setup → enable {code} while signed in. When totpEnabled, login returns 401 {error:"totp_required"} until a valid 6-digit totpCode is sent. UI: My security + setup QR/secret. Full ops: TOTP.md. |
| TOTP required | STAFF_TOTP_REQUIRED=1 — seeds stay totpEnabled: false; after password login full app access is 403 {error:"totp_enrollment_required"} until /2fa/setup + /2fa/enable. /me, /2fa/*, /logout, /enroll/status still work. Disable refused while policy is on. |
| mTLS | Copy + status + optional lab mint. GET /enroll/status returns state, nextSteps[], csrSchemaUrl, csrPostStatus (501 default; 201 when lab CA ready), labCa probe, proxyHeadersSeen, proxy. GET /enroll/csr/schema = fields only. Default POST /enroll/csr → 501. LAB ONLY: LAB_CA_ENABLE=1 + lab-pki → shell-out lab-test-ca.sh → 201 + X-Lab-CA: 1 (refused when NODE_ENV=production). Enforce with STAFF_MTLS_ENFORCE=1 and mtlsEnrolled: true. Production mint still external PKI. ENROLL.md. |
| Session | Cookie / Bearer staff_session only. Never issues customer xVerae- tokens. |
Wire other doors
STAFF_IAM_URL=http://127.0.0.1:3028
STAFF_AUTH=1 # department HTML still redirects if check fails
GET /check?permission=cs.credit — cookie or Authorization: Bearer. Cookie name remains staff_session. Multi-host: STAFF_COOKIE_DOMAIN.
Roles
owner, admin, iam-admin, billing-admin, cs, starter, ender, sales, accounting, operator, viewer. Full permission map: GET /roles.
Product catalog (edge dual-staff)
GET /roles/catalog
→ { "roles": ["starter", "ender", "admin"] }
Public, no auth. Edge (STAFF_IAM_URL) uses this catalog when dual-staff impersonate is on. Per-user roles:
GET /users/:usernameOrId/roles
→ { "id", "username", "active", "roles": ["ender", …] }
Roles-only (no secrets). Unknown/inactive → 404 { error, roles: [] }. Assign starter / ender / admin on people like any other role id.
Active role interface
Sessions carry capabilities (assigned roles) and currentRole (active interface).
GET /me/GET /interfaces— capabilities, currentRole, door URLsPOST /session/role{ "role": "sales" }— switch active role (must be assigned); returns navigateurl- Staff UIs show a top-right Interface dropdown when a user has two or more capabilities
- Admins (
owner/iam-admin) manage assignments under People & roles on this host
CORS allows credentialed calls from *.zapier.georgelambert.org so department doors can call IAM directly.
First login
New staff accounts are created with mustChangePassword=true (invite OTP is one-time).
After /login the session cannot use staff doors (GET /check returns 403 must_change_password)
until the user completes:
GET /change-password→POST /password/change{ currentPassword, newPassword }- Optional
GET /enroll-2fa→POST /2fa/setup→POST /2fa/enable { code }, orPOST /2fa/skip(skip denied whenSTAFF_TOTP_REQUIRED)
When authenticator is enabled, subsequent logins require totpCode (or 401 totp_required).
Admin: POST /admin/mark-must-change { usernames?: string[] } (defaults to the seven owner invites)
re-marks accounts so their invite OTP cannot skip the change-password step.
Account security UI
Signed-in Security tab covers:
- Change password (
/change-password) - Authenticator enrollment (
/enroll-2fa,/2fa/*) — required whenSTAFF_TOTP_REQUIRED=1 - Browser client certificate (mTLS) status from
GET /enroll/status(state,nextSteps,updatedAt,proxyHeadersSeen,labCachips) — default no mint; lab opt-in only viaLAB_CA_ENABLE=1
Contributing
Questions: open an issue at https://git.georgelambert.org/marchon/verae-staff-iam/issues. Pull requests are accepted.
License
UNLICENSED © George Lambert