`twitterv2` provider uses OAuth 1.0a internally — fails with X Free plan (401 Unauthorized)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 6.6k
- Forks
- 631
- PR merge metrics
- No merged PRs in 30d
Description
Summary
The twitterv2 provider does not actually use OAuth 2.0 for its authentication flow. It uses OAuth 1.0a via mrjones/oauth, and only calls the Twitter API v2 endpoints (/2/users/me). This causes a 401 Unauthorized error on X's Free plan, which does not support OAuth 1.0a.
Details
The twitterv2 provider was added in #460 to address #440, but the underlying auth mechanism was not changed to OAuth 2.0 — only the API endpoints were updated to v2.
Internal dependency chain:
goth/providers/twitterv2
→ github.com/mrjones/oauth (OAuth 1.0a, HMAC-SHA1 signatures)
Error seen at runtime:
HTTP response is not 200/OK as expected.
Response Status: '401 Unauthorized'
Request Headers: Authorization: OAuth oauth_signature_method="HMAC-SHA1" ...
X's Free plan (the only plan available for new developer accounts) requires OAuth 2.0 with PKCE. The OAuth 1.0a request token endpoint returns 401 because it is disabled for Free-tier apps.
Expected behavior
The twitterv2 provider should use OAuth 2.0 + PKCE (code_challenge_method=S256) as documented in X's OAuth 2.0 documentation.
Workaround
I worked around this by implementing X OAuth 2.0 + PKCE outside of goth using golang.org/x/oauth2, while keeping goth for Google login.
Implementation approach:
- Created a standalone X OAuth 2.0 handler that wraps
golang.org/x/oauth2.Config - In the HTTP handler, routes for
"x"are dispatched to the custom OAuth 2.0 flow; all other providers continue to use goth - State and PKCE code verifier are stored in a dedicated
gorilla/sessionscookie (separate from goth's session) - On callback: validate state, exchange code with PKCE verifier, then call
/2/users/meto fetch user info - The result is mapped to the same internal user struct used by goth providers, so downstream session/account logic is unchanged
Key golang.org/x/oauth2 APIs used:
oauth2.GenerateVerifier()— generate PKCE code verifieroauth2.S256ChallengeOption(verifier)— attach S256 challenge to auth URLoauth2.VerifierOption(verifier)— attach verifier to token exchangeConfig.AuthCodeURL()/Config.Exchange()/Config.Client()— standard OAuth 2.0 flow
X OAuth 2.0 endpoints:
- Auth:
https://twitter.com/i/oauth2/authorize - Token:
https://api.twitter.com/2/oauth2/token - User info:
https://api.twitter.com/2/users/me?user.fields=id,name,username,profile_image_url - Scopes:
users.read tweet.read
This works correctly with X's Free plan. Login flow completes successfully and returns user profile data.
Related issues
- #440 — Original request for Twitter OAuth 2.0 support (closed by #460)
- #460 — Added
twitterv2provider (API v2 endpoints, but auth flow remained OAuth 1.0a)
Environment
- goth v1.82.0
- Go 1.26
- X Developer Portal: Free plan
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in goth/providers/twitterv2 and trace its current mrjones/oauth authentication flow and /2/users/me request. Compare the provider with X's documented OAuth 2.0 + PKCE endpoints and the listed golang.org/x/oauth2 APIs. Done means the provider authenticates on X's Free plan and still returns the existing internal user data.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authentication
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100