Drop the redundant force-ssl scope, and reject the playlistItems "fix"

force-ssl is gone from the requested scope. Its grant ("see, edit, and
permanently delete your videos, ratings, comments and captions") is a strict
subset of auth/youtube's ("Manage your YouTube account"), and Google's own
liveBroadcasts.insert reference lists auth/youtube as an accepted ALTERNATIVE
to force-ssl, so every endpoint this app calls — channels, liveBroadcasts,
liveStreams, liveChat, playlistItems — is covered by the one we keep. Two
consent lines became one, and one fewer scope to justify. Removing a scope
cannot narrow capability when the scope being kept is the broader of the two,
and there is no forced re-consent: a refresh token keeps the scopes it was
originally granted, so existing sessions are untouched.

The playlistItems change is NOT being made, and the map entry recommending it
was wrong. It came out of the verification hunt on the theory that public data
should not ride on user credentials. Walking the code kills it: ChannelStats-
Service is built with one HttpClient, and channels?mine=true on the same
service genuinely needs the session. Splitting it means a second unauthenticated
client PLUS a Google API key, since every YouTube Data API request needs a
credential and the call 403s without one. So the fix would ship a new
extractable, quota-abusable credential inside the binary to replace a token the
app already holds, buying zero verification-surface reduction (the data is
public, no scope involved) and zero correctness. Strictly worse. Recorded as a
do-not-do so it is not "helpfully" reintroduced later.

Test: ExactlyOneScopeIsRequested_AndItIsTheBroadOne asserts on the const's
parsed VALUE rather than scanning the file — a file-wide DoesNotContain
fails on the doc comment that names force-ssl to explain its removal, which is
the same trap I hit and documented earlier today in the locator test. Suite
380/380, 0 warnings.
This commit is contained in:
2026-09-28 09:45:35 -07:00
parent 4fcf76289c
commit b3edc2e42e
4 changed files with 110 additions and 13 deletions
+21 -12
View File
@@ -59,18 +59,27 @@ re-consent. Parking verification does not fix that; it accepts it for the solo-u
real work the moment a *second real user* needs to sign in, or at Store submission (TASK 48). The
sensitive-vs-restricted classification was never read and is moot until then.
**These two survive the tabling** — neither needs verification to land, and both shrink the surface
if it ever comes back:
- `Services/YouTubeAuthService.cs:18` requests `auth/youtube` **+ `auth/youtube.force-ssl`**.
`force-ssl` is **redundant** — Google's liveChat docs list `auth/youtube` as an acceptable
alternative, and it is the broader of the two ("Manage your YouTube account" ⊃ "see/edit/delete
your videos, ratings, comments, captions"). Drop it: one fewer scope to justify, one fewer consent line.
- `Services/ChannelStatsService.cs:104,107` — `playlistItems.list` on the creator's own uploads
playlist (`UU…`) is **public data needing no OAuth scope at all** (API key suffices); it currently
rides on the user's session token. Does not change the requested scope (`auth/youtube` is forced by
`liveBroadcasts.*`/`liveStreams.list` regardless) but that fetch should not need user credentials.
The `channels?mine=true` call on the same service **does** need the session — do not "fix" it.
- `auth/youtube` is **unavoidable**: no narrower scope covers live broadcast create/transition.
**One of these two survives the tabling, and the other was a bad idea (corrected 2026-09-28):**
- ✅ **DONE — `Services/YouTubeAuthService.cs` no longer requests `auth/youtube.force-ssl`.**
It is **redundant**: its grant is *"see, edit, and permanently delete your YouTube videos,
ratings, comments and captions"*, a strict subset of `auth/youtube`'s *"Manage your YouTube
account"*, which is the scope we keep. Google's own `liveBroadcasts.insert` reference lists
`auth/youtube` as an accepted **alternative** to `force-ssl`, so every endpoint this app calls
(`channels`, `liveBroadcasts`, `liveStreams`, `liveChat`, `playlistItems`) is covered.
Two consent lines became one. **No forced re-consent** — a refresh token keeps the scopes it
was originally granted, so existing sessions are untouched; the reduced set applies at the
next fresh sign-in. Removing a scope cannot narrow capability when the one kept is broader.
- ❌ **NOT doing this, and the earlier note recommending it was wrong: moving
`playlistItems.list` off the user's session token.** It was written during the verification
hunt on the theory that "public data should not ride on user credentials". Walking the code
kills it: `ChannelStatsService` is built as `new ChannelStatsService(_youtubeAuth)` with **one**
`HttpClient`, and `channels?mine=true` (same service) **genuinely needs the session**. To split
it you would need a second unauthenticated client **plus a Google API key**, because every
YouTube Data API request needs a credential — without a key the call 403s. So the "fix" means
**shipping a new extractable credential in the binary, subject to quota abuse, to replace a
token the app already holds.** It buys **zero** verification-surface reduction (the data is
public; no scope is involved) and zero correctness. Strictly worse. ⛔ Do not "fix" this later
without a new ruling.
## ✅ DECIDED 2026-09-27 — pricing: `$10/mo` subscription, or `$400` lifetime, **no annual**