Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 102 additions & 4 deletions app/api/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,108 @@ async def get_current_user_from_clerk(
)
user = result.scalar_one_or_none()
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found",
)
# Fetch user data from Clerk API
# https://clerk.com/docs/reference/backend-api/tag/users/get/users/%7Buser_id%7D
if not CLERK_API_KEY:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Clerk API key not configured",
)

# Call Clerk API to get user info
url = f"{CLERK_API_URL}/users/{clerk_user_id}"

try:
async with aiohttp.ClientSession() as session:
async with session.get(url, headers={"Authorization": f"Bearer {CLERK_API_KEY}"}) as response:
response.raise_for_status()
user_data = await response.json()

# Extract email address
email = None
if user_data.get("primary_email_address_id") and user_data.get(
"email_addresses"
):
for email_obj in user_data.get("email_addresses", []):
if email_obj["id"] == user_data["primary_email_address_id"]:
email = email_obj.get("email_address")
break
if email is None:
raise ValueError("No email found in Clerk user data")

# Use email as username directly
username = email
except Exception as e:
logger.exception(f"Error fetching Clerk user data: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to fetch user data from Clerk",
)

# Check if user exists with this email
result = await db.execute(select(User).filter(User.email == email))
existing_user = result.scalar_one_or_none()
if existing_user:
# Link existing user to Clerk ID
try:
existing_user.clerk_user_id = clerk_user_id
await db.commit()
return existing_user
except IntegrityError:
# Another request might have already linked this user or created a new one
await db.rollback()
# Retry the query to get the user
result = await db.execute(
select(User).filter(User.clerk_user_id == clerk_user_id)
)
user = result.scalar_one_or_none()
if user:
return user
# If still no user, continue with creation attempt

# Create new user
try:
user = User(
email=email,
username=username,
clerk_user_id=clerk_user_id,
is_active=True,
hashed_password="", # Clerk handles authentication
)
db.add(user)
await db.commit()
await db.refresh(user)

# Create default TensorBlock provider for the new user
try:
await create_default_tensorblock_provider_for_user(user.id, db)
except Exception as e:
# Log error but don't fail user creation
logger.warning(
f"Failed to create default TensorBlock provider for user {user.id}: {e}"
)

return user
except IntegrityError as e:
# Handle race condition: another request might have created the user
await db.rollback()
if "users_clerk_user_id_key" in str(e) or "clerk_user_id" in str(e):
# Retry the query to get the user that was created by another request
result = await db.execute(
select(User).filter(User.clerk_user_id == clerk_user_id)
)
user = result.scalar_one_or_none()
if user:
return user
else:
# This shouldn't happen, but handle it gracefully
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to create or retrieve user due to database constraint",
)
else:
# Re-raise other integrity errors
raise

return user

Expand Down
Loading