From c2107e5bb3689a69c170ca526925f4ed84bd00f5 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 10 Aug 2026 22:36:42 -0600 Subject: [PATCH] refac --- backend/open_webui/main.py | 4 +-- backend/open_webui/routers/auths.py | 5 ++++ backend/open_webui/utils/auth.py | 33 +++++++++++++++++++++ backend/open_webui/utils/oauth.py | 46 ++++++++++++++++++++++++++--- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 6269315d32..725c9a20d7 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -2649,7 +2649,7 @@ async def oauth_client_authorize( detail='OAuth client registration is still invalid after re-registration', ) - return await oauth_client_manager.handle_authorize(request, client_id=client_id) + return await oauth_client_manager.handle_authorize(request, client_id=client_id, user_id=user.id) @app.get('/oauth/clients/{client_id}/callback') @@ -2657,12 +2657,10 @@ async def oauth_client_callback( client_id: str, request: Request, response: Response, - user=Depends(get_verified_user), ): return await oauth_client_manager.handle_callback( request, client_id=client_id, - user_id=user.id if user else None, response=response, ) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 9168240d47..a907cb4e1c 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -966,6 +966,11 @@ async def signout(request: Request, response: Response, db: AsyncSession = Depen ) response.delete_cookie('token') + try: + request.session.clear() + except Exception: + pass + response.delete_cookie('owui-session') response.delete_cookie('oui-session') response.delete_cookie('oauth_id_token') diff --git a/backend/open_webui/utils/auth.py b/backend/open_webui/utils/auth.py index 42d6c77dfa..0443a34cbb 100644 --- a/backend/open_webui/utils/auth.py +++ b/backend/open_webui/utils/auth.py @@ -519,6 +519,39 @@ async def get_verified_user_by_token(token: str, redis=None): return user +async def get_verified_user_by_id(user_id: str | None): + if not user_id: + return None + + user = await Users.get_user_by_id(user_id) + if user is None or user.role not in VERIFIED_USER_ROLES: + return None + + return user + + +async def get_optional_verified_user_from_request(request: Request): + token = None + auth_token = get_http_authorization_cred(request.headers.get('Authorization')) + if auth_token: + token = auth_token.credentials + if token is None: + token = request.cookies.get('token') + if token is None and getattr(request.state, 'token', None): + token = request.state.token.credentials + if not token: + return None + + try: + if token.startswith('sk-'): + user = await get_current_user_by_api_key(request, token) + return user if user.role in VERIFIED_USER_ROLES else None + + return await get_verified_user_by_token(token, getattr(request.app.state, 'redis', None)) + except HTTPException: + return None + + def get_admin_user(user=Depends(get_current_user)): if user.role != 'admin': raise HTTPException( diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 2ef1bee69f..a6180e1b50 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -84,7 +84,12 @@ from open_webui.models.groups import GroupForm, GroupModel, Groups, GroupUpdateF from open_webui.models.oauth_sessions import OAuthSessions from open_webui.models.users import Users from open_webui.retrieval.web.utils import get_ssrf_safe_session, validate_url -from open_webui.utils.auth import create_token, get_password_hash +from open_webui.utils.auth import ( + create_token, + get_password_hash, + get_optional_verified_user_from_request, + get_verified_user_by_id, +) from open_webui.utils.groups import apply_default_group_assignment from open_webui.utils.misc import parse_duration from open_webui.utils.validate import validate_profile_image_url @@ -1167,7 +1172,7 @@ class OAuthClientManager: log.error(f'Exception during token refresh for client_id {client_id}: {e}') return None - async def handle_authorize(self, request, client_id: str) -> RedirectResponse: + async def handle_authorize(self, request, client_id: str, user_id: str) -> RedirectResponse: client = await self.get_client(client_id) if client is None: raise HTTPException(404) @@ -1183,7 +1188,15 @@ class OAuthClientManager: # Pass explicit scope/resource parameters for providers that require them. kwargs = build_oauth_request_params(client_info) try: - return await client.authorize_redirect(request, redirect_uri_str, **kwargs) + auth_data = await client.create_authorization_url(redirect_uri_str, **kwargs) + if not auth_data.get('state'): + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail='OAuth authorization state was not generated', + ) + auth_data['user_id'] = user_id + await client.save_authorize_data(request, redirect_uri=redirect_uri_str, **auth_data) + return RedirectResponse(auth_data['url'], status_code=302) except RuntimeError as e: # authlib raises RuntimeError('Missing "authorize_url" value') when the # authorization endpoint could not be resolved from server metadata. @@ -1199,14 +1212,36 @@ class OAuthClientManager: ), ) - async def handle_callback(self, request, client_id: str, user_id: str, response): + async def handle_callback(self, request, client_id: str, response): client = await self.get_client(client_id) if client is None: raise HTTPException(404) error_message = None + state = request.query_params.get('state') + user_id = None try: client_info = await self.get_client_info(client_id) + state_data = await client.framework.get_state_data(request.session, state) if state else None + user_id = state_data.get('user_id') if state_data else None + if not user_id: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail='OAuth callback state is invalid or expired', + ) + + if not await get_verified_user_by_id(user_id): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail='OAuth callback user is not authorized', + ) + + request_user = await get_optional_verified_user_from_request(request) + if request_user and request_user.id != user_id: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail='OAuth callback user does not match authenticated session', + ) # Note: Do NOT pass client_id/client_secret explicitly here. # The Authlib client already has these configured during add_client(). @@ -1257,6 +1292,9 @@ class OAuthClientManager: error_message, exc_info=True, ) + finally: + if state and client is not None: + await client.framework.clear_state_data(request.session, state) webui_url = await Config.get('webui.url') redirect_url = (str(webui_url or request.base_url)).rstrip('/')