Add explicit login functionality

This commit is contained in:
2026-09-13 00:50:15 +02:00
parent 74bfdbfd8a
commit 1b77cdd165
14 changed files with 629 additions and 258 deletions
+34 -19
View File
@@ -81,22 +81,27 @@ as `Authorization: Bearer <token>`, or paste it into the form's token field.
and on `SIGHUP`. It must stay mode `0600` — the server refuses to start
otherwise, since it holds credential material.
### Remembering a token
### Logging in
Tick **Remember this token on this device** and the server sets a cookie, so the
token only has to be pasted once. The upload page then says who you are and
shows your real limits; **Forget** clears it, as does unticking the box on your
next upload. It works with JavaScript disabled, because the browser sends the
cookie either way.
Open **Log in**, paste a token, and the browser keeps it until you log out. The
header then shows who you are, the upload page shows your real limits, and
**Administration** appears if the token is an admin one. Tick *stay logged in*
and it survives a browser restart; leave it unticked and it dies with the
browser, which is the right choice on a machine that is not yours.
The cookie is `HttpOnly`, which means the page's own script cannot read it — the
server resolves the identity and renders it instead. That is deliberately
unlike `localStorage`, where any script injected into the origin could read the
token straight out and walk away with it. It is also `SameSite=Strict`, so no
other site can make your browser upload or delete anything with it attached.
The upload form also keeps a collapsed **Use a different token for this upload**
field. That one applies to a single upload and never changes the session, so a
quick upload under another token does not mean logging in and out.
Callers sending `Authorization: Bearer` are never given a cookie; an API client
keeps its own credentials.
The session cookie is `HttpOnly`, so the page's own script cannot read it — the
server resolves the session and renders it. That is deliberately unlike
`localStorage`, where any script injected into the origin could read the token
straight out. It is also `SameSite=Strict`, so no other site can make your
browser upload or delete anything with it attached.
Nothing about the session involves JavaScript: every page states who you are
because the server rendered it that way. Callers sending `Authorization: Bearer`
are never given a cookie; an API client keeps its own credentials.
## Uploading
@@ -143,8 +148,10 @@ file is accepted.
| `GET /d/{id}` | the file, as an attachment; supports resuming |
| `GET /i/{id}` | a page showing name, size, expiry, digest — and where a delete token is used |
| `POST /api/d/{id}/delete` | delete, with `token=` in the form or `Authorization: Bearer` |
| `POST /api/forget` | clear a remembered token |
| `GET /login`, `POST /login` | start a browser session with a token |
| `POST /logout` | end it |
| `GET /admin` | administration page; admin tokens only |
| `GET /api/limits` | what the presented credential may do; for scripts, the pages do not use it |
Deleting accepts the object's delete token, the token that uploaded it, or any
admin token.
@@ -225,12 +232,20 @@ Worth knowing if you are going to run this somewhere real.
protection — so the upload handler maintains a per-read deadline instead.
- **`X-Forwarded-For` is ignored** unless the peer is a configured
`--trusted-proxy`, and then only to skip further trusted hops.
- **A remembered token lives in an `HttpOnly`, `SameSite=Strict` cookie**, not
in `localStorage`, so neither an injected script nor another website can get
at it. `Secure` is set whenever the service knows it is being served over
- **The session lives in an `HttpOnly`, `SameSite=Strict` cookie**, not in
`localStorage`, so neither an injected script nor another website can get at
it. `Secure` is set whenever the service knows it is being served over
HTTPS — from `--public-url`, from a TLS connection, or from a trusted proxy's
`X-Forwarded-Proto`. On browsers old enough to ignore `SameSite` entirely
(pre-2017) the cookie would be CSRF-exposed; nothing here defends that case.
`X-Forwarded-Proto`.
- **Every `POST` must be same-origin.** `SameSite` covers requests that need a
cookie, but logging in needs none: without this check a hostile page could
sign a visitor into an account it controls and collect what they upload next.
Browsers label their own requests with `Sec-Fetch-Site`, falling back to
`Origin`; a request carrying neither is not a browser and is allowed through,
since a bearer token cannot be attached by a third party anyway.
- **Failed credential attempts are throttled per address** — a wrong delete
token or a wrong login — while correct ones are never delayed, because only
failures consume the budget.
- Rate limiting is per client address, with a separate bound on uploads in
flight. Both are in memory and reset on restart.