Also repairs two stale Phase 21 fixtures: - Supplies a valid PDF to exercise the upload dependency-failure path. - Injects getPersonalAccount into the representation-involvement VM fixture.
123 lines
6.1 KiB
Markdown
123 lines
6.1 KiB
Markdown
# Administrative authentication foundation
|
|
|
|
## Integrated administrative sign-in
|
|
|
|
Administrators sign in at `/admin/login` using the shared credential configured
|
|
as `ADMIN_AUTH_SECRET`. The page submits the credential directly to
|
|
`POST /api/admin/auth/login`; it does not place the credential in a URL or store
|
|
it in browser storage.
|
|
|
|
Successful authentication creates the administrative session cookie and sends
|
|
the browser to `/admin`. The `/admin` server-side loader validates that cookie
|
|
before loading administrative data. A valid session is not sufficient by
|
|
itself: the existing `/admin` IP allowlist remains an additional restriction,
|
|
and a valid administrator outside the allowed network is sent to `/403`.
|
|
|
|
The administrative interface provides a **Sign out** control which calls
|
|
`POST /api/admin/auth/logout`, clears the cookie, and returns the browser to
|
|
`/admin/login`. Sessions expire after 20 minutes and expired, invalid, or
|
|
missing sessions are redirected to the login page.
|
|
|
|
## Required server-side configuration
|
|
|
|
The deployment must provide all of the following server-side values:
|
|
|
|
- `ADMIN_AUTH_SECRET`: a unique high-entropy administrative passphrase of at
|
|
least 32 characters.
|
|
- `ADMIN_SESSION_SECRET`: a different unique high-entropy signing key of at
|
|
least 32 characters.
|
|
- `NEXTAUTH_URL`: the existing canonical application URL. Administrative login
|
|
and logout compare the request `Origin` with this server-configured origin.
|
|
|
|
The administrative values must never use a `NEXT_PUBLIC_` prefix or be exposed
|
|
to browser JavaScript. Do not commit real values. Missing, short, or identical
|
|
administrative secrets fail closed.
|
|
|
|
## Security properties and limitations
|
|
|
|
- Credentials are derived with Node `scrypt` and compared with
|
|
`timingSafeEqual`.
|
|
- Sessions are stateless, versioned HMAC-SHA256 tokens with a 20-minute fixed
|
|
lifetime and a random nonce.
|
|
- The cookie is `HttpOnly`, `SameSite=Strict`, scoped to `/`, explicitly
|
|
expires, and is `Secure` in production.
|
|
- Login and logout require an exact same-origin `Origin` match against
|
|
`NEXTAUTH_URL`; missing or malformed origins fail closed.
|
|
- Authentication responses use `Cache-Control: no-store`.
|
|
- Logout clears the browser cookie. Because sessions are stateless, a stolen
|
|
token can remain valid until its short expiry; later protected routes must
|
|
continue verifying signature and expiry on every request.
|
|
- This is shared-credential authentication. A valid token proves knowledge of
|
|
the shared credential, not the identity of a named administrator.
|
|
- Protecting `/admin` prevents its server-side Prisma, storage, and CRM data
|
|
loading from running without a valid administrative session. It does not by
|
|
itself protect browser-addressable API routes.
|
|
|
|
## Administrative API protection limitations
|
|
|
|
The current administrative UI directly uses API routes that do not yet enforce
|
|
the administrative session, including:
|
|
|
|
- `/api/admin/getnewappeals_api`
|
|
- `/api/admin/getlatestdocuments_api`
|
|
- `/api/admin/getStatusCountsByAppeal_api`
|
|
- `/api/admin/getStatusCountsByAppealAndLPA_api`
|
|
|
|
The appeals and documents routes expose paging operations used after the page
|
|
has loaded. The status routes expose administrative dashboard reads. These
|
|
routes retain their existing relay token and HMAC behaviour but have no
|
|
independent administrative-session authorization. A later authorised stage
|
|
must enforce the administrative session on privileged API routes and account
|
|
for both browser calls and the existing server-side bootstrap callers.
|
|
|
|
Storage download, representation editing, and queue actions retain their
|
|
existing authorization behaviour. The other administrative API and storage
|
|
limitations described in this section remain open.
|
|
|
|
Directly observed storage routes include:
|
|
|
|
- `/api/file/getrepsblobproxy`, which forwards a caller-supplied container using
|
|
server relay credentials and path signing.
|
|
- `/api/file/editRepJson`, which performs a direct blob mutation and has no
|
|
visible administrative-session or HMAC check.
|
|
- `/api/file/downloadblob` and `/api/file/createrepcompletemessage_api`, which
|
|
enforce existing path-HMAC checks but not administrative authorization.
|
|
|
|
## Stage 3 deletion controls
|
|
|
|
Ordinary case and representation deletion through `/api/file/deleteblobcase`
|
|
and `/api/file/deleteblobrep` now requires a valid NextAuth session whose user
|
|
ID matches the requested Azure container. The existing signed-query/HMAC check
|
|
also remains required. Requests that fail ownership or HMAC validation are
|
|
rejected before the Azure storage deletion operation is called.
|
|
|
|
Administrative cross-container representation deletion uses the dedicated
|
|
`POST /api/admin/deleteblobrep` route. That endpoint independently validates
|
|
the administrative session and requires a trusted same-origin request. It also
|
|
validates the requested container, case-folder segment, and representation-file
|
|
segment before performing deletion.
|
|
|
|
Administrative case deletion was not investigated or remediated as part of
|
|
Stage 3 and remains a separate, unverified security boundary. The other
|
|
administrative API and storage limitations documented above also remain open.
|
|
|
|
Automated contract verification for the implemented deletion controls has
|
|
passed. Live Azure behaviour, browser-level behaviour, and deployment
|
|
configuration have not been verified by that automated coverage. These changes
|
|
do not establish closure of the overall ITHC finding.
|
|
|
|
## Deployment requirements before production use
|
|
|
|
The repository does not provide a demonstrated distributed rate-limit service.
|
|
Administrative login must therefore be protected by an ingress, gateway, WAF,
|
|
or shared backing service that enforces rate limits consistently across all
|
|
deployed instances. A process-local limiter is intentionally not used because
|
|
it would reset on restart and could be bypassed across instances.
|
|
|
|
The deployment must also ensure `NEXTAUTH_URL` is the correct canonical HTTPS
|
|
origin. Login and logout intentionally do not derive trust from `Host` or
|
|
forwarding headers.
|
|
|
|
Any future administrative state-changing API must require more than possession
|
|
of the cookie: it must independently validate the administrative session and
|
|
enforce same-origin/CSRF protection before performing side effects. |