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>
3.3 KiB
3.3 KiB
Server integration
Auth: Login Flow v2
The app never collects a Nextcloud password directly. It implements
Login Flow v2
via LoginFlowService:
LoginFlowService.initiate(serverUrl)POSTs to{server}/index.php/login/v2, gets back a browser login URL + a poll endpoint/token.- The app opens the login URL in the system browser (
url_launcher); the user authenticates and authorizes there. ServerProviderpollsLoginFlowService.poll(pollEndpoint, token)every 2 seconds (Timer.periodic, see_pollTimer/_pollTimeoutTimerinserver_provider.dart) until it gets a 200 withserver/loginName/appPassword, a non-404 error, or a 10-minute timeout.- 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 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/MOVEetc. against/remote.php/dav/files/{username}/...) via rawhttp/diocalls with a hand-rolled XML request body andpackage:xmlfor parsing responses — there is no WebDAV client dependency._parseDavDate/_davPathin this file exist because WebDAV responses use RFC 1123 dates and either bare paths or full URLs forhref; 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 theOCS-APIRequest: trueheader required on every OCS call. - Auth header is HTTP Basic (
username:appPassword, base64), built in_headers/exposed asauthHeadersfor 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) usefetchBytesviapackage:http.
Session persistence
- Credentials (
server,loginName,appPassword) live influtter_secure_storage— OS keychain/keystore-backed, nevershared_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 ofserver_provider.dart. - On startup,
ServerProvider._restoreSession()reads the secure-storage keys and, if all three are present, rebuilds aNextcloudServicewithout re-hitting the login flow (_applyCredentials(..., persist: false)).isRestoringSessiongates the splash screen until this resolves — seestandards.mdfor why widget tests must mock both storage channels rather than relying on this async path throwing naturally.