Files
noo/.claude/context/server.md
T
ayushyaandClaude Sonnet 5 0c877e619c Add CLAUDE.md with topic-specific context docs
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>
2026-09-16 00:25:53 -04:00

3.3 KiB

Server integration

Auth: Login Flow v2

The app never collects a Nextcloud password directly. It implements Login Flow v2 via LoginFlowService:

  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 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.