Skip to content

Commit 1d87324

Browse files
authored
Merge pull request #19 from xdevplatform/fix-oauth2-bugs
Fix OAuth2 PKCE Implementation Issues
2 parents 5a4e14f + 934f8f6 commit 1d87324

4 files changed

Lines changed: 30 additions & 19 deletions

File tree

xdk-gen/templates/python/main_client.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Client:
2222
"""Client for interacting with the X API."""
2323

2424
def __init__(self,
25-
base_url: str = "https://api.twitter.com",
25+
base_url: str = "https://api.x.com",
2626
bearer_token: str = None,
2727
client_id: str = None,
2828
client_secret: str = None,

xdk-gen/templates/python/oauth2_auth.j2

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class OAuth2PKCEAuth:
2121
"""OAuth2 PKCE authentication for the X API."""
2222

2323
def __init__(self,
24-
base_url: str = "https://api.twitter.com",
24+
base_url: str = "https://api.x.com",
2525
client_id: str = None,
2626
client_secret: str = None,
2727
redirect_uri: str = None,
@@ -30,7 +30,7 @@ class OAuth2PKCEAuth:
3030
"""Initialize the OAuth2 PKCE authentication.
3131

3232
Args:
33-
base_url: The base URL for the X API.
33+
base_url: The base URL for the X API token endpoint (defaults to https://api.x.com).
3434
client_id: The client ID for the X API.
3535
client_secret: The client secret for the X API.
3636
redirect_uri: The redirect URI for OAuth2 authorization.
@@ -100,8 +100,10 @@ class OAuth2PKCEAuth:
100100
scope=self.scope
101101
)
102102

103+
# Authorization URL is always https://x.com/i/oauth2/authorize
104+
# (not using base_url since it's for API calls, not authorization)
103105
auth_url, state = self.oauth2_session.authorization_url(
104-
f"{self.base_url}/oauth2/authorize",
106+
"https://x.com/i/oauth2/authorize",
105107
code_challenge=self.code_challenge,
106108
code_challenge_method="S256"
107109
)
@@ -121,7 +123,7 @@ class OAuth2PKCEAuth:
121123
raise ValueError("OAuth2 session not initialized. Call get_authorization_url first.")
122124

123125
self.token = self.oauth2_session.fetch_token(
124-
f"{self.base_url}/oauth2/token",
126+
f"{self.base_url}/2/oauth2/token",
125127
authorization_response=authorization_response,
126128
code_verifier=self.code_verifier,
127129
client_id=self.client_id,
@@ -139,7 +141,7 @@ class OAuth2PKCEAuth:
139141
if not self.oauth2_session or not self.token:
140142
raise ValueError("No token to refresh")
141143

142-
refresh_url = f"{self.base_url}/oauth2/token"
144+
refresh_url = f"{self.base_url}/2/oauth2/token"
143145

144146
self.token = self.oauth2_session.refresh_token(
145147
refresh_url,

xdk-gen/templates/python/process_for_mintlify.j2

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,43 +1042,48 @@ This example shows how to use OAuth 2.0 with Proof Key for Code Exchange (PKCE).
10421042
**Example** (using a web server for callback):
10431043
10441044
```python
1045-
from xdk.auth import OAuth2PKCE
1045+
from xdk.oauth2_auth import OAuth2PKCEAuth
10461046
from urllib.parse import urlparse
10471047
import webbrowser
10481048
10491049
# Step 1: Create PKCE instance
1050-
auth = OAuth2PKCE(
1051-
client_id="your_client_id",
1052-
redirect_uri="http://localhost:8080/callback",
1053-
scopes=["tweet.read", "users.read", "offline.access"] # Adjust scopes as needed
1050+
auth = OAuth2PKCEAuth(
1051+
client_id="YOUR_CLIENT_ID",
1052+
redirect_uri="YOUR_CALLBACK_URL",
1053+
scope="tweet.read users.read offline.access"
10541054
)
10551055
10561056
# Step 2: Get authorization URL
1057-
auth_url = auth.get_authorization_url()
1057+
auth_url, state = auth.get_authorization_url()
10581058
print(f"Visit this URL to authorize: {auth_url}")
10591059
webbrowser.open(auth_url)
10601060
10611061
# Step 3: Handle callback (in a real app, use a web framework like Flask)
10621062
# Assume callback_url = "http://localhost:8080/callback?code=AUTH_CODE_HERE"
10631063
callback_url = input("Paste the full callback URL here: ")
1064-
parsed = urlparse(callback_url)
1065-
code = parsed.query.split("=")[1]
10661064
10671065
# Step 4: Exchange code for tokens
1068-
tokens = auth.fetch_token(authorization_code=code)
1066+
tokens = auth.fetch_token(authorization_response=callback_url)
10691067
access_token = tokens["access_token"]
10701068
refresh_token = tokens["refresh_token"] # Store for renewal
10711069
10721070
# Step 5: Create client
1073-
client = Client(oauth2_access_token=access_token)
1071+
# Option 1: Use bearer_token (OAuth2 access tokens work as bearer tokens)
1072+
client = Client(bearer_token=access_token)
1073+
1074+
# Option 2: Pass the full token dict for automatic refresh support
1075+
# client = Client(token=tokens)
10741076
```
10751077
10761078
**Token Refresh** (automatic in SDK for long-lived sessions):
10771079
10781080
```python
10791081
# If access token expires, refresh using stored refresh_token
1080-
tokens = auth.refresh_token(refresh_token=refresh_token)
1081-
client = Client(oauth2_access_token=tokens["access_token"])
1082+
# The refresh_token method uses the stored token from the OAuth2PKCEAuth instance
1083+
tokens = auth.refresh_token()
1084+
# Use the refreshed token
1085+
client = Client(bearer_token=tokens["access_token"])
1086+
# Or pass the full token dict: client = Client(token=tokens)
10821087
```
10831088
10841089
### 3. OAuth 1.0a User Context

xdk-gen/templates/typescript/oauth2_auth.j2

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@ export class OAuth2 {
6464
state: state || ''
6565
});
6666

67-
// PKCE parameters are handled separately - not generated automatically
67+
// Add PKCE parameters if they've been set
68+
if (this.codeChallenge) {
69+
params.append('code_challenge', this.codeChallenge);
70+
params.append('code_challenge_method', 'S256');
71+
}
6872

6973
return `https://x.com/i/oauth2/authorize?${params.toString()}`;
7074
}

0 commit comments

Comments
 (0)