Skip to content

feat: implement X OAuth 2.0 PKCE backend and env var support#60

Open
shaal wants to merge 1 commit intoviperrcrypto:mainfrom
shaal:feat/x-oauth-backend
Open

feat: implement X OAuth 2.0 PKCE backend and env var support#60
shaal wants to merge 1 commit intoviperrcrypto:mainfrom
shaal:feat/x-oauth-backend

Conversation

@shaal
Copy link
Copy Markdown

@shaal shaal commented Mar 24, 2026

Summary

  • Implements the complete X OAuth 2.0 PKCE flow backend that the existing Live Import UI expects but was missing
  • Adds environment variable support (X_OAUTH_CLIENT_ID, X_OAUTH_CLIENT_SECRET) as fallback for X OAuth credentials, consistent with how ANTHROPIC_API_KEY works
  • Adds env var documentation to .env.example

New API routes

Route Method Description
/api/import/x-oauth/status GET Returns OAuth config & connection status
/api/import/x-oauth/authorize GET Generates PKCE challenge and returns X auth URL
/api/import/x-oauth/callback GET Handles X redirect, exchanges code for tokens
/api/import/x-oauth/disconnect POST Revokes token and clears stored credentials
/api/import/x-oauth/fetch POST Fetches bookmarks via X API v2 with pagination

Note

The X API v2 GET /2/users/:id/bookmarks endpoint requires a paid developer plan (Basic at $200/month). Free-tier accounts will see a credits error after connecting. This is an X platform limitation, not a Siftly bug — but it may be worth adding a note in the UI.

Test plan

  • Set X_OAUTH_CLIENT_ID and X_OAUTH_CLIENT_SECRET in .env.local
  • Verify /import page shows "Connect X Account" button (not "not configured" warning)
  • Click connect → redirects to X authorization page
  • After authorizing, callback redirects back to /import?x_connected=true
  • Status endpoint shows connected: true with user info
  • Disconnect clears tokens and reverts to "Connect" button

Closes #59

- Add complete OAuth 2.0 PKCE flow: authorize, callback, status,
  disconnect, and bookmark fetch endpoints
- Support X_OAUTH_CLIENT_ID and X_OAUTH_CLIENT_SECRET env vars
  as fallback when credentials aren't stored in the database
- Auto-refresh expired tokens using offline.access scope
- Paginate through X API v2 bookmarks with media expansion
- Document new env vars in .env.example

Closes viperrcrypto#59
Copy link
Copy Markdown

@xkonjin xkonjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: X OAuth 2.0 PKCE Integration

Overall: Comprehensive OAuth implementation with proper PKCE flow, token refresh, and secure storage. The code follows X's OAuth 2.0 guidelines and handles the edge cases well.

What looks good

  • Proper PKCE implementation with SHA256 code challenge
  • State parameter for CSRF protection
  • Token refresh logic with automatic retry on expiry
  • Graceful handling of missing user info during callback
  • Token revocation on disconnect (optional but good practice)
  • Support for both env vars and DB-stored credentials

Security concerns

1. Token storage (medium risk)
OAuth tokens are stored in the database unencrypted. While better than localStorage, consider:

  • Documenting that this is acceptable for the threat model (single-user app)
  • Or adding encryption at rest using an encryption key from env
  • The refresh token is particularly sensitive — if leaked, it grants persistent access

2. Error message information disclosure
Some error responses include raw error details that might leak internal state. Consider sanitizing error messages in production.

Issues to address

1. Missing rate limiting on OAuth endpoints
The authorize/fetch endpoints don't appear to have rate limiting. Consider adding per-IP and per-user rate limiting.

2. No token expiration notification
If the refresh token expires or is revoked, the user just sees an error. Consider storing a needs-reconnect flag and showing a UI banner.

3. Pagination token not persisted
If the fetch is interrupted mid-pagination, the next_token is lost. Consider storing it in DB to resume large imports.

Testing gap

OAuth flows are hard to test, but at minimum:

  • Unit tests for refreshAccessToken and getAccessToken with mocked Prisma
  • Test for state mismatch rejection
  • Test for token exchange failure handling

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Live import (X OAuth 2.0 PKCE) requires paid API tier for bookmarks

2 participants