Commit Graph
521 Commits
Author SHA1 Message Date
LeoVasanko 9da1abc2c7 Handshake: carry ready-frame rejections into the connection state
The satellite's domain rejections at registration/reconnect were dropped
until the next domains_ack; seed the connection with them so the upstream
link list shows conflicts immediately. Plus a dead frontend import.
2026-10-01 06:36:15 +00:00
LeoVasanko 0638117279 Drop the dead registry-rebuild listener machinery, guard the minted-link dialog
Satellite replicas no longer reconcile off registry rebuilds (uplinks are
db-driven now), so the listener mechanism has no users left. The minted
link URL step of the dialog dismisses with Done instead of re-minting.
2026-10-01 06:33:59 +00:00
LeoVasanko 28fa0ff61f Tests for the link model + lint/format pass
test_remote.py: link URL parsing, RAM-only offer mint/spend/expiry,
pushed-domain application (accept/idempotence/removal/local conflict/
auth-host collision), admin link lifecycle (offer, scope, revoke) and
uplink list/unlink, remote-write rejection on the domains API, live feed
scope mutation.

test_satellite_e2e.py: real registration handshake over TCP (key material
identical on both sides, duplicate and bad-URL rejections), established
link reconnect, revocation unlinking the satellite, live scope shrink
stopping the feed, domain conflicts reported upstream.

Also: ruff format drift fixes in startupbox/test_cli.
2026-10-01 06:30:32 +00:00
LeoVasanko 05ea188ac9 Docs: RemoteProxy.md for the link model
Configuration is one copy-paste of a wss link URL (Satellites panel or
the paskia satellite CLI), registration is the online 3-way handshake,
scope is adjustable post-registration, the upstream is authoritative for
pushed domains, revocation unlinks both sides.
2026-10-01 06:16:50 +00:00
LeoVasanko ebe18912fc Admin UI: Satellites panel replaces domain-dialog satellite config
The domain edit dialog returns to pure local domain editing — the remote
toggle, sync URL field, uplink probing and per-domain token management
are gone (the bad checkbox-on-its-own-line UX with them).

Satellite setup now lives in a self-contained Satellites section of the
admin overview, covering both roles:

- Upstream: links table (satellite label, served domains, live status,
  the satellite's rejection reports), New satellite link dialog with
  domain scope checkboxes minting the wss link URL (shown once, 10 min,
  single-use), scope editing pushed live, revocation with confirm.
- Satellite: uplinks table (upstream URL, served domains, live
  connected/cached/offline status with gentle polling while
  disconnected), Add uplink dialog taking the pasted link URL with the
  registration handshake running synchronously (immediate errors),
  unlink with confirm.

Remote domains in the domains list are no longer clickable/editable or
deletable — they carry a 'via uplink' badge pointing at the Satellites
panel.
2026-10-01 06:15:48 +00:00
LeoVasanko 3a6e2bb370 CLI: paskia satellite <wss-url> — one-step satellite-only setup
The same register_uplink path as the admin UI, runnable without any web
interface (a satellite may be a purely local instance serving backend
apps on one server): the handshake runs immediately, the link credential
and pushed domains are stored, rejections are printed. With no database
yet, one is created first (localhost domain + admin bootstrap) — the
command is the whole setup; 'paskia' then serves the linked domains.
2026-10-01 06:09:14 +00:00
LeoVasanko 0f38a8b096 Admin API for satellite links and uplinks
/satellites (upstream role): mint registration offers (RAM-only initial
keys shown once as a wss link URL), list links with live state and the
satellite's domain rejections, adjust scope (pushed live), revoke (drops
connections; the satellite unlinks on the policy close).

/uplinks (satellite role): register from a pasted link URL with the full
handshake running synchronously in the request (immediate feedback), list
uplinks with served domains and replica state, unlink.

Domain writes no longer accept a remote object: remote domains are
uplink-managed. Editing a domain pushes its new configuration to the
links serving it; deleting one shrinks their scopes.
2026-10-01 06:07:27 +00:00
LeoVasanko d0c7949052 Satellite links: online registration over a 3-way handshake
Replace pre-minted sync tokens with links created by an online
registration dialogue. The auth host mints a RAM-only, short-lived,
single-use registration offer shown as a wss://<initial-key>@<host> URL
(a scheme browsers refuse to open — the URL is only for a satellite to
connect to). The existing sync WebSocket endpoint multiplexes both
flows on the first frame:

  satellite -> upstream  hello    {link: null, key: <initial key>}
  upstream  -> satellite  welcome  {link, key: <server part>, domains}
  satellite -> upstream  ready    {key: <client part>, rejected}

The permanent credential is created during the handshake from parts both
sides contribute; both sides then store the link in their databases
(SatelliteLink / Uplink, with a key: bytes field). Placeholder
cryptography: parts are exchanged as-is, the final version derives the
channel key from the handshake. Established links run the same dialogue
with hello {link, key} as proof; JSON frames follow as before (to be
transparently encrypted in the final version).

Scope is no longer bound to the credential: a link's rp-id set is
adjustable on the upstream at any time and pushed live as a domains
frame — register once, adjust scope later. The upstream is authoritative
for the pushed domains (rp-id, name, origins verbatim); the satellite
validates them against its local config (conflicting rp-ids and origin
collisions are rejected and reported in the ready/ack frames), persists
the accepted set, and rebuilds its registry.

Satellite side: one replica connection per uplink (not per domain),
keyed by link; dispatch resolves host -> domain -> uplink. A policy
close (1008) at the handshake means the link was revoked — the satellite
unlinks its side. Registration is one function (register_uplink) shared
by the admin UI and the CLI.
2026-10-01 06:04:58 +00:00
LeoVasanko 2209e2395c Satellite v2: upstream-minted scoped sync tokens, per-session contexts
Registration simplified to a single sync URL: minted in the upstream's
admin UI (Satellite access section of the domain dialog), carrying the
token as userinfo — https://<token>@<auth-host>. Tokens live hashed in
the upstream database, scoped to one rp-id, revocable (drops live
connections). PASKIA_SYNC_TOKENS env is gone.

Data minimization: the sync feed now carries only the scope's sessions
and credentials; the user graph never leaves the upstream. Each session
upsert embeds its master-computed ApiSessionContext (identity display
data + effective permissions for the session's host); user-graph
mutations eagerly re-push affected sessions with fresh contexts, so
revocations land within ~1 RTT. /check and /user-info are proxied to
the upstream (they need the user graph).

Satellite side: replicas keyed by rp-id (no more shared-replica config
conflicts — each remote domain has its own token), the replica refuses
to go live on a ready-message scope mismatch, write-backs are confined
to the token's scope server-side.

Admin dialog: one sync-URL paste field (token split from userinfo,
write-only), uplink verification required to save (reachability, rp-id
match, no nesting, auth host present); a verified uplink takes over the
domain's origins (**.<rp-id> + upstream auth host) and auth host. New
domains adopt the upstream's rp-id and name.
2026-10-01 05:40:32 +00:00
LeoVasanko da0b3e0e8b Proxy to another Paskia (#5)
Geographically separate servers may have notable latency between them, making forward/validate request roundtrip unnecessarily slow. This adds a proxy support, where the local server can keep a local copy of a remote domain, while the auth host remains authoritative, but requests can be answered in less than 1ms from local copy.

Reviewed-on: https://git.zi.fi/LeoVasanko/paskia/pulls/5
v2.1.1
2026-09-29 03:49:23 +00:00
LeoVasanko b5733657f9 Fastapi-vue-setup 1.7.2 logging fixes. 2026-09-18 19:07:33 +00:00
LeoVasanko 4de164c457 Use fastapi-vue RuntimeConfig passing, simplifying code. 2026-09-18 19:00:13 +00:00
LeoVasanko 3e4f77ba93 Update fastapi-vue-setup 1.6.1 with major changes. 2026-09-18 03:22:54 +00:00
LeoVasanko 66c2a9bf07 release.py: print the publish command unindented, on its own line 2026-09-18 01:35:09 +00:00
LeoVasanko c4360df110 Release 2.1.0 v2.1.0 2026-09-18 01:32:45 +00:00
LeoVasanko c676665795 Add release script with version bump, tagging and rollback 2026-09-18 01:32:09 +00:00
LeoVasanko 55dd43661c Rewritten README. 2026-09-18 01:11:45 +00:00
LeoVasanko 753ce868c6 Examples: simplify profile demo to print the result code 2026-09-18 00:27:21 +00:00
LeoVasanko 3e0152e688 Profile iframe handles no-session internally with in-place login
The restricted entry validates the session before rendering anything
(no load-time flash): 401/403 switches to the existing login component
in place of the profile, success renders the panel fully populated via
props, other failures show a minimal card with Back only. The dialog
drops the standalone page's heading and help text.
2026-09-18 00:27:21 +00:00
LeoVasanko c5efa03908 profile(): resolve 'login' when login flow completes inside the dialog
The overlay now tracks which dialog kind is open: auth-success from a
profile dialog resolves 'login', auth-back only rejects
AuthCancelledError for auth dialogs.
2026-09-18 00:27:21 +00:00
LeoVasanko 0ca4e07e23 Dev server: serve paskia-js module, rework examples page
The Vite dev server now maps /paskia-js/ to the local paskia-js build
(the examples page's module import has 404'd since it was introduced,
leaving all buttons dead). Navigation actions on the examples page are
now plain same-window links so the flows' back navigation returns to
the page; added a Profile Summary button exercising profile().
2026-09-17 19:17:00 +00:00
LeoVasanko baa993e586 Theme precedence: URL param, then localStorage, then browser default
The restricted page's early script now honors the URL theme parameter
before the cached profile theme, so a fresh server-provided override
wins and the host color scheme applies without a flash. After session
load, an empty profile theme clears the cache but keeps the URL
parameter in effect instead of reverting to the system default.
2026-09-17 19:17:00 +00:00
LeoVasanko 42240dd2c7 Unify host profile view as framed dialog, add profile iframe mode
HostProfileView now renders the same centered frame card as the login
flows, whether shown full-page at /auth/ (host mode) or inside the new
#mode=profile restricted iframe. The component self-fetches its data
when the parent does not provide it, and emits back/logout so each
context reacts appropriately: the full page reloads, the iframe posts
auth-back / auth-logout to the host.
2026-09-17 19:17:00 +00:00
LeoVasanko 2ec709905e paskia-js: profile() dialog, auth-logout message, host color-scheme adoption
New profile() function opens the minimal profile in a compact dialog
iframe and always resolves ('logout' | 'back'), keeping the auth flow's
resolve/reject contract separate and unchanged. The overlay now injects
the host page's computed color-scheme into the iframe URL theme param
when the server has not provided one.
2026-09-17 19:17:00 +00:00
LeoVasanko 6eb862278f Add tests for startup box sign-in summary wildcard pruning 2026-09-10 19:57:37 +00:00
LeoVasanko dbd697772a Updated systemd unit in README for better messages. 2026-09-10 01:55:44 +00:00
LeoVasanko 17abcc48c0 Replace ua-parser wrapper with uarite uaparse v2.0.2 2026-09-09 19:22:59 +00:00
LeoVasanko 97ce10dd6f Improved formatting of origin configuration in startup box. 2026-09-09 19:10:01 +00:00
LeoVasanko 3a7ba09ddd Fix legacy conversion dropping 'empty origins = allow all' when an auth host was set
The **.{rp-id} wildcard was only added when the resulting origins dict
was empty, so a legacy database with a dedicated auth host but no
configured origins ended up allowing only the auth host.
2026-09-09 17:35:04 +00:00
LeoVasanko 0da04ac3e9 Restore --save option to persist CLI setting --listen as the default v2.0.1 2026-09-09 17:25:51 +00:00
LeoVasanko ae1928241e Migrate command: merge legacy and current databases into existing paskia.kantadb
- paskia migrate accepts an rp-id, a legacy *.paskiadb path, or a
  current-format *.kantadb path; with an existing target database the
  incoming data is merged (uuid-keyed records make conflicts a non-issue,
  domains merge per rp-id with a union of origins)
- Migration transactions are labeled migrate:cli:{rp-id} (slash-joined
  for multi-domain sources) instead of 'bootstrap'
2026-09-09 17:23:27 +00:00
LeoVasanko baa7e47187 Compact cleaner startup box design. v2.0.0 2026-09-08 03:35:15 +00:00
LeoVasanko 7d9b90fd6c Split AdminDialogs.vue into per-dialog components
Break the monolithic admin dialog component into a thin dispatcher plus
one component per dialog type under admin/dialogs/, with a shared
AdminDialog frame (Modal wrapper, title, error and Cancel/Save actions).
No functional change. Also drop two unused input refs (nameInput,
displayNameInput).
2026-09-08 02:36:01 +00:00
LeoVasanko 4152052c90 Startup box: serve-only, per-domain clickable URLs, compact sign-in summary
- Print the box only when serving; 'paskia init' output is the reset
  link, which already carries the full auth site URL
- Domain row is always 'Domain:'; rows beneath it are unlabeled,
  belonging to the domain by position
- Multi-domain: each domain's auth site printed as a full clickable URL
  (auth host root when marked, else <site>/auth/)
- In-domain sign-in sites collapsed to a one-line summary
  ('example.com and all subdomains, +N sites'); related origins are few
  and surprising, so always listed in full
2026-09-08 01:42:28 +00:00
LeoVasanko 84985501f5 MultiSite: one instance serves authentication across many domains (#4)
- Serve multiple domains (RP IDs) from one instance: host-based dispatch,
  per-domain credentials and sessions, domains managed at runtime in the
  admin UI — previously one RP per instance
- Cross-domain sign-in via Related Origin Requests: per-domain related-origins
  list with a served .well-known/webauthn document
- Explicit per-domain origin lists with shell-glob wildcards (**. for apex +
  any subdomain depth, *. for one level), editable in the admin UI with
  validation and self-lockout guards
- Per-domain auth hosts: the account/admin UI can live on a different host
  per domain, no longer confined to subdomains of a single RP
- CLI: 'paskia init <rp-id [rp-name]' initializes or adds a domain to an
  existing database; 'paskia migrate' converts legacy databases

BREAKING CHANGES (v2.0):
- Database schema: config is now per-domain and credentials/sessions carry
  an rp_id — existing databases must be converted with 'paskia migrate'
- Origins are now explicit: main implicitly allowed every subdomain of the
  RP; configure '**.' origins to reproduce that behavior
- CLI: the flat '--rp-id/--rp-name/--origin/--auth/--save' flags are
  replaced by the 'init' and 'migrate' subcommandsReviewed-on: https://git.zi.fi/LeoVasanko/paskia/pulls/4
2026-09-07 22:14:42 +00:00
LeoVasanko 383c9f472e Add public access mode (public=1) to forward auth
/auth/api/forward?public=1 passes requests through with a Remote-Public
header (anonymous/forbidden/authenticated) instead of 401/403, so routes
can allow anonymous visitors while still identifying logged-in users.
Reauth (max_age) still requires the auth flow. Documented in Headers.md,
api/forward.md, Integration.md and all proxy guides.
2026-09-05 16:06:32 +00:00
LeoVasanko 8c2809a879 Update fastapi-vue-setup, make use of its access logging facility. 2026-09-05 14:36:25 +00:00
LeoVasanko 3912b5473e Fix Caddyfile indentation 2026-08-15 05:54:28 +00:00
LeoVasanko 223429d51c Add renew=0 query arg on validate, useful when only a permission check is required. v1.5.6 2026-08-11 01:46:56 +00:00
LeoVasanko 2456730f70 Check max-age only after checking permissions: if neither is passing, we want a 403 error; simply authenticating again won't fix it so don't bother reauth flow. After forbidden flow e.g. account change we are already good with max-age too. v1.5.5 2026-08-11 00:56:01 +00:00
LeoVasanko f74bf3ebe6 Require Python 3.14, ruff formatting for simpler typing. 2026-08-11 00:46:45 +00:00
LeoVasanko 79074dd4f1 OR semantics in perm query arg, strict parsing, segment-aware wildcards
perm=a|b+c now means (a or b) and c; repeated perm args remain ANDed.
Out-of-spec values (empty alternatives, chars outside the scope charset,
stray %2B) are rejected with 400 instead of being silently misparsed;
extra spaces between groups are tolerated. Forward endpoint 400/500
details name /auth/api/forward as origin without echoing query args.
Wildcards are now filename-like: * stays within a :- or /-separated
segment, ** spans segments, partial segments allowed. Slash added to
allowed scope characters for path-based permissions.
2026-08-11 00:44:58 +00:00
LeoVasanko 00ef0ae2e7 Update API and proxy docs 2026-08-10 21:35:46 +00:00
LeoVasanko 6f5287e070 Avoid clearing session.user_agent if a validation request lack this header. Backend-initiated session validations may not have the data. 2026-08-10 14:09:42 +00:00
LeoVasanko 051e1bbb41 Replace paskia.db.logging with kanta's built-in logging (kanta 0.7.0)
The vendored db/logging module duplicated what kanta now provides:
diff formatting, UUID-to-label resolution via logfmt callbacks, unsafe
character filtering and value truncation. Censoring of oidc.key material
moves into the format_log_uuid logfmt callback in db.lifecycle, taking
care to hide only the value, not the 'key' path component itself.
v1.5.4
2026-08-09 23:25:16 +00:00
LeoVasanko 4b156b712c Proper handling of auth site runtime change done via web interface, making the change immediately effective. Kept in origins list that is still also visible on the same dialog, where it can be removed if needed. 2026-08-09 23:00:22 +00:00
LeoVasanko df8a7c0026 Cleanup of admin user panel where incorrect toast messages were issued after changes. 2026-08-09 21:45:23 +00:00
LeoVasanko 9b28250391 Upgrade to kanta 0.4.0:
- Make use of its new features and cleanup our interfacing and init/shutdown processes and migrations
- Clean up circular deps, simplify app init
- Add specific pytest for CLI main to cover the changes
v1.5.3
2026-06-13 21:59:18 +00:00
LeoVasanko b9aec6bb58 Remove built-in database, replace with kanta package. No disk format changes. v1.5.2 2026-06-12 19:39:30 +00:00
LeoVasanko c79cb497ee Fix profile image path on OIDC. v1.5.1 2026-05-22 02:45:12 +00:00