Refactor Authentication (#8040)

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: rohitvinodmalhotra@gmail.com <rohitvinodmalhotra@gmail.com>
This commit is contained in:
tofarr
2025-04-24 18:49:41 -06:00
committed by GitHub
parent 9b1aaa53fe
commit c5245a622d
19 changed files with 931 additions and 851 deletions

View File

@@ -0,0 +1,48 @@
from fastapi import Request
from pydantic import SecretStr
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
from openhands.integrations.service_types import ProviderType
from openhands.server.settings import Settings
from openhands.server.user_auth.user_auth import get_user_auth
from openhands.storage.settings.settings_store import SettingsStore
async def get_provider_tokens(request: Request) -> PROVIDER_TOKEN_TYPE | None:
user_auth = await get_user_auth(request)
provider_tokens = await user_auth.get_provider_tokens()
return provider_tokens
async def get_access_token(request: Request) -> SecretStr | None:
user_auth = await get_user_auth(request)
access_token = await user_auth.get_access_token()
return access_token
async def get_user_id(request: Request) -> str | None:
user_auth = await get_user_auth(request)
user_id = await user_auth.get_user_id()
return user_id
async def get_github_user_id(request: Request) -> str | None:
provider_tokens = await get_provider_tokens(request)
if not provider_tokens:
return None
github_provider = provider_tokens.get(ProviderType.GITHUB)
if github_provider:
return github_provider.user_id
return None
async def get_user_settings(request: Request) -> Settings | None:
user_auth = await get_user_auth(request)
user_settings = await user_auth.get_user_settings()
return user_settings
async def get_user_settings_store(request: Request) -> SettingsStore | None:
user_auth = await get_user_auth(request)
user_settings_store = await user_auth.get_user_settings_store()
return user_settings_store

View File

@@ -0,0 +1,57 @@
from dataclasses import dataclass
from fastapi import Request
from pydantic import SecretStr
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
from openhands.server import shared
from openhands.server.settings import Settings
from openhands.server.user_auth.user_auth import UserAuth
from openhands.storage.settings.settings_store import SettingsStore
@dataclass
class DefaultUserAuth(UserAuth):
"""Default user authentication mechanism"""
_settings: Settings | None = None
_settings_store: SettingsStore | None = None
async def get_user_id(self) -> str | None:
"""The default implementation does not support multi tenancy, so user_id is always None"""
return None
async def get_access_token(self) -> SecretStr | None:
"""The default implementation does not support multi tenancy, so access_token is always None"""
return None
async def get_user_settings_store(self):
settings_store = self._settings_store
if settings_store:
return settings_store
user_id = await self.get_user_id()
settings_store = await shared.SettingsStoreImpl.get_instance(
shared.config, user_id
)
self._settings_store = settings_store
return settings_store
async def get_user_settings(self) -> Settings | None:
settings = self._settings
if settings:
return settings
settings_store = await self.get_user_settings_store()
settings = await settings_store.load()
self._settings = settings
return settings
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
settings = await self.get_user_settings()
secrets_store = getattr(settings, 'secrets_store', None)
provider_tokens = getattr(secrets_store, 'provider_tokens', None)
return provider_tokens
@classmethod
async def get_instance(cls, request: Request) -> UserAuth:
user_auth = DefaultUserAuth()
return user_auth

View File

@@ -0,0 +1,63 @@
from __future__ import annotations
import os
from abc import ABC, abstractmethod
from fastapi import Request
from pydantic import SecretStr
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
from openhands.server.settings import Settings
from openhands.server.shared import server_config
from openhands.storage.settings.settings_store import SettingsStore
from openhands.utils.import_utils import get_impl
class UserAuth(ABC):
"""Extensible class encapsulating user Authentication"""
_settings: Settings | None
@abstractmethod
async def get_user_id(self) -> str | None:
"""Get the unique identifier for the current user"""
@abstractmethod
async def get_access_token(self) -> SecretStr | None:
"""Get the access token for the current user"""
@abstractmethod
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
"""Get the provider tokens for the current user."""
@abstractmethod
async def get_user_settings_store(self) -> SettingsStore | None:
"""Get the settings store for the current user."""
async def get_user_settings(self) -> Settings | None:
"""Get the user settings for the current user"""
settings = self._settings
if settings:
return settings
settings_store = await self.get_user_settings_store()
if settings_store is None:
return None
settings = await settings_store.load()
self._settings = settings
return settings
@classmethod
@abstractmethod
async def get_instance(cls, request: Request) -> UserAuth:
"""Get an instance of UserAuth from the request given"""
async def get_user_auth(request: Request) -> UserAuth:
user_auth = getattr(request.state, 'user_auth', None)
if user_auth:
return user_auth
impl_name = server_config.user_auth_class
impl = get_impl(UserAuth, impl_name)
user_auth = await impl.get_instance(request)
request.state.user_auth = user_auth
return user_auth