Add a general-purpose OAuth 2.0 flow to the agent/bot infrastructure so Ava can connect to any OAuth provider (Dexcom, Oura, etc.) on behalf of the user.
Design
Callback Endpoint
- Add GET /oauth/callback route to agentd's existing warp/servant server
- Add GET /oauth/pending/:state route for the bot to poll for completed auth codes
- Store pending auth codes in SQLite (oauth_pending table with code, state, timestamp)
- Callback handler shows a simple 'you can close this tab' HTML page after receiving the code
- Redirect URI uses Tailscale MagicDNS hostname with HTTPS (e.g. https://beryllium.TAILNET.ts.net:8400/oauth/callback)
- No public internet exposure — only accessible from devices on the tailnet
Provider Config Model
OAuthProvider:
- providerId :: Text (e.g. 'dexcom', 'oura')
- authorizationUrl :: Text
- tokenUrl :: Text
- clientId :: Text
- clientSecret :: Maybe Text
- scopes :: [Text]
- usePkce :: Bool
- extraParams :: Map Text Text (provider-specific quirks)
Telegram Bot Flow
1. User says 'connect <provider>' in Telegram
2. Bot prompts for client ID and client secret (or reads from stored config)
3. Bot generates auth URL with random state param and redirect_uri pointing to agentd callback
4. Sends clickable link to Telegram
5. User clicks, authorizes on provider site, browser redirects to agentd callback on Tailscale
6. Callback stores code+state in oauth_pending SQLite table
7. Bot polls /oauth/pending/<state> until code arrives
8. Bot exchanges code for tokens using stored provider config (+ PKCE verifier if applicable)
9. Stores tokens in auth.json under provider key (Omni.Agent.Auth pattern)
10. Confirms in Telegram with checkmark
Existing Code to Build On
- Omni.Auth — generic OAuth types (OAuthArgs, OAuthResponse), GitHub OAuth flow, servant cookie helpers
- Omni.Agent.Auth — token storage in ~/.local/share/agent/auth.json, PKCE support
- Telegram bot handleLoginCommand — existing OAuth-via-chat flow (hardcoded to Anthropic, should be generalized)
- pendingLoginsVar :: TVar (Map Int (Text, UTCTime)) — existing in-flight auth tracking
- agentd already runs a warp server with servant routes on port 8400
Tailscale HTTPS
- Use tailscale cert for auto-HTTPS on MagicDNS name
- Ensure agentd serves HTTPS on the callback route (or use Tailscale's built-in TLS termination)
Token Refresh
- Store refresh_token alongside access_token
- On API calls, check expiry and auto-refresh if needed
- This should be generic — part of the OAuthProvider abstraction
Acceptance Criteria
- [ ] agentd has /oauth/callback and /oauth/pending/:state routes
- [ ] Telegram bot can initiate OAuth flow for any configured provider
- [ ] Tokens are stored and retrievable via Omni.Agent.Auth
- [ ] Works over Tailscale (no public internet exposure)
- [ ] Token refresh is handled generically
- [ ] First integration: Dexcom CGM data access