# Sprint 10: Integration Config + OAuth Types

> **Priority**: 4 | **Owner**: Backend + Client | **Complexity**: Medium-High
> **Prerequisite**: Sprint 9

---

## Validation Status (2025-12-29)

### ✅ IMPLEMENTATION COMPLETE

**All OAuth infrastructure verified and production-ready.**

| Component | Location | Status |
|-----------|----------|--------|
| Integration domain entity | `domain/entities/integration.py:13-124` | ✅ IntegrationType, IntegrationStatus, Integration dataclass |
| OAuth token storage | `persistence/models/integrations/integration.py:108-137` | ✅ IntegrationSecretModel with encrypted storage |
| InitiateOAuth RPC | `grpc/_mixins/calendar.py:113-133` | ✅ Returns auth_url + state |
| CompleteOAuth RPC | `grpc/_mixins/calendar.py:135-162` | ✅ Exchanges code, retrieves email |
| GetOAuthConnectionStatus RPC | `grpc/_mixins/calendar.py:164-177` | ✅ Returns connection state |
| DisconnectOAuth RPC | `grpc/_mixins/calendar.py:179-190` | ✅ Revokes tokens, deletes integration |
| Token refresh | `application/services/calendar_service.py:336-351` | ✅ Automatic refresh on expiry |
| PKCE (S256) | `infrastructure/calendar/oauth_manager.py:312-420` | ✅ code_verifier, code_challenge |
| Deep link handler | `client/src-tauri/tauri.conf.json:46-50`, `client/src/hooks/use-oauth-flow.ts` | ✅ noteflow:// scheme + CSRF validation |
| Integration config panel | `client/src/components/settings/integrations-section.tsx` | ✅ Calendar tab with OAuth connect/disconnect |
| Repository methods | `repositories/integration_repo.py:54-200` | ✅ get_by_provider, get_secrets, set_secrets |
| Backend OAuth tests | `tests/grpc/test_oauth.py` | ✅ Comprehensive behavioral tests |
| Frontend OAuth tests | `client/src/hooks/use-oauth-flow.test.ts` | ✅ Hook state management tests |
| Calendar trigger warning | `client/src/components/settings/triggers-section.tsx` | ✅ Warns when calendar not connected |

**Closure Notes:**
- ✅ Behavioral tests implemented (backend + frontend)
- ✅ Integration config now persists via preferences sync (Sprint 14)

---

## Objective

Persist integration configuration to the backend and validate the existing OAuth flow end-to-end (PKCE, deep link, token storage).

---

## Current State Analysis

### Backend Infrastructure

**Domain Layer** ✅ Complete:
- `IntegrationType` enum with `CALENDAR` value
- `IntegrationStatus` enum: `disconnected`, `connected`, `error`
- `Integration` dataclass with provider and status tracking

**Persistence Layer** ✅ Complete:
- `IntegrationModel` — Configuration and status
- `IntegrationSecretModel` — Encrypted OAuth tokens
- `CalendarEventModel` — Cached calendar events
- `SqlAlchemyIntegrationRepository` with methods: `get`, `get_by_provider`, `create`, `update`, `delete`, `get_secrets`, `set_secrets`, `list_by_type`

**OAuth Endpoints** ✅ Already present:
- `InitiateOAuth`, `CompleteOAuth`, `GetOAuthConnectionStatus`, `DisconnectOAuth` live in `grpc/_mixins/calendar.py`
- PKCE state handling and token refresh are managed by `infrastructure/calendar/oauth_manager.py` and `CalendarService`

### Frontend State

**Existing Types** (modularized in `client/src/api/types/`):
- `OAuthConfig` at `requests.ts:187`, `Integration` at `requests.ts:250`, `IntegrationStatus` at `requests.ts:184`
- Types are modularized across `core.ts`, `enums.ts`, `requests.ts`, `features.ts`
- PKCE state is handled internally by `use-oauth-flow.ts`; no shared type needed

---

## Scope

| Task | Effort | Risk |
|------|--------|------|
| Persist integration config to backend | Medium | Medium (data migration) |
| Expose config sync in API/Tauri adapter | Low | Low |
| Validate OAuth flow + deep link wiring | Low | Low |
| Wire Settings integration cards to backend | Low | Low |

---

## Implementation Plan

### Task 1: Persist Integration Config to Backend

**Files to Modify**:
- `src/noteflow/infrastructure/persistence/repositories/integration_repo.py` — add/update config persistence helpers
- `src/noteflow/grpc/_mixins/calendar.py` (or new mixin) — add RPCs to read/write integration config
- `client/src/api/interface.ts`, `client/src/api/tauri-adapter.ts` — expose config endpoints
- `client/src/pages/Settings.tsx` — save config via backend instead of local-only prefs

**Notes**:
- Reuse `IntegrationModel.config` JSON for provider-specific configuration.
- Avoid creating new OAuth endpoints; existing OAuth flow is already implemented.

### Task 2: Validate Existing OAuth Wiring ✅ VERIFIED

**Files Verified**:
- `src/noteflow/infrastructure/calendar/oauth_manager.py` — PKCE (`_generate_code_verifier`, `_generate_code_challenge`), token exchange, refresh, 10-min state TTL
- `src/noteflow/application/services/calendar_service.py:336` — Implicit token refresh during event fetch
- `src/noteflow/grpc/_mixins/calendar.py` — `InitiateOAuth` (L113), `CompleteOAuth` (L135), `GetOAuthConnectionStatus` (L164), `DisconnectOAuth` (L179)
- `client/src/hooks/use-oauth-flow.ts` — `useOAuthFlow` hook with `parseOAuthCallback`
- `client/src-tauri/tauri.conf.json` — `deep-link` plugin with `noteflow://` scheme

**Notes**:
- OAuth state is stored in-memory with TTL; not suitable for multi-worker deployments without shared cache.

### Task 3: Align Frontend Types + UI

- Keep using `client/src/api/types.ts` for OAuth/Integration types.
- Surface integration errors and sync status in Settings (use `error_message` + `status`).

---

## Behavioral Tests (Implemented)

> **Status**: ✅ Implemented — `tests/grpc/test_oauth.py` (19 tests) and
> `client/src/hooks/use-oauth-flow.test.ts` (19 tests).

### Backend OAuth Tests

**File**: `tests/grpc/test_oauth.py`

Comprehensive test suite covering:

| Test Class | Coverage |
|------------|----------|
| `TestGetCalendarProviders` | Provider listing, empty results |
| `TestInitiateOAuth` | Auth URL generation, state tokens, invalid providers |
| `TestCompleteOAuth` | Code exchange, invalid state handling, missing params |
| `TestGetOAuthConnectionStatus` | Connection state retrieval per provider |
| `TestDisconnectOAuth` | Token revocation, disconnect handling |
| `TestOAuthRoundTrip` | Full OAuth flow integration |
| `TestOAuthSecurityBehavior` | CSRF validation, no secrets in errors |

### Frontend OAuth Hook Tests

**File**: `client/src/hooks/use-oauth-flow.test.ts`

Comprehensive test suite covering:

| Test Category | Coverage |
|---------------|----------|
| Initial state | Default state values, no pending auth |
| `initiateAuth()` | Auth URL retrieval, state storage, error handling |
| `completeAuth()` | Code exchange, success/failure states, CSRF validation |
| `checkConnection()` | Status polling, provider-specific checks |
| `disconnect()` | Token revocation, state reset |
| `reset()` | Full state reset |
| State transitions | Loading states, error recovery, concurrent ops |
| Edge cases | Empty providers, network errors, repeated calls |

## Acceptance Criteria

### Functional

- [x] Users can connect Google Calendar via OAuth *(flow verified via RPC + client hook tests)*
- [x] PKCE is used for all OAuth flows (`oauth_manager.py` implements S256)
- [x] Deep link callbacks are handled correctly (`tauri.conf.json` + `use-oauth-flow.ts`)
- [x] Users can disconnect integrations (`DisconnectOAuth` RPC implemented)
- [x] Status updates immediately in UI (`GetOAuthConnectionStatus` RPC implemented)
- [x] Calendar tab in Settings uses real OAuth flow (`integrations-section.tsx`)
- [x] TriggersSection warns when calendar enabled but not connected

### Security

- [x] State parameter validated (CSRF protection) — `oauth_manager.py` validates state
- [x] Code verifier only sent to token endpoint — PKCE flow in `_exchange_code()`
- [x] Tokens encrypted at rest — `IntegrationSecretModel` with encryption
- [x] Tokens revoked on disconnect — `DisconnectOAuth` calls `revoke_tokens()`
- [x] OAuth state expires after 10 minutes (`STATE_TTL_SECONDS = 600` in `oauth_manager.py`)

### Quality Gates

- [x] `pytest tests/grpc/test_oauth.py` passes
- [x] `npm run test` passes for frontend
- [x] No secrets in client code or logs
- [x] Deep links work on macOS and Linux (`noteflow://` scheme configured)

---

## Rollback Plan

1. **OAuth issues**: Disable connect buttons, show "coming soon"
2. **Token storage issues**: Clear tokens, require re-auth
3. **Deep link issues**: Add manual code entry fallback

---

## Post-Sprint

- Add additional providers beyond Google/Outlook
- Expose manual refresh/health check endpoint
- Add OAuth error recovery UI
- Support multiple accounts per provider
