feat: add Linux OAuth adapters

Add a bounded libcurl transport with verified TLS, no redirects or retries, sanitized failures, binary-safe requests, and resolved-address enforcement for cleartext loopback traffic.

Add shell-free xdg-open launching and a one-shot IPv4 loopback callback receiver with strict HTTP/query parsing, deadlines, fixed callback path, and RAII cleanup. Token parsing, Secret Service, and Graph remain follow-up phases.
This commit is contained in:
2026-07-18 11:33:06 +01:00
parent dd6e02cb55
commit 295acbc125
12 changed files with 2095 additions and 28 deletions

View File

@@ -183,10 +183,40 @@ consent policy remains authoritative and may require administrator approval or
block consent. A desktop binary distributed as open source is not a confidential
client and must not embed or depend on a client secret.
This boundary does not yet contain a concrete HTTP transport, system-browser or
loopback-listener adapter, token JSON parsing, a Secret Service backend, Graph
requests, or usable account setup. Those concrete desktop, HTTP, and secret
storage adapters are the next slice. Read-only Microsoft Graph calendar
The generic boundary deliberately contains no concrete networking or desktop
code. The Linux adapters below supply the transport, browser launch, and
loopback listener. Token JSON parsing, Secret Service storage, Graph requests,
and usable account setup remain later slices. Read-only Microsoft Graph calendar
discovery and delta synchronization follows; delta links remain opaque and are
persisted only through the calendar/window transaction described above. Remote
writes and conflict resolution come later.
### Concrete Linux adapters
The concrete adapter phase provides a libcurl transport that keeps certificate
and host verification enabled, bounds response bodies and
connection/operation timeouts, and disables automatic redirects. It implements
one request attempt only: neither the adapter nor the generic transport retries.
Cleartext HTTP is accepted solely for explicit loopback tests; provider traffic
uses HTTPS.
The browser adapter launches `xdg-open` directly without a shell, so an
authorization URL is never interpreted as shell syntax. The callback receiver
binds IPv4 `127.0.0.1` on a dynamically assigned port, advertises that port with
the `localhost` host and fixed `/nocal/oauth/callback` path, and handles one
bounded callback before closing. Bounded header/input sizes and a deadline keep
a local peer from holding the authorization process indefinitely.
The Entra registration must configure Nocal as a Mobile and desktop public
client with `http://localhost/nocal/oauth/callback`. Microsoft ignores the port
when matching a `localhost` redirect, which permits the runtime-selected port.
Using an HTTP redirect with the literal `127.0.0.1` host instead requires editing
the application manifest, and IPv6 loopback redirect addresses are currently
unsupported. The advertised `localhost` URL therefore remains distinct from the
IPv4-only listener binding.
Concrete token JSON parsing, Secret Service storage, Graph requests, account UI,
and usable account setup are still absent. libsecret's synchronous calls may
block; the next adapter isolates them behind an off-render-thread boundary
rather than calling them from the TUI loop. Read-only Graph discovery and delta
follow token parsing and secret persistence.

View File

@@ -141,13 +141,25 @@ desktop binary.
The provider-neutral HTTP seam accepts an injected transport and can therefore
be exercised with deterministic fakes. It intentionally owns no generic retry
policy; provider operations must decide whether a retry is safe. This phase does
not make Office 365 usable yet. It includes no concrete HTTP transport, real
browser/loopback adapter, token JSON parser, Secret Service backend, Graph call,
or usable account setup. The next product slice supplies concrete desktop,
network, and secret-storage adapters, followed by read-only Graph calendar
discovery and delta synchronization. Remote writes remain gated on the
durability and conflict paths being exercised end to end.
policy; provider operations must decide whether a retry is safe. The concrete
Linux adapter uses libcurl while preserving
TLS certificate and host verification, bounds responses and timeouts, and
follows neither redirects nor retries automatically. Cleartext HTTP exists only
for loopback tests. Browser opening invokes `xdg-open` without a shell.
The receiver listens only on `127.0.0.1`, chooses a dynamic port, advertises a
`localhost` URL with the fixed `/nocal/oauth/callback` path, and processes one
bounded callback. The Microsoft registration is a Mobile and desktop public
client with `http://localhost/nocal/oauth/callback`. Microsoft ignores the port
when matching localhost redirects. Literal HTTP `127.0.0.1` registration needs
a manifest edit, and IPv6 loopback redirects are currently unsupported.
These adapters still do not make Office 365 usable. They include no token JSON
parser, Secret Service backend, Graph call, or account UI. libsecret exposes
synchronous operations that may block, so the next phase isolates its adapter
off the render thread. Token parsing and secure persistence come before
read-only Graph calendar discovery and delta synchronization. Remote writes
remain gated on the durability and conflict paths being exercised end to end.
## Local file interchange

View File

@@ -46,9 +46,21 @@ Completed HTTP/OAuth boundary:
block user consent
- No client secret in the open-source desktop binary
Completed concrete Linux adapters:
- libcurl transport with verified TLS, bounded responses/timeouts, no automatic
redirects/retries, and cleartext HTTP only for loopback tests
- Shell-free `xdg-open` launcher and one-shot `127.0.0.1` callback receiver
advertising `http://localhost:<dynamic-port>/nocal/oauth/callback`
- Mobile/Desktop public-client registration at
`http://localhost/nocal/oauth/callback`; Microsoft ignores localhost ports,
while literal HTTP `127.0.0.1` needs a manifest edit and IPv6 loopback is not
supported
Next provider-boundary slices:
- Concrete HTTP, browser/loopback, token JSON, and Secret Service adapters
- Token JSON parsing and a Secret Service adapter isolated off the render thread
because synchronous libsecret calls may block
- OAuth secrets in Secret Service, never SQLite
- Generic `SyncProvider` contract and observable sync status
@@ -65,12 +77,11 @@ Next provider-boundary slices:
ETag-aware remote writes after the conflict boundary is proven
- Account/calendar management UI and per-calendar ANSI identity
The cache and generic HTTP/OAuth boundaries are prerequisites, not usable
Microsoft 365 integration. The boundary phase has no concrete HTTP transport,
real browser/loopback adapter, token parser, Secret Service backend, Graph call,
or account setup. The immediate sequence is concrete desktop, HTTP, and Secret
Service adapters, then read-only Microsoft Graph discovery and delta
synchronization.
The cache, generic HTTP/OAuth boundary, and concrete Linux adapters are
prerequisites, not usable Microsoft 365 integration. There is still no token
parser, Secret Service backend, Graph call, or account UI. The immediate sequence is token parsing plus an
off-render-thread Secret Service adapter, then read-only Microsoft Graph
discovery and delta synchronization.
## 1.0 — distribution quality