Skip to content

Commit 9efd439

Browse files
committed
address review: move Instagram token exchange logic to fetch_token method
1 parent 7ae8c43 commit 9efd439

File tree

1 file changed

+60
-58
lines changed

1 file changed

+60
-58
lines changed

backend/aci/server/oauth2_manager.py

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,48 @@ async def create_authorization_url(
114114

115115
return str(authorization_url)
116116

117+
async def exchange_short_lived_token(self, short_lived_token: str) -> dict[str, Any]:
118+
"""
119+
Exchange short-lived access token for long-lived access token.
120+
This is specific to Instagram's API requirements.
121+
122+
Args:
123+
short_lived_token: The short-lived access token from the initial OAuth flow
124+
125+
Returns:
126+
Token response dictionary with long-lived access token
127+
"""
128+
if self.app_name != "INSTAGRAM":
129+
raise OAuth2Error("Token exchange is only supported for Instagram")
130+
131+
exchange_token_url = self.custom_data.get(
132+
"exchange_token_url", "https://graph.instagram.com/access_token"
133+
)
134+
135+
try:
136+
response = await self.oauth2_client.get(
137+
exchange_token_url,
138+
params={
139+
"grant_type": "ig_exchange_token",
140+
"client_secret": self.client_secret,
141+
"access_token": short_lived_token,
142+
},
143+
timeout=30.0,
144+
)
145+
response.raise_for_status()
146+
147+
token_data = cast(dict[str, Any], response.json())
148+
logger.info(
149+
f"Successfully exchanged short-lived token for long-lived token, app_name={self.app_name}"
150+
)
151+
return token_data
152+
153+
except Exception as e:
154+
logger.error(
155+
f"Failed to exchange short-lived token, app_name={self.app_name}, error={e}"
156+
)
157+
raise OAuth2Error("Failed to exchange short-lived token for long-lived token") from e
158+
117159
# TODO: some app may not support "code_verifier"?
118160
async def fetch_token(
119161
self,
@@ -143,6 +185,24 @@ async def fetch_token(
143185
scope=self.scope,
144186
),
145187
)
188+
# handle Instagram's special case - exchange short-lived token for long-lived token
189+
if self.app_name == "INSTAGRAM":
190+
if "access_token" in token:
191+
short_lived_token = token["access_token"]
192+
logger.info(
193+
f"Exchanging short-lived token for long-lived token, app_name={self.app_name}"
194+
)
195+
long_lived_token_response = await self.exchange_short_lived_token(
196+
short_lived_token
197+
)
198+
# Update data with long-lived token response: add expires_in and token_type, update access_token
199+
token.update(long_lived_token_response)
200+
else:
201+
logger.error(
202+
f"Missing access_token in Instagram OAuth response, app={self.app_name}"
203+
)
204+
raise OAuth2Error("Missing access_token in Instagram OAuth response")
205+
# return the token response with long-lived access token
146206
return token
147207
except Exception as e:
148208
logger.error(f"Failed to fetch access token, app_name={self.app_name}, error={e}")
@@ -164,48 +224,6 @@ async def refresh_token(
164224
logger.error(f"Failed to refresh access token, app_name={self.app_name}, error={e}")
165225
raise OAuth2Error("Failed to refresh access token") from e
166226

167-
async def exchange_short_lived_token(self, short_lived_token: str) -> dict[str, Any]:
168-
"""
169-
Exchange short-lived access token for long-lived access token.
170-
This is specific to Instagram's API requirements.
171-
172-
Args:
173-
short_lived_token: The short-lived access token from the initial OAuth flow
174-
175-
Returns:
176-
Token response dictionary with long-lived access token
177-
"""
178-
if self.app_name != "INSTAGRAM":
179-
raise OAuth2Error("Token exchange is only supported for Instagram")
180-
181-
exchange_token_url = self.custom_data.get(
182-
"exchange_token_url", "https://graph.instagram.com/access_token"
183-
)
184-
185-
try:
186-
response = await self.oauth2_client.get(
187-
exchange_token_url,
188-
params={
189-
"grant_type": "ig_exchange_token",
190-
"client_secret": self.client_secret,
191-
"access_token": short_lived_token,
192-
},
193-
timeout=30.0,
194-
)
195-
response.raise_for_status()
196-
197-
token_data = cast(dict[str, Any], response.json())
198-
logger.info(
199-
f"Successfully exchanged short-lived token for long-lived token, app_name={self.app_name}"
200-
)
201-
return token_data
202-
203-
except Exception as e:
204-
logger.error(
205-
f"Failed to exchange short-lived token, app_name={self.app_name}, error={e}"
206-
)
207-
raise OAuth2Error("Failed to exchange short-lived token for long-lived token") from e
208-
209227
async def parse_fetch_token_response(self, token: dict) -> OAuth2SchemeCredentials:
210228
"""
211229
Parse OAuth2SchemeCredentials from token response with app-specific handling.
@@ -226,22 +244,6 @@ async def parse_fetch_token_response(self, token: dict) -> OAuth2SchemeCredentia
226244
logger.error(f"Missing authed_user in Slack OAuth response, app={self.app_name}")
227245
raise OAuth2Error("Missing access_token in Slack OAuth response")
228246

229-
# handle Instagram's special case - exchange short-lived token for long-lived token
230-
if self.app_name == "INSTAGRAM":
231-
if "access_token" in data:
232-
short_lived_token = data["access_token"]
233-
logger.info(
234-
f"Exchanging short-lived token for long-lived token, app_name={self.app_name}"
235-
)
236-
long_lived_token_response = await self.exchange_short_lived_token(short_lived_token)
237-
# Update data with long-lived token response: add expires_in and token_type, update access_token
238-
data.update(long_lived_token_response)
239-
else:
240-
logger.error(
241-
f"Missing access_token in Instagram OAuth response, app={self.app_name}"
242-
)
243-
raise OAuth2Error("Missing access_token in Instagram OAuth response")
244-
245247
if "access_token" not in data:
246248
logger.error(f"Missing access_token in OAuth response, app={self.app_name}")
247249
raise OAuth2Error("Missing access_token in OAuth response")

0 commit comments

Comments
 (0)