Split project context into .claude/context/ (architecture, server, styling, standards) instead of one large file, referenced from a minimal root CLAUDE.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
64 lines
3.3 KiB
Markdown
64 lines
3.3 KiB
Markdown
# Server integration
|
|
|
|
## Auth: Login Flow v2
|
|
|
|
The app **never** collects a Nextcloud password directly. It implements
|
|
[Login Flow v2](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2)
|
|
via [`LoginFlowService`](../../lib/services/login_flow_service.dart):
|
|
|
|
1. `LoginFlowService.initiate(serverUrl)` POSTs to
|
|
`{server}/index.php/login/v2`, gets back a browser login URL + a poll
|
|
endpoint/token.
|
|
2. The app opens the login URL in the system browser (`url_launcher`); the
|
|
user authenticates and authorizes there.
|
|
3. `ServerProvider` polls `LoginFlowService.poll(pollEndpoint, token)` every
|
|
2 seconds (`Timer.periodic`, see `_pollTimer`/`_pollTimeoutTimer` in
|
|
`server_provider.dart`) until it gets a 200 with `server`/`loginName`/
|
|
`appPassword`, a non-404 error, or a 10-minute timeout.
|
|
4. The returned **app password** (scoped, revocable) is what gets stored and
|
|
used for every subsequent request — real user passwords are never in
|
|
memory or on disk.
|
|
|
|
`LoginFlowStatus` (`idle` → `initiating` → `awaitingBrowser` → `error`)
|
|
drives `LoginView`'s UI; see `LoginFlowService`'s doc comment for the full
|
|
flow rationale before changing it.
|
|
|
|
## Talking to the server
|
|
|
|
[`NextcloudService`](../../lib/services/nextcloud_service.dart) is the
|
|
client for an authenticated session — constructed with `serverUrl` +
|
|
`username` + the app password, one instance per login (held as
|
|
`ServerProvider.service`, recreated on login/logout).
|
|
|
|
- **Files**: WebDAV (`PROPFIND`/`MKCOL`/`DELETE`/`MOVE` etc. against
|
|
`/remote.php/dav/files/{username}/...`) via raw `http`/`dio` calls with a
|
|
hand-rolled XML request body and `package:xml` for parsing responses —
|
|
there is no WebDAV client dependency. `_parseDavDate`/`_davPath` in this
|
|
file exist because WebDAV responses use RFC 1123 dates and either bare
|
|
paths or full URLs for `href`; reuse them rather than re-deriving.
|
|
- **Everything else** (shares, activity, trash, favorites, quota, user info)
|
|
goes through Nextcloud's OCS APIs (`/ocs/v2.php/...`), JSON in, with the
|
|
`OCS-APIRequest: true` header required on every OCS call.
|
|
- Auth header is HTTP Basic (`username:appPassword`, base64), built in
|
|
`_headers`/exposed as `authHeaders` for widgets that need to hit URLs
|
|
directly (e.g. `Image.network(url, headers: service.authHeaders)` for
|
|
thumbnails/previews).
|
|
- Downloads stream through `Dio` (`downloadToFile`) for progress callbacks;
|
|
small in-app previews (text/PDF) use `fetchBytes` via `package:http`.
|
|
|
|
## Session persistence
|
|
|
|
- **Credentials** (`server`, `loginName`, `appPassword`) live in
|
|
`flutter_secure_storage` — OS keychain/keystore-backed, never
|
|
`shared_preferences`.
|
|
- **UI/app preferences** (theme mode, seed color, dynamic-color toggle,
|
|
bottom-bar opacity/blur, grid vs. list, sort field, hidden-files toggle,
|
|
etc.) live in `shared_preferences` — see the `_pref*` key constants at the
|
|
top of `server_provider.dart`.
|
|
- On startup, `ServerProvider._restoreSession()` reads the secure-storage
|
|
keys and, if all three are present, rebuilds a `NextcloudService` without
|
|
re-hitting the login flow (`_applyCredentials(..., persist: false)`).
|
|
`isRestoringSession` gates the splash screen until this resolves — see
|
|
`standards.md` for why widget tests must mock both storage channels
|
|
rather than relying on this async path throwing naturally.
|