# Notion OAuth session-conflict repro and hardening plan

## Current auth boundary

Time-2-Notion starts OAuth from `extension/src/options.js` with `chrome.identity.launchWebAuthFlow()` against `https://api.notion.com/v1/oauth/authorize`. Chrome then navigates a browser-controlled auth window using the current Chrome profile's Notion cookies/session. The extension only regains control after Notion redirects to the extension redirect URI from `chrome.identity.getRedirectURL("notion")`.

After that redirect, the extension posts only `{ code, redirectUri }` to the configured relay at `/oauth/token`. The relay exchanges the code with Notion using the Notion client ID/secret. The relay and later Notion API calls do not use browser Notion cookies, so an already-open Notion tab can only affect the pre-redirect authorization UI/session selection, not the token exchange or normal API requests.

## Working root-cause hypothesis

The reported “already logged into Notion in another tab makes auth choke” is most likely at the Notion authorization/session-selection boundary inside `chrome.identity.launchWebAuthFlow`, where Notion reuses the active Chrome profile's Notion session instead of presenting a clean account/workspace chooser. A stale, wrong-account, or multi-workspace Notion session can make Notion return an OAuth error, never reach the extension redirect, or return a code for a different workspace than expected.

Do not treat this as confirmed until a live browser repro captures whether the failure happens:

1. before the extension receives a redirect from Notion;
2. after redirect but before `/oauth/token` relay exchange;
3. during relay exchange;
4. after tokens are stored while loading shared Notion data sources.

## Clean repro matrix

Use a demo/test Notion workspace and redact all codes, tokens, client secrets, workspace IDs, and private workspace names from notes/screenshots.

For every case below, start from the same extension build and the same Notion OAuth connection/relay configuration.

### Shared setup

1. Load `extension/` unpacked in Chrome.
2. Open the options page.
3. Confirm the redirect URI shown on the options page exactly matches the Notion OAuth redirect allowlist.
4. Confirm the relay health endpoint returns `ok: true`, `hasClientId: true`, and `hasClientSecret: true`.
5. Click **Disconnect** in Time-2-Notion to clear extension tokens/timer state.
6. In options page DevTools, enable **Preserve log**.
7. Enter the Notion client ID and OAuth relay URL.

### Cases to run

| Case | Browser precondition | Expected observation to capture |
| --- | --- | --- |
| A. Clean profile baseline | Fresh Chrome profile or Incognito profile with extension allowed; no Notion tabs; no Notion cookies | Does Notion login + authorize complete and return to the extension? |
| B. Intended-account session | Same Chrome profile is already logged into the intended Notion account/workspace in another tab | Does Notion skip login and still complete authorization? |
| C. Different-account session | Same Chrome profile is logged into a different Notion account than the one intended for Time-2-Notion | Does Notion choose the wrong account/workspace, return an OAuth error, or fail to redirect? |
| D. Multi-workspace account | Same Notion account has multiple workspaces and is currently viewing a non-demo workspace | Does the OAuth UI let the user choose the workspace/page access, or does it silently bind to the active workspace? |
| E. Stale/revoked session | User is logged into Notion in a tab, then revokes the integration or signs out/in before retrying | Does Notion return `access_denied`, no redirect, or a relay token error? |

## Data to capture for each case

1. Final status text shown in the Time-2-Notion options page.
2. Whether the OAuth window visibly reaches the extension redirect URI or closes with a Chrome identity error.
3. The `chrome.runtime.lastError.message`, if present.
4. If a redirect URL is received, record only:
   - origin/path,
   - query parameter names,
   - whether `state` exists and matches,
   - whether `code` exists,
   - whether `error` / `error_description` exists.
   Do not record the `code` value.
5. Relay logs for `/oauth/token`:
   - request timestamp,
   - HTTP status,
   - sanitized Notion error code/message,
   - never log authorization codes, access tokens, refresh tokens, or client secrets.
6. If tokens are stored and the failure happens during data-source load, capture the Notion API status/message from the options page and relay is no longer implicated.

## Decision tree

- **No redirect and `chrome.runtime.lastError` is set**: boundary is Chrome identity / Notion hosted auth UI / browser session. Hardening should improve user guidance and capture safer diagnostics; code cannot force an isolated Notion cookie jar.
- **Redirect contains `error`**: boundary is Notion OAuth authorization. Surface Notion's `error_description` when available and add user guidance for switching accounts/workspaces before retry.
- **Redirect contains `code`, but relay `/oauth/token` fails**: boundary is relay or Notion OAuth app configuration. Check redirect URI allowlist, client ID/secret pairing, and relay deployment.
- **Token exchange succeeds, but data-source loading fails**: boundary is Notion API permissions/shared data sources. Ask the user to confirm the integration has access to the target pages/data sources.

## Hardening path

1. Add temporary, redacted OAuth diagnostics around `launchWebAuthFlow` during live triage: lastError, redirect param names, state match, and relay HTTP status only.
2. Improve permanent UI error messages after the failing boundary is known:
   - no redirect/lastError: explain that Chrome reuses the active Notion browser session and suggest retrying in a dedicated Chrome profile or Incognito window with the intended Notion account;
   - Notion `error_description`: show the description without secrets;
   - relay errors: distinguish redirect/client-secret configuration from user authorization problems.
3. Prevent concurrent auth attempts by disabling **Connect Notion** while a flow is active and re-enabling on callback/error.
4. Consider extracting auth URL construction and redirect parsing into testable helpers before changing auth behavior.
5. Do not add non-standard OAuth parameters such as `prompt=login` / `prompt=select_account` unless Notion documents support for them and a live test confirms they fix the session-conflict case.
