Skip to content

Commit cc61e1b

Browse files
committed
different usernames
1 parent 11daa72 commit cc61e1b

5 files changed

Lines changed: 132 additions & 184 deletions

File tree

papers2code-ui/src/features/profile/ProfilePage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ const ProfilePage: React.FC = () => {
364364
</div>
365365

366366
{/* Social Links */}
367-
{(userDetails.websiteUrl || userDetails.twitterProfileUrl || userDetails.linkedinProfileUrl || userDetails.blueskyUsername || userDetails.huggingfaceUsername || userDetails.username) && (
367+
{(userDetails.websiteUrl || userDetails.twitterProfileUrl || userDetails.linkedinProfileUrl || userDetails.blueskyUsername || userDetails.huggingfaceUsername || userDetails.githubId) && (
368368
<div className="flex gap-3 justify-center md:justify-start">
369369
{userDetails.websiteUrl && (
370370
<a
@@ -377,9 +377,9 @@ const ProfilePage: React.FC = () => {
377377
<Globe size={16} />
378378
</a>
379379
)}
380-
{userDetails.username && (
380+
{userDetails.githubId && userDetails.githubUsername && (
381381
<a
382-
href={`https://github.com/${userDetails.username}`}
382+
href={`https://github.com/${userDetails.githubUsername}`}
383383
target="_blank"
384384
rel="noopener noreferrer"
385385
className="p-2 bg-muted hover:bg-muted/80 rounded-md transition-colors"

papers2code-ui/src/shared/types/user.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export interface UserProfile {
2929
email?: string | null;
3030
githubId?: number | null;
3131
googleId?: string | null;
32+
githubUsername?: string | null;
33+
googleUsername?: string | null;
3234
}
3335

3436
export interface PaperActionUserProfile {

papers2code_app2/schemas/minimal.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class UserSchema(BaseModel): # Model returned by get_current_user
1313
github_id: Optional[int] = Field(None, alias='githubId') # Made optional to support Google OAuth
1414
google_id: Optional[str] = Field(None, alias='googleId') # Added for Google OAuth
1515
username: str
16+
github_username: Optional[str] = Field(None, alias='githubUsername') # Provider-specific username
17+
google_username: Optional[str] = Field(None, alias='googleUsername') # Provider-specific username
1618
email: Optional[str] = None # Added for Google OAuth users
1719
name: Optional[str] = None
1820
avatar_url: Optional[str] = Field(None, alias='avatarUrl') # Primary avatar URL (computed based on preference)

papers2code_app2/services/github_oauth_service.py

Lines changed: 63 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -189,105 +189,79 @@ async def handle_github_callback(
189189

190190
current_time = datetime.now(timezone.utc)
191191

192-
# Check if user already exists by email (for account linking)
193-
existing_user_by_email = None
194-
if email:
195-
existing_user_by_email = await self.users_collection.find_one({
196-
"email": email,
197-
"googleId": {"$exists": True},
198-
"githubId": {"$exists": False}
199-
})
200-
201-
if existing_user_by_email:
202-
# User exists via Google, potential account linking scenario
203-
logger.info(f"Found existing Google user with matching email: {email}")
204-
205-
# Store pending linking data in a temporary JWT token
206-
import base64
207-
import json
192+
# No automatic account linking by email - users must manually link via settings
193+
# Normal GitHub user creation/update
194+
try:
195+
# Ensure username uniqueness by checking and appending numbers if needed
196+
# This prevents conflicts when a Google user already has this username
197+
base_username = username
198+
counter = 1
199+
while await self.users_collection.find_one({"username": username, "githubId": {"$ne": github_user_id}}):
200+
username = f"{base_username}{counter}"
201+
counter += 1
208202

209-
pending_data = {
210-
"existing_user_id": str(existing_user_by_email["_id"]),
211-
"existing_username": existing_user_by_email.get("username"),
212-
"existing_avatar": existing_user_by_email.get("googleAvatarUrl"),
203+
set_payload = {
204+
"name": name,
205+
"githubAvatarUrl": avatar_url, # Store GitHub avatar separately
206+
"email": email,
213207
"githubId": github_user_id,
214-
"github_username": username,
215-
"github_name": name,
216-
"github_email": email,
217-
"github_avatar": avatar_url,
218-
"github_token": github_token,
219-
"exp": (datetime.now(timezone.utc) + timedelta(minutes=10)).timestamp()
208+
"githubUsername": username, # Store provider-specific username
209+
"githubAccessToken": github_token, # Store the token for API calls
210+
"updatedAt": current_time,
211+
"lastLoginAt": current_time,
220212
}
221-
222-
pending_token = jwt.encode(pending_data, config_settings.FLASK_SECRET_KEY, algorithm=config_settings.ALGORITHM)
223-
224-
# Redirect to frontend with pending_link query parameter
225-
frontend_url = f"{frontend_url}?pending_link={pending_token}"
226-
227-
redirect_response = RedirectResponse(url=frontend_url)
228-
return redirect_response
229-
else:
230-
# Normal GitHub user creation/update
231-
try:
232-
set_payload = {
233-
"name": name,
234-
"githubAvatarUrl": avatar_url, # Store GitHub avatar separately
235-
"email": email,
236-
"githubId": github_user_id,
237-
"githubAccessToken": github_token, # Store the token for API calls
238-
"updatedAt": current_time,
239-
"lastLoginAt": current_time,
240-
}
241213

242-
set_on_insert_payload = {
243-
"username": username,
244-
"createdAt": current_time,
245-
"isAdmin": False,
246-
# Set default privacy settings for new users
247-
"showEmail": True,
248-
"showGithub": True,
249-
"preferredAvatarSource": "github", # Default to GitHub avatar
250-
}
214+
set_on_insert_payload = {
215+
"username": username,
216+
"createdAt": current_time,
217+
"isAdmin": False,
218+
# Set default privacy settings for new users
219+
"showEmail": True,
220+
"showGithub": True,
221+
"preferredAvatarSource": "github", # Default to GitHub avatar
222+
}
251223

252-
user_document = await self.users_collection.find_one_and_update(
253-
{"username": username},
254-
{
255-
"$set": set_payload,
256-
"$setOnInsert": set_on_insert_payload
257-
},
258-
upsert=True,
259-
return_document=ReturnDocument.AFTER
224+
# Match by githubId ONLY to prevent automatic account merging
225+
user_document = await self.users_collection.find_one_and_update(
226+
{"githubId": github_user_id},
227+
{
228+
"$set": set_payload,
229+
"$setOnInsert": set_on_insert_payload
230+
},
231+
upsert=True,
232+
return_document=ReturnDocument.AFTER
233+
)
234+
235+
# Compute primary avatar_url based on preference
236+
preferred_source = user_document.get("preferredAvatarSource", "github")
237+
if preferred_source == "google" and user_document.get("googleAvatarUrl"):
238+
computed_avatar = user_document.get("googleAvatarUrl")
239+
else:
240+
computed_avatar = user_document.get("githubAvatarUrl")
241+
242+
# Update with computed avatar_url
243+
if computed_avatar:
244+
await self.users_collection.update_one(
245+
{"_id": user_document["_id"]},
246+
{"$set": {"avatarUrl": computed_avatar}}
260247
)
261-
262-
# Compute primary avatar_url based on preference
263-
preferred_source = user_document.get("preferredAvatarSource", "github")
264-
if preferred_source == "google" and user_document.get("googleAvatarUrl"):
265-
computed_avatar = user_document.get("googleAvatarUrl")
266-
else:
267-
computed_avatar = user_document.get("githubAvatarUrl")
268-
269-
# Update with computed avatar_url
270-
if computed_avatar:
271-
await self.users_collection.update_one(
272-
{"_id": user_document["_id"]},
273-
{"$set": {"avatarUrl": computed_avatar}}
274-
)
275-
user_document["avatarUrl"] = computed_avatar
276-
277-
if not user_document:
278-
logger.error("GitHubOAuthService: Failed to upsert user document, find_one_and_update returned None unexpectedly.")
279-
return RedirectResponse(url=f"{frontend_url}/?login_error=database_user_op_failed", status_code=307)
280-
281-
logger.info(f"GitHubOAuthService: User {username} (DB ID: {user_document['_id']}, GitHub ID: {user_document.get('github_id')}) upserted successfully.")
248+
user_document["avatarUrl"] = computed_avatar
249+
250+
if not user_document:
251+
logger.error("GitHubOAuthService: Failed to upsert user document, find_one_and_update returned None unexpectedly.")
252+
return RedirectResponse(url=f"{frontend_url}/?login_error=database_user_op_failed", status_code=307)
253+
254+
logger.info(f"GitHubOAuthService: User {user_document.get('username')} (DB ID: {user_document['_id']}, GitHub ID: {user_document.get('githubId')}) upserted successfully.")
282255

283-
except Exception as db_exc:
284-
logger.error(f"Database operation error during user upsert: {db_exc}")
285-
return RedirectResponse(url=f"{frontend_url}/?login_error=database_user_op_generic_error", status_code=307)
256+
except Exception as db_exc:
257+
logger.error(f"Database operation error during user upsert: {db_exc}")
258+
return RedirectResponse(url=f"{frontend_url}/?login_error=database_user_op_generic_error", status_code=307)
286259

287260
user_id_str = str(user_document["_id"])
261+
username_from_db = user_document["username"]
288262
access_token_payload = {
289263
"sub": user_id_str,
290-
"username": username,
264+
"username": username_from_db,
291265
"githubId": github_user_id,
292266
}
293267
access_token = create_access_token(data=access_token_payload)
@@ -324,5 +298,5 @@ async def handle_github_callback(
324298
path="/",
325299
secure=True if config_settings.ENV_TYPE == "production" else False
326300
)
327-
logger.info(f"Successfully authenticated user {username}. Redirecting to frontend. Cookies being set: Access, Refresh, CSRF.")
301+
logger.info(f"Successfully authenticated user {username_from_db}. Redirecting to frontend. Cookies being set: Access, Refresh, CSRF.")
328302
return redirect_response

0 commit comments

Comments
 (0)