diff --git a/Dockerfile b/Dockerfile index de501838f..5f0c13cb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ ENV OPENAI_API_BASE_URL "" ENV OPENAI_API_KEY "" ENV WEBUI_SECRET_KEY "" +ENV WEBUI_AUTH_TRUSTED_EMAIL_HEADER "" ENV SCARF_NO_ANALYTICS true ENV DO_NOT_TRACK true diff --git a/backend/apps/web/main.py b/backend/apps/web/main.py index dd5c0c704..66cdfb3d4 100644 --- a/backend/apps/web/main.py +++ b/backend/apps/web/main.py @@ -20,6 +20,7 @@ from config import ( ENABLE_SIGNUP, USER_PERMISSIONS, WEBHOOK_URL, + WEBUI_AUTH_TRUSTED_EMAIL_HEADER, ) app = FastAPI() @@ -34,7 +35,7 @@ app.state.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS app.state.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE app.state.USER_PERMISSIONS = USER_PERMISSIONS app.state.WEBHOOK_URL = WEBHOOK_URL - +app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER app.add_middleware( CORSMiddleware, diff --git a/backend/apps/web/models/auths.py b/backend/apps/web/models/auths.py index 75637700d..365958555 100644 --- a/backend/apps/web/models/auths.py +++ b/backend/apps/web/models/auths.py @@ -123,6 +123,16 @@ class AuthsTable: except: return None + def authenticate_user_by_trusted_header(self, email: str) -> Optional[UserModel]: + log.info(f"authenticate_user_by_trusted_header: {email}") + try: + auth = Auth.get(Auth.email == email, Auth.active == True) + if auth: + user = Users.get_user_by_id(auth.id) + return user + except: + return None + def update_user_password_by_id(self, id: str, new_password: str) -> bool: try: query = Auth.update(password=new_password).where(Auth.id == id) diff --git a/backend/apps/web/routers/auths.py b/backend/apps/web/routers/auths.py index d881ec746..e938a58f5 100644 --- a/backend/apps/web/routers/auths.py +++ b/backend/apps/web/routers/auths.py @@ -29,6 +29,7 @@ from utils.utils import ( from utils.misc import parse_duration, validate_email_format from utils.webhook import post_webhook from constants import ERROR_MESSAGES, WEBHOOK_MESSAGES +from config import WEBUI_AUTH_TRUSTED_EMAIL_HEADER router = APIRouter() @@ -79,6 +80,8 @@ async def update_profile( async def update_password( form_data: UpdatePasswordForm, session_user=Depends(get_current_user) ): + if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: + raise HTTPException(400, detail=ERROR_MESSAGES.ACTION_PROHIBITED) if session_user: user = Auths.authenticate_user(session_user.email, form_data.password) @@ -98,7 +101,22 @@ async def update_password( @router.post("/signin", response_model=SigninResponse) async def signin(request: Request, form_data: SigninForm): - user = Auths.authenticate_user(form_data.email.lower(), form_data.password) + if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: + if WEBUI_AUTH_TRUSTED_EMAIL_HEADER not in request.headers: + raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_TRUSTED_HEADER) + + trusted_email = request.headers[WEBUI_AUTH_TRUSTED_EMAIL_HEADER].lower() + if not Users.get_user_by_email(trusted_email.lower()): + await signup( + request, + SignupForm( + email=trusted_email, password=str(uuid.uuid4()), name=trusted_email + ), + ) + user = Auths.authenticate_user_by_trusted_header(trusted_email) + else: + user = Auths.authenticate_user(form_data.email.lower(), form_data.password) + if user: token = create_token( data={"id": user.id}, diff --git a/backend/config.py b/backend/config.py index 04778f9f0..7dd115a69 100644 --- a/backend/config.py +++ b/backend/config.py @@ -362,6 +362,9 @@ WEBUI_VERSION = os.environ.get("WEBUI_VERSION", "v1.0.0-alpha.100") #################################### WEBUI_AUTH = True +WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get( + "WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None +) #################################### # WEBUI_SECRET_KEY diff --git a/backend/constants.py b/backend/constants.py index 8bcdd0789..f8daf338d 100644 --- a/backend/constants.py +++ b/backend/constants.py @@ -20,6 +20,7 @@ class ERROR_MESSAGES(str, Enum): ENV_VAR_NOT_FOUND = "Required environment variable not found. Terminating now." CREATE_USER_ERROR = "Oops! Something went wrong while creating your account. Please try again later. If the issue persists, contact support for assistance." DELETE_USER_ERROR = "Oops! Something went wrong. We encountered an issue while trying to delete the user. Please give it another shot." + EMAIL_MISMATCH = "Uh-oh! This email does not match the email your provider is registered with. Please check your email and try again." EMAIL_TAKEN = "Uh-oh! This email is already registered. Sign in with your existing account or choose another email to start anew." USERNAME_TAKEN = ( "Uh-oh! This username is already registered. Please choose another username." @@ -36,6 +37,7 @@ class ERROR_MESSAGES(str, Enum): INVALID_PASSWORD = ( "The password provided is incorrect. Please check for typos and try again." ) + INVALID_TRUSTED_HEADER = "Your provider has not provided a trusted header. Please contact your administrator for assistance." UNAUTHORIZED = "401 Unauthorized" ACCESS_PROHIBITED = "You do not have permission to access this resource. Please contact your administrator for assistance." ACTION_PROHIBITED = ( diff --git a/backend/main.py b/backend/main.py index a4cb7f889..f2d2a1546 100644 --- a/backend/main.py +++ b/backend/main.py @@ -194,6 +194,7 @@ async def get_app_config(): "images": images_app.state.ENABLED, "default_models": webui_app.state.DEFAULT_MODELS, "default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS, + "trusted_header_auth": bool(webui_app.state.AUTH_TRUSTED_EMAIL_HEADER), } diff --git a/src/lib/components/common/Spinner.svelte b/src/lib/components/common/Spinner.svelte index 206c7f5ce..4b7f5e396 100644 --- a/src/lib/components/common/Spinner.svelte +++ b/src/lib/components/common/Spinner.svelte @@ -1,24 +1,25 @@