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 b26236ef8..26934d6e2 100644 --- a/backend/apps/web/models/auths.py +++ b/backend/apps/web/models/auths.py @@ -122,6 +122,17 @@ 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..c1a1121b3 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,16 @@ 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( + ) + 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}, @@ -138,6 +150,17 @@ async def signup(request: Request, form_data: SignupForm): if Users.get_user_by_email(form_data.email.lower()): raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN) + 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 trusted_email != form_data.email: + raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_MISMATCH) + # TODO: Yolo hack to assign a password + form_data.password = str(uuid.uuid4()) + try: role = ( "admin" diff --git a/backend/config.py b/backend/config.py index 27311face..41269488e 100644 --- a/backend/config.py +++ b/backend/config.py @@ -348,6 +348,8 @@ 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 d4b67079c..85514e431 100644 --- a/backend/main.py +++ b/backend/main.py @@ -171,6 +171,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/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index d392e9f77..a72f0e055 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -15,6 +15,8 @@ let email = ''; let password = ''; + let showPasswordField = !($config?.trusted_header_auth ?? false); + const setSessionUser = async (sessionUser) => { if (sessionUser) { console.log(sessionUser); @@ -141,17 +143,19 @@ /> -
-
{$i18n.t('Password')}
- -
+ {#if showPasswordField} +
+
{$i18n.t('Password')}
+ +
+ {/if}