RESEARCH: Google OAuth for Gmail/Calendar/Contacts without a licence — how superlocal does it #408

Open
opened 2026-09-29 06:54:47 +00:00 by kayg · 5 comments
Owner

Question (owner, 2026-09-29)

"doesn't gmail oauth need a license or something? if we can implement it without, that'd be great! https://github.com/R44VC0RP/superlocal see how ryan's superlocal does it? if we don't need one then of course for gmail, we should use Oauth"

Research (no product code)

  1. How superlocal (github.com/R44VC0RP/superlocal) authenticates to Gmail/Google Calendar: OAuth client type, scopes, whether it ships a shared client ID, asks users for their own, or uses another path; read its source and docs.
  2. Google's rules today for each scope we need: Gmail read/sync (https://mail.google.com/ or gmail.readonly — restricted scopes), Calendar (calendar — sensitive), Contacts (contacts — sensitive): verification, the CASA security assessment for restricted scopes (who needs it, cost, yearly renewal), the 100-user cap and warning screen for unverified apps, "testing" mode limits (7-day refresh tokens), and "internal" apps.
  3. Options for calternal: (a) a verified calternal OAuth client for calternal.cloud; (b) bring-your-own OAuth client per instance (self-hosters and the owner create their own Google Cloud project; no verification needed for personal use); (c) IMAP/SMTP with an app password for mail plus OAuth only for Calendar/Contacts (sensitive scopes: verification without CASA); (d) anything superlocal does differently. Cost, time, user experience, and legal/ToS risk for each.
  4. Recommendation for calternal.cloud and for self-hosters, with sources and access dates. Output: docs/research/google-oauth.md and a summary comment on #408. Numbers only from primary sources (Google docs, CASA provider price pages).
## Question (owner, 2026-09-29) "doesn't gmail oauth need a license or something? if we can implement it without, that'd be great! https://github.com/R44VC0RP/superlocal see how ryan's superlocal does it? if we don't need one then of course for gmail, we should use Oauth" ## Research (no product code) 1. How superlocal (github.com/R44VC0RP/superlocal) authenticates to Gmail/Google Calendar: OAuth client type, scopes, whether it ships a shared client ID, asks users for their own, or uses another path; read its source and docs. 2. Google's rules today for each scope we need: Gmail read/sync (`https://mail.google.com/` or `gmail.readonly` — restricted scopes), Calendar (`calendar` — sensitive), Contacts (`contacts` — sensitive): verification, the CASA security assessment for restricted scopes (who needs it, cost, yearly renewal), the 100-user cap and warning screen for unverified apps, "testing" mode limits (7-day refresh tokens), and "internal" apps. 3. Options for calternal: (a) a verified calternal OAuth client for calternal.cloud; (b) bring-your-own OAuth client per instance (self-hosters and the owner create their own Google Cloud project; no verification needed for personal use); (c) IMAP/SMTP with an app password for mail plus OAuth only for Calendar/Contacts (sensitive scopes: verification without CASA); (d) anything superlocal does differently. Cost, time, user experience, and legal/ToS risk for each. 4. Recommendation for calternal.cloud and for self-hosters, with sources and access dates. Output: docs/research/google-oauth.md and a summary comment on #408. Numbers only from primary sources (Google docs, CASA provider price pages).
Author
Owner

Started research on branch job/google-oauth from dev at c465150468. I am checking the upstream superlocal source and current Google OAuth policy from primary sources; I will add the research note and report findings here.

Started research on branch job/google-oauth from dev at c46515046871936ad681c4d00e8b76de69dde3b7. I am checking the upstream superlocal source and current Google OAuth policy from primary sources; I will add the research note and report findings here.
Author
Owner

Research is committed in docs/research/google-oauth.md at 1501b545.

Google’s docs show no separate OAuth licence requirement. Gmail inbox sync uses restricted scopes; a shared calternal.cloud service that stores or sends Gmail data needs restricted-scope verification and a CASA assessment at least every 12 months. Calendar and Contacts use sensitive scopes: a public OAuth client needs scope verification, but those scopes do not require CASA by themselves. Recommendation: use OAuth for Calendar and Contacts, and keep Gmail on the app-password path in DESIGN.md §§45 and 49.

Superlocal configures a Google web client per host. It separates app login from Gmail authorization, but its write-enabled Gmail scopes remain restricted. A per-Instance client does not clearly remove CASA for server-side Gmail data under Google’s current wording; the personal-use review exception and server assessment rule are not reconciled in the docs. Testing mode has a 100-test-user limit and seven-day authorization expiry; unverified production has a lifetime cap of 100 new Users.

I omitted assessor price research because the repository rules prohibit financial research in public files and issues.

Research is committed in `docs/research/google-oauth.md` at `1501b545`. Google’s docs show no separate OAuth licence requirement. Gmail inbox sync uses restricted scopes; a shared calternal.cloud service that stores or sends Gmail data needs restricted-scope verification and a CASA assessment at least every 12 months. Calendar and Contacts use sensitive scopes: a public OAuth client needs scope verification, but those scopes do not require CASA by themselves. Recommendation: use OAuth for Calendar and Contacts, and keep Gmail on the app-password path in DESIGN.md §§45 and 49. Superlocal configures a Google web client per host. It separates app login from Gmail authorization, but its write-enabled Gmail scopes remain restricted. A per-Instance client does not clearly remove CASA for server-side Gmail data under Google’s current wording; the personal-use review exception and server assessment rule are not reconciled in the docs. Testing mode has a 100-test-user limit and seven-day authorization expiry; unverified production has a lifetime cap of 100 new Users. I omitted assessor price research because the repository rules prohibit financial research in public files and issues.
Author
Owner

Completed the Google OAuth research note and committed it.

  • File: docs/research/google-oauth.md
  • Commit / head: 1501b545fcd2a57c76cd29bdfda73a3672c34833
  • Superlocal was reviewed at d3725bcb963f28544a10cd2a463f1d1017c6a6b7. Google primary sources were accessed on 2026-09-29.
  • Findings: Gmail read/sync scopes are restricted and server-side sync triggers CASA; Calendar and Contacts scopes are sensitive. The note recommends OAuth for Calendar and Contacts and keeping Gmail on the existing app-password path for calternal.cloud. It records that Google's public guidance is unclear on whether self-hosted personal use removes CASA for server-side Gmail data.
  • No product code or credentials were added. Price research is omitted because the repository rules prohibit financial research in this public repository and issue tracker.
  • Decision where DESIGN.md is silent: treat per-Instance OAuth as a bring-your-own-client option, but do not claim it removes CASA for server-side Gmail data without a written answer from Google.

Gates:

cargo fmt --check: exit 0, no output.

cargo clippy --all-targets -- -D warnings: passed. Final output:

    Checking calternal-sync v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-sync)
    Checking calternal-plugin-analytics v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/plugins/analytics)
    Checking calternal-search v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-search)
    Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-collab)
    Checking calternal-notes-core v0.1.0 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-notes-core)
    Checking calternal-plugin-notes v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/plugins/notes)
    Checking calternal-path v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-path)
    Checking calternal-fs v0.1.0 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-fs)
    Checking calternal-embed v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-embed)
    Checking calternal-dav v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-dav)
    Checking calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-tags)
    Checking calternal-plugin v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-plugin)
    Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-db)
    Checking calternal-api v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-api)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 58.22s

cargo test: exit 101. It compiled successfully, then calternal-cli/tests/output_contract.rs failed one test. The other completed suites shown before the failure reported 1, 63, and 21 passing tests. Failure output:

test connection_resets_retry_safe_read_requests ... FAILED

failures:
    connection_resets_retry_safe_read_requests

thread 'connection_resets_retry_safe_read_requests' panicked at crates/calternal-cli/tests/output_contract.rs:772:5:
stderr: [123, 34, 101, 114, 114, 111, 114, 34, 58, 123, 34, 101, 120, 105, 116, 95, 99, 111, 100, 101, 34, 58, 57, 44, 34, 109, 101, 115, 115, 97, 103, 101, 34, 58, 34, 101, 114, 114, 111, 114, 32, 115, 101, 110, 100, 105, 110, 103, 32, 114, 101, 113, 117, 101, 115, 116, 32, 102, 111, 114, 32, 117, 114, 108, 32, 40, 104, 116, 116, 112, 58, 47, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 51, 50, 57, 50, 49, 47, 97, 112, 105, 47, 118, 49, 47, 97, 117, 116, 104, 47, 109, 101, 41, 34, 125, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 49, 125, 10]

error: test failed, to rerun pass `-p calternal-cli --test output_contract`

No test expectation was changed. This job changes no API, so no adversarial endpoint round was applicable.

bun run check:

$ node scripts/check-type-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
Text sizes use shared role tokens.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/google-oauth/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test:

 Test Files  123 passed (123)
      Tests  787 passed (787)
   Start at  14:02:22
   Duration  92.42s (transform 56%, environment 16%, import 14%, tests 10%, setup 5%)

  Transform  |component| transforming modules took 190.68s · 49% of tracked time, re-done on every run
             persist transforms across runs with fsModuleCache: true
             learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

Before gates, git merge dev returned Already up to date. at base c46515046871936ad681c4d00e8b76de69dde3b7. The shared dev ref later advanced to 191b179baac3ef4f5bebfe07ce91c4b7a887ace2 while gates ran; I did not merge it a second time.

Completed the Google OAuth research note and committed it. - File: `docs/research/google-oauth.md` - Commit / head: `1501b545fcd2a57c76cd29bdfda73a3672c34833` - Superlocal was reviewed at `d3725bcb963f28544a10cd2a463f1d1017c6a6b7`. Google primary sources were accessed on 2026-09-29. - Findings: Gmail read/sync scopes are restricted and server-side sync triggers CASA; Calendar and Contacts scopes are sensitive. The note recommends OAuth for Calendar and Contacts and keeping Gmail on the existing app-password path for calternal.cloud. It records that Google's public guidance is unclear on whether self-hosted personal use removes CASA for server-side Gmail data. - No product code or credentials were added. Price research is omitted because the repository rules prohibit financial research in this public repository and issue tracker. - Decision where DESIGN.md is silent: treat per-Instance OAuth as a bring-your-own-client option, but do not claim it removes CASA for server-side Gmail data without a written answer from Google. Gates: `cargo fmt --check`: exit 0, no output. `cargo clippy --all-targets -- -D warnings`: passed. Final output: ```text Checking calternal-sync v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-sync) Checking calternal-plugin-analytics v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/plugins/analytics) Checking calternal-search v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-search) Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-collab) Checking calternal-notes-core v0.1.0 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-notes-core) Checking calternal-plugin-notes v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/plugins/notes) Checking calternal-path v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-path) Checking calternal-fs v0.1.0 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-fs) Checking calternal-embed v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-embed) Checking calternal-dav v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-dav) Checking calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-tags) Checking calternal-plugin v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-plugin) Checking calternal-db v0.1.0 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-db) Checking calternal-api v0.0.1 (/home/kayg/Developer/calternal-wt/google-oauth/crates/calternal-api) Finished `dev` profile [unoptimized + debuginfo] target(s) in 58.22s ``` `cargo test`: exit 101. It compiled successfully, then `calternal-cli/tests/output_contract.rs` failed one test. The other completed suites shown before the failure reported 1, 63, and 21 passing tests. Failure output: ```text test connection_resets_retry_safe_read_requests ... FAILED failures: connection_resets_retry_safe_read_requests thread 'connection_resets_retry_safe_read_requests' panicked at crates/calternal-cli/tests/output_contract.rs:772:5: stderr: [123, 34, 101, 114, 114, 111, 114, 34, 58, 123, 34, 101, 120, 105, 116, 95, 99, 111, 100, 101, 34, 58, 57, 44, 34, 109, 101, 115, 115, 97, 103, 101, 34, 58, 34, 101, 114, 114, 111, 114, 32, 115, 101, 110, 100, 105, 110, 103, 32, 114, 101, 113, 117, 101, 115, 116, 32, 102, 111, 114, 32, 117, 114, 108, 32, 40, 104, 116, 116, 112, 58, 47, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 51, 50, 57, 50, 49, 47, 97, 112, 105, 47, 118, 49, 47, 97, 117, 116, 104, 47, 109, 101, 41, 34, 125, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 49, 125, 10] error: test failed, to rerun pass `-p calternal-cli --test output_contract` ``` No test expectation was changed. This job changes no API, so no adversarial endpoint round was applicable. `bun run check`: ```text $ node scripts/check-type-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json Text sizes use shared role tokens. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/google-oauth/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test`: ```text Test Files 123 passed (123) Tests 787 passed (787) Start at 14:02:22 Duration 92.42s (transform 56%, environment 16%, import 14%, tests 10%, setup 5%) Transform |component| transforming modules took 190.68s · 49% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` Before gates, `git merge dev` returned `Already up to date.` at base `c46515046871936ad681c4d00e8b76de69dde3b7`. The shared `dev` ref later advanced to `191b179baac3ef4f5bebfe07ce91c4b7a887ace2` while gates ran; I did not merge it a second time.
Author
Owner

Owner idea (2026-09-29): use the Google Workspace CLI (github.com/googleworkspace/cli, 'gws') to proxy Gmail, Calendar and Contacts. Orchestrator assessment: gws uses the same Google OAuth, with the User's own Cloud project and client in unverified testing mode. Test users must be added by hand, the scope count is limited, and for consumer gmail.com accounts testing-mode refresh tokens expire after 7 days; Workspace 'Internal' apps avoid that but only inside an organisation. So it does not avoid restricted-scope verification for calternal.cloud. It fits the bring-your-own-client path for self-hosters: a guided setup modelled on 'gws auth setup', with an explicit re-auth warning, in Settings → Integrations. Our server calls the REST APIs directly; no CLI subprocess.

Owner idea (2026-09-29): use the Google Workspace CLI (github.com/googleworkspace/cli, 'gws') to proxy Gmail, Calendar and Contacts. Orchestrator assessment: gws uses the same Google OAuth, with the User's own Cloud project and client in unverified testing mode. Test users must be added by hand, the scope count is limited, and for consumer gmail.com accounts testing-mode refresh tokens expire after 7 days; Workspace 'Internal' apps avoid that but only inside an organisation. So it does not avoid restricted-scope verification for calternal.cloud. It fits the bring-your-own-client path for self-hosters: a guided setup modelled on 'gws auth setup', with an explicit re-auth warning, in Settings → Integrations. Our server calls the REST APIs directly; no CLI subprocess.
Author
Owner

Owner decision (2026-09-29): take the recommended path.

  • calternal.cloud: Gmail uses an app password (IMAP/SMTP), as today. Google Calendar and Contacts use OAuth with the sensitive scopes only (calendar, contacts), through one verified calternal.cloud OAuth client. No restricted Gmail scopes and no CASA.
  • Self-hosted: optional bring-your-own Google client (the Superlocal and gws pattern: a host-configured client ID and secret; login kept separate from the service connection). The UI warns about the testing-mode 7-day expiry and the unverified-app limits.
  • Owner action needed later: a Google Cloud project for calternal.cloud, the OAuth consent screen, domain verification and the demo video for sensitive-scope verification (the orchestrator prepares the texts and the checklist).
    Build after #407's Apple-style Integrations rework lands (same screens).
**Owner decision (2026-09-29): take the recommended path.** - calternal.cloud: Gmail uses an **app password** (IMAP/SMTP), as today. Google **Calendar and Contacts use OAuth** with the sensitive scopes only (calendar, contacts), through one verified calternal.cloud OAuth client. No restricted Gmail scopes and no CASA. - Self-hosted: optional bring-your-own Google client (the Superlocal and gws pattern: a host-configured client ID and secret; login kept separate from the service connection). The UI warns about the testing-mode 7-day expiry and the unverified-app limits. - Owner action needed later: a Google Cloud project for calternal.cloud, the OAuth consent screen, domain verification and the demo video for sensitive-scope verification (the orchestrator prepares the texts and the checklist). Build after #407's Apple-style Integrations rework lands (same screens).
Sign in to join this conversation.
No labels
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
kayg/calternal#408
No description provided.