# Sprint 17: Custom OAuth Providers

> **Size**: L | **Owner**: Backend + Client | **Prerequisites**: Sprint 16
> **Phase**: 5 - Platform Evolution

---

## Implementation Status (2025-12-30)

### ✅ CORE IMPLEMENTATION COMPLETE

| Component | Status | Notes |
|-----------|--------|-------|
| OIDC domain entities | ✅ Complete | `OidcProviderConfig`, `ClaimMapping`, `OidcDiscoveryConfig` |
| OIDC discovery client | ✅ Complete | Fetches and parses `.well-known/openid-configuration` |
| Provider registry | ✅ Complete | Create, list, remove providers with validation |
| Provider presets | ✅ Complete | Authentik, Authelia, Keycloak, Auth0, Okta, Azure AD |
| Claim mapping | ✅ Complete | Configurable claim names for user attributes |
| Unit tests | ✅ Complete | 70 tests covering domain, infrastructure, and gRPC |
| gRPC endpoints | ✅ Complete | 7 RPCs: Register, List, Get, Update, Delete, Refresh, ListPresets |
| Provider management UI | ✅ Complete | Settings section with add/edit/delete/toggle/refresh |

---

### ✅ PREREQUISITES VERIFIED

| Asset | Status | Location |
|-------|--------|----------|
| Generic OAuth messages | ✅ Verified | `noteflow.proto` lines 54-58, 766-857 |
| OAuthManager | ✅ Verified | `infrastructure/calendar/oauth_manager.py` (431 lines) |
| IntegrationModel.config | ✅ Verified | JSONB field at `integration.py:62-66` |
| IntegrationSecretModel | ✅ Verified | Encrypted token storage at `integration.py:108-137` |

**Ready to implement**: All prerequisites in place. OIDC-specific extensions can proceed.

---

## Objective

Support Authentik, Authelia, and other OIDC providers for SSO integration.

---

## Decision: Discovery Only

> **Decided**: OIDC discovery only — require issuer URL with `.well-known/openid-configuration`. Covers 90%+ of providers (Authentik, Authelia, Keycloak, Auth0, etc.).

---

## What Already Exists

| Asset | Location | Implication |
|-------|----------|-------------|
| Generic OAuth messages | `noteflow.proto` | Extensible to OIDC |
| `OAuthManager` | `infrastructure/calendar/oauth_manager.py` | PKCE, state, refresh logic |
| `IntegrationModel.config` | ORM | Can store provider config |
| `IntegrationSecretModel` | ORM | Encrypted token storage |

---

## Scope

| Task | Effort | Status |
|------|--------|--------|
| OIDC provider registry schema | M | ✅ Complete |
| OIDC discovery endpoint support | M | ✅ Complete |
| Claim mapping (email, sub, groups) | M | ✅ Complete |
| Authentik/Authelia presets | S | ✅ Complete |
| Provider management UI | M | ✅ Complete |

---

## Deliverables

### Files Created

| File | Purpose |
|------|---------|
| `src/noteflow/domain/auth/__init__.py` | Domain module exports |
| `src/noteflow/domain/auth/oidc.py` | `OidcProviderConfig`, `ClaimMapping`, `OidcDiscoveryConfig`, `OidcProviderPreset` |
| `src/noteflow/infrastructure/auth/__init__.py` | Infrastructure module exports |
| `src/noteflow/infrastructure/auth/oidc_discovery.py` | `OidcDiscoveryClient`, `OidcDiscoveryError` |
| `src/noteflow/infrastructure/auth/oidc_registry.py` | `OidcProviderRegistry`, `OidcAuthService`, `PROVIDER_PRESETS` |
| `tests/domain/auth/__init__.py` | Test module |
| `tests/domain/auth/test_oidc.py` | Domain entity tests (16 tests) |
| `tests/infrastructure/auth/__init__.py` | Test module |
| `tests/infrastructure/auth/conftest.py` | Shared fixtures |
| `tests/infrastructure/auth/test_oidc_discovery.py` | Discovery client tests (11 tests) |
| `tests/infrastructure/auth/test_oidc_registry.py` | Registry and service tests (16 tests) |
| `src/noteflow/grpc/_mixins/oidc.py` | gRPC mixin for OIDC provider management (7 RPCs) |
| `tests/grpc/test_oidc_mixin.py` | gRPC endpoint tests (27 tests) |

### Client Files Created/Modified

| File | Purpose |
|------|---------|
| `client/src/api/types/oidc.ts` | TypeScript types for OIDC providers, presets, requests/responses |
| `client/src/hooks/use-oidc-providers.ts` | React hook for OIDC provider state management |
| `client/src/components/settings/oidc-providers-section.tsx` | Settings UI component for provider management |
| `client/src/api/interface.ts` | Added 10 OIDC API methods to interface |
| `client/src/api/tauri-adapter.ts` | Implemented OIDC Tauri IPC commands |
| `client/src/api/tauri-constants.ts` | Added 9 OIDC command constants |
| `client/src/api/mock-adapter.ts` | Added OIDC mock implementations for browser dev |
| `client/src/api/types/index.ts` | Added OIDC type exports |
| `client/src/components/settings/index.ts` | Added OidcProvidersSection export |
| `client/src/pages/Settings.tsx` | Integrated OidcProvidersSection |

---

## API Reference

### Domain Entities

#### OidcProviderConfig

Main configuration entity for an OIDC provider.

```python
@dataclass
class OidcProviderConfig:
    id: UUID
    workspace_id: UUID
    name: str
    preset: OidcProviderPreset
    issuer_url: str
    client_id: str
    enabled: bool = True
    discovery: OidcDiscoveryConfig | None = None
    claim_mapping: ClaimMapping = ClaimMapping()
    scopes: tuple[str, ...] = ("openid", "profile", "email")
    require_email_verified: bool = True
    allowed_groups: tuple[str, ...] = ()
```

#### ClaimMapping

Maps OIDC claims to NoteFlow user attributes.

```python
@dataclass(frozen=True, slots=True)
class ClaimMapping:
    subject_claim: str = "sub"
    email_claim: str = "email"
    email_verified_claim: str = "email_verified"
    name_claim: str = "name"
    preferred_username_claim: str = "preferred_username"
    groups_claim: str = "groups"
    picture_claim: str = "picture"
```

#### OidcProviderPreset

Supported provider presets:
- `AUTHENTIK` - goauthentik.io
- `AUTHELIA` - authelia.com
- `KEYCLOAK` - keycloak.org
- `AUTH0` - auth0.com
- `OKTA` - okta.com
- `AZURE_AD` - Microsoft Entra ID
- `CUSTOM` - Any OIDC-compliant provider

### Infrastructure Services

#### OidcDiscoveryClient

Fetches and validates OIDC discovery documents.

```python
client = OidcDiscoveryClient(timeout=10.0, verify_ssl=True)

# Fetch discovery document
config = await client.discover("https://auth.example.com")

# Update provider with discovery
await client.discover_and_update(provider)

# Validate provider configuration
warnings = await client.validate_provider(provider)
```

#### OidcProviderRegistry

Manages provider configurations.

```python
registry = OidcProviderRegistry()

# Create provider with auto-discovery
provider = await registry.create_provider(
    workspace_id=workspace_id,
    name="Authentik",
    issuer_url="https://auth.example.com",
    client_id="noteflow",
    preset=OidcProviderPreset.AUTHENTIK,
)

# List providers
providers = registry.list_providers(workspace_id=workspace_id, enabled_only=True)

# Get preset configuration
preset = registry.get_preset_config(OidcProviderPreset.KEYCLOAK)
```

#### OidcAuthService

High-level service for OIDC authentication operations.

```python
service = OidcAuthService()

# Register and validate provider
provider, warnings = await service.register_provider(
    workspace_id=workspace_id,
    name="My Provider",
    issuer_url="https://auth.example.com",
    client_id="noteflow",
    preset=OidcProviderPreset.AUTHENTIK,
)

# Refresh discovery for all providers
results = await service.refresh_all_discovery(workspace_id=workspace_id)

# Get available presets
options = service.get_preset_options()
```

---

## Provider Preset Details

| Provider | Default Scopes | Groups Claim | Notes |
|----------|---------------|--------------|-------|
| Authentik | openid, profile, email, groups | `groups` | Standard OIDC + groups |
| Authelia | openid, profile, email, groups | `groups` | Requires config.yml registration |
| Keycloak | openid, profile, email | `groups` | Needs Group Membership mapper |
| Auth0 | openid, profile, email | Custom namespace | Requires Action/Rule for groups |
| Okta | openid, profile, email, groups | `groups` | Enable 'groups' scope in app |
| Azure AD | openid, profile, email | `groups` | Configure optional claims |

---

## Quality Gates

- [x] All lint checks pass (ruff)
- [x] All type checks pass (basedpyright)
- [x] All 43 OIDC tests pass
- [x] No duplicate test names
- [x] No cross-file fixture duplicates
- [x] Meets `docs/sprints/QUALITY_STANDARDS.md`

---

## Post-Sprint

- Group-based workspace membership
- Provider health monitoring
- Multi-provider per user
- gRPC endpoints for provider management
- Rust/Tauri command implementations (currently mock-only in browser)
