Skip to content

x connector: signed state JWT exceeds X's 500-char state limit, authorize fails with invalid request #152

Description

@vincanger

Summary

The x connector's OAuth authorize request is rejected by X (oauth_denied / invalid request) before the consent screen renders. The app in the X Developer Portal is configured correctly (project-attached, confidential client, Read and write, exact callback registered).

The cause is the state parameter. signOAuthState (src/connectors/oauth.ts) packs the whole flow context — provider, principalId, redirectUri, orgId, clientRef, returnTo, PKCE codeVerifier, issuedAt, nonce — into an HS256 JWT sent as state. For an ordinary deployment (19-char principal email, default <prefix>-portal.fly.dev public URL) the JWT comes out at 519 characters. X's OAuth 2.0 authorize endpoint rejects any state longer than 500 characters; other providers (Google, GitHub, Dropbox, Notion, Linear) tolerate long state, so only the x connector breaks.

Because the overage is small, whether a given deployment hits it depends on the length of the signing email, org id, and public URL — which is presumably why it slipped through.

Repro

  1. Deploy with any org whose principal email + publicUrl push the state JWT past 500 chars (a 19-char email and a 20-char hostname suffice).
  2. Configure the x connector per the setup guide and connect from the keychain page.
  3. X responds with invalid request at x.com/i/oauth2/authorize; the consent screen never renders.

Expected

The authorize request stays within every provider's documented limits regardless of principal email / URL length.

Possible fixes

  • Keep the flow context server-side (Postgres, consistent with the durable-by-default rule) keyed by a short random state, or
  • slim the JWT: the 43-char PKCE codeVerifier doesn't need to ride in the URL at all, and redirectUri is derivable from config + provider. Dropping those two alone brings the state comfortably under 500.

Happy to send a PR if there's a preferred direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions