Ubisam-MobileGit/docs/decisions/004-favorites-write-user-scope.md

3.6 KiB

ADR-004: Favorites use Gitea star via a new write:user scope

Status: suspended by TASK-019 after target-server authorization failure. Production defaults have returned to read-only scopes; this decision must not be used as release authorization.

Status

Accepted — 2026-08-26

Context

The human product owner asked for a Favorites tab. Two designs were considered: a device-local favorite flag (no server write, no new scope) or Gitea's native star feature (GET/PUT/DELETE /user/starred/{owner}/{repo}), which syncs with the Gitea web UI and across devices but requires a write call. ADR-002 explicitly required product approval, an ADR revision, and explicit reauthorization UX before any new scope was added — this ADR is that revision.

The human product owner chose Gitea-native star after being told explicitly that it requires the app's first write endpoint, that no NAS/Gitea server restart or configuration is needed (starring is a standard, already-live Gitea REST endpoint), and that the only consequence is a fresh login to pick up the new scope.

Decision

  • Add write:user to the app's requested OAuth scopes. Per Gitea's OAuth2 provider documentation, write:user grants read/write access to user-scoped operations including repository subscriptions, which covers starring.
  • OAuthClientConfig's scope guard is relaxed from "every scope must be read-only" to "every scope must be read-only or appear in an explicit, hard-coded EXPLICITLY_APPROVED_WRITE_SCOPES allow-list." Today that list contains exactly write:user. Adding any other write scope later requires the same kind of explicit human decision and ADR, not just adding an enum value.
  • Star/unstar are the only write calls added. No create/edit/merge/close/comment/delete/admin endpoint is introduced by this or any related task (TASK-018).
  • Star/unstar require no confirmation dialog: both are immediately reversible (tap again to undo) and non-destructive (no data loss, no effect on other users), matching Gitea's and GitHub's own web UI convention and AGENTS.md's confirmation requirement, which is scoped to destructive operations.

Alternatives considered

  • Device-local-only favorites: rejected by the human product owner in favor of cross-device/web parity, despite being the option with zero new scope and zero server interaction.
  • A generic "allow any write scope" guard relaxation: rejected — kept the allow-list explicit and enumerable so a future write scope cannot be added without deliberately touching this decision again.

Consequences

  • Existing signed-in users must log in again once before they can use Favorites; their existing token only carries the old read-only scopes.
  • If this Gitea instance's OAuth application registration restricts requestable scopes independently of what the client requests (a server-admin setting, not something requiring a restart), the first login attempt with the new scope will surface as an explicit authorization error rather than silently degrading; this is a known, accepted risk to verify on-device rather than something assumed away.
  • Favorite state is not cached locally; the starred set is always read fresh from Gitea, consistent with the product's existing no-offline-cache posture.

Evidence