feat: Implement OAuth2 authentication with Google and GitHub
- Added OAuth2 endpoints for Google and GitHub authentication. - Created OAuth service to handle provider interactions and user info retrieval. - Implemented user OAuth repository for managing user OAuth links in the database. - Updated auth service to support linking existing users and creating new users via OAuth. - Added CORS middleware to allow frontend access. - Created tests for OAuth endpoints and service functionality. - Introduced environment configuration for OAuth client IDs and secrets. - Added logging for OAuth operations and error handling.
This commit is contained in:
@@ -54,8 +54,6 @@ async def register(
|
||||
samesite=settings.COOKIE_SAMESITE,
|
||||
)
|
||||
|
||||
# Return only user data, tokens are now in cookies
|
||||
return auth_response.user
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
@@ -64,6 +62,8 @@ async def register(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Registration failed",
|
||||
) from e
|
||||
else:
|
||||
return auth_response.user
|
||||
|
||||
|
||||
@router.post("/login")
|
||||
@@ -101,8 +101,6 @@ async def login(
|
||||
samesite=settings.COOKIE_SAMESITE,
|
||||
)
|
||||
|
||||
# Return only user data, tokens are now in cookies
|
||||
return auth_response.user
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
@@ -111,6 +109,8 @@ async def login(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Login failed",
|
||||
) from e
|
||||
else:
|
||||
return auth_response.user
|
||||
|
||||
|
||||
@router.get("/me")
|
||||
@@ -156,7 +156,6 @@ async def refresh_token(
|
||||
samesite=settings.COOKIE_SAMESITE,
|
||||
)
|
||||
|
||||
return {"message": "Token refreshed successfully"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
@@ -165,6 +164,8 @@ async def refresh_token(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Token refresh failed",
|
||||
) from e
|
||||
else:
|
||||
return {"message": "Token refreshed successfully"}
|
||||
|
||||
|
||||
@router.post("/logout")
|
||||
@@ -176,7 +177,7 @@ async def logout(
|
||||
) -> dict[str, str]:
|
||||
"""Logout endpoint - clears cookies and revokes refresh token."""
|
||||
user = None
|
||||
|
||||
|
||||
# Try to get user from access token first
|
||||
if access_token:
|
||||
try:
|
||||
@@ -188,7 +189,7 @@ async def logout(
|
||||
logger.info("Found user from access token: %s", user.email)
|
||||
except (HTTPException, Exception) as e:
|
||||
logger.info("Access token validation failed: %s", str(e))
|
||||
|
||||
|
||||
# If no user found, try refresh token
|
||||
if not user and refresh_token:
|
||||
try:
|
||||
@@ -200,14 +201,14 @@ async def logout(
|
||||
logger.info("Found user from refresh token: %s", user.email)
|
||||
except (HTTPException, Exception) as e:
|
||||
logger.info("Refresh token validation failed: %s", str(e))
|
||||
|
||||
|
||||
# If we found a user, revoke their refresh token
|
||||
if user:
|
||||
await auth_service.revoke_refresh_token(user)
|
||||
logger.info("Successfully revoked refresh token for user: %s", user.email)
|
||||
else:
|
||||
logger.info("No user found, skipping token revocation")
|
||||
|
||||
|
||||
# Always clear both cookies regardless of token validity
|
||||
response.delete_cookie(
|
||||
key="access_token",
|
||||
@@ -221,5 +222,5 @@ async def logout(
|
||||
secure=settings.COOKIE_SECURE,
|
||||
samesite=settings.COOKIE_SAMESITE,
|
||||
)
|
||||
|
||||
|
||||
return {"message": "Successfully logged out"}
|
||||
|
||||
Reference in New Issue
Block a user