Upgrade Google Sheets OAuth scope to spreadsheets (write) #436

Open
opened 2026-04-10 23:30:14 +00:00 by forgejo_admin · 0 comments
Contributor

Type

Feature

Lineage

Standalone — spawned from the westside-sheet-sync project scaffold on 2026-04-10. Prerequisite for every other ticket in the sheet-sync series; the sync cannot append rows to the sheet with a read-only token.

Repo

forgejo_admin/basketball-api

User Story

As ops
I want the westsidebasktball@gmail.com OAuth token to include the spreadsheets write scope
So that the sheet_sync service can append new paid jersey orders to Marcus's Google Sheet without a second token or service account

Ties to story:sheet-sync — see story-westside-jersey-sheet-sync.

Context

Tonight (2026-04-10) we did a one-shot OAuth consent to get the Google Sheets spreadsheets.readonly scope on the westsidebasktball@gmail.com account. The resulting refresh token lives at ~/secrets/google-oauth/sheets-westsidebasketball.json. That token can read the "Westside" jersey order sheet but cannot write to it. The sheet_sync service (separate ticket) needs write access to call the Sheets API values:append endpoint.

Scopes are locked at consent time — you cannot "upgrade" an existing token to cover a new scope. The only path is a fresh consent flow with the new scope requested. One-time browser interaction, then the new refresh token is good indefinitely.

The existing consent script ~/westside-sheets/auth_sheets_remote.py is already parameterized for this — change one constant from spreadsheets.readonly to spreadsheets and re-run. The new token lands in a new file so the readonly token continues to work for anything that uses it.

File Targets

Files to create:

  • ~/westside-sheets/auth_sheets_remote_write.py — copy of auth_sheets_remote.py with SCOPE = "https://www.googleapis.com/auth/spreadsheets" and TOKEN_PATH = Path.home() / "secrets/google-oauth/sheets-westsidebasketball-write.json". Alternatively, parameterize the existing script to take a --scope and --out flag and drop the duplicate file.

Files NOT to touch:

  • auth_sheets_remote.py — preserve the readonly consent flow exactly as-is for anything that uses it.
  • ~/secrets/google-oauth/sheets-westsidebasketball.json — keep the readonly token intact.

Acceptance Criteria

  • When the new consent script runs, then it opens a browser URL for westsidebasktball@gmail.com.
  • When the user grants consent, then a token with scope https://www.googleapis.com/auth/spreadsheets is written to ~/secrets/google-oauth/sheets-westsidebasketball-write.json with mode 0600.
  • When I hit https://oauth2.googleapis.com/tokeninfo?access_token=<access> with the new token, then the response includes the spreadsheets scope (not spreadsheets.readonly).
  • When I call the Sheets API values:append endpoint with a test range and body using the new token, then the response is HTTP 200 with the appended row's range.
  • When I re-run any existing code that uses the readonly token, then it still works (the readonly token is not touched).

Test Expectations

  • Manual test: run the consent flow, grant the scope, verify the token file exists.
  • Scope verification: tokeninfo endpoint confirms scope: https://www.googleapis.com/auth/spreadsheets.
  • Append test: POST a single test row to a scratch tab or the bottom of the Westside tab, confirm HTTP 200 and verify the row appears.
  • Run command: python3 ~/westside-sheets/auth_sheets_remote_write.py start then finish <redirect_url>

Constraints

  • Do NOT overwrite the existing readonly token file.
  • Do NOT delete the readonly consent script.
  • The new token must include a refresh_token (use access_type=offline and prompt=consent like the existing flow).
  • Kubernetes secret sync is out of scope for this ticket — the secret mounting into basketball-api is a separate ticket on the sheet_sync service module.

Checklist

  • PR opened (or committed directly to ~/westside-sheets/ — this lives outside a git repo)
  • Token file created with 0600 permissions
  • Append test against the Sheets API succeeds
  • westside-sheet-sync — project this affects
  • story-westside-jersey-sheet-sync — user story
  • Blocks: sheet_sync service module ticket, CronJob deployment ticket
### Type Feature ### Lineage Standalone — spawned from the westside-sheet-sync project scaffold on 2026-04-10. Prerequisite for every other ticket in the sheet-sync series; the sync cannot append rows to the sheet with a read-only token. ### Repo `forgejo_admin/basketball-api` ### User Story As ops I want the westsidebasktball@gmail.com OAuth token to include the `spreadsheets` write scope So that the sheet_sync service can append new paid jersey orders to Marcus's Google Sheet without a second token or service account Ties to `story:sheet-sync` — see [story-westside-jersey-sheet-sync](https://docs.tail5b443a.ts.net/notes/story-westside-jersey-sheet-sync). ### Context Tonight (2026-04-10) we did a one-shot OAuth consent to get the Google Sheets `spreadsheets.readonly` scope on the westsidebasktball@gmail.com account. The resulting refresh token lives at `~/secrets/google-oauth/sheets-westsidebasketball.json`. That token can read the "Westside" jersey order sheet but cannot write to it. The sheet_sync service (separate ticket) needs write access to call the Sheets API `values:append` endpoint. Scopes are locked at consent time — you cannot "upgrade" an existing token to cover a new scope. The only path is a fresh consent flow with the new scope requested. One-time browser interaction, then the new refresh token is good indefinitely. The existing consent script `~/westside-sheets/auth_sheets_remote.py` is already parameterized for this — change one constant from `spreadsheets.readonly` to `spreadsheets` and re-run. The new token lands in a new file so the readonly token continues to work for anything that uses it. ### File Targets Files to create: - `~/westside-sheets/auth_sheets_remote_write.py` — copy of `auth_sheets_remote.py` with `SCOPE = "https://www.googleapis.com/auth/spreadsheets"` and `TOKEN_PATH = Path.home() / "secrets/google-oauth/sheets-westsidebasketball-write.json"`. Alternatively, parameterize the existing script to take a `--scope` and `--out` flag and drop the duplicate file. Files NOT to touch: - `auth_sheets_remote.py` — preserve the readonly consent flow exactly as-is for anything that uses it. - `~/secrets/google-oauth/sheets-westsidebasketball.json` — keep the readonly token intact. ### Acceptance Criteria - [ ] When the new consent script runs, then it opens a browser URL for `westsidebasktball@gmail.com`. - [ ] When the user grants consent, then a token with scope `https://www.googleapis.com/auth/spreadsheets` is written to `~/secrets/google-oauth/sheets-westsidebasketball-write.json` with mode 0600. - [ ] When I hit `https://oauth2.googleapis.com/tokeninfo?access_token=<access>` with the new token, then the response includes the `spreadsheets` scope (not `spreadsheets.readonly`). - [ ] When I call the Sheets API `values:append` endpoint with a test range and body using the new token, then the response is HTTP 200 with the appended row's range. - [ ] When I re-run any existing code that uses the readonly token, then it still works (the readonly token is not touched). ### Test Expectations - [ ] Manual test: run the consent flow, grant the scope, verify the token file exists. - [ ] Scope verification: `tokeninfo` endpoint confirms `scope: https://www.googleapis.com/auth/spreadsheets`. - [ ] Append test: POST a single test row to a scratch tab or the bottom of the Westside tab, confirm HTTP 200 and verify the row appears. - Run command: `python3 ~/westside-sheets/auth_sheets_remote_write.py start` then `finish <redirect_url>` ### Constraints - Do NOT overwrite the existing readonly token file. - Do NOT delete the readonly consent script. - The new token must include a `refresh_token` (use `access_type=offline` and `prompt=consent` like the existing flow). - Kubernetes secret sync is out of scope for this ticket — the secret mounting into basketball-api is a separate ticket on the sheet_sync service module. ### Checklist - [ ] PR opened (or committed directly to `~/westside-sheets/` — this lives outside a git repo) - [ ] Token file created with 0600 permissions - [ ] Append test against the Sheets API succeeds ### Related - `westside-sheet-sync` — project this affects - `story-westside-jersey-sheet-sync` — user story - Blocks: sheet_sync service module ticket, CronJob deployment ticket
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/basketball-api#436
No description provided.