From 29f13f34d3b58371dde8a8fcd11bf191fe11e5cd Mon Sep 17 00:00:00 2001 From: Jun Siang Cheah Date: Tue, 26 Mar 2024 21:30:53 +0000 Subject: [PATCH 1/6] feat: add WEBUI_AUTH_TRUSTED_EMAIL_HEADER for authenticating users by a trusted header This is very yolo code, use at your own risk --- Dockerfile | 1 + backend/apps/web/main.py | 3 ++- backend/apps/web/models/auths.py | 11 +++++++++++ backend/apps/web/routers/auths.py | 25 ++++++++++++++++++++++++- backend/config.py | 2 ++ backend/constants.py | 2 ++ backend/main.py | 1 + src/routes/auth/+page.svelte | 26 +++++++++++++++----------- 8 files changed, 58 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index de501838..5f0c13cb 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 dd5c0c70..66cdfb3d 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 b26236ef..26934d6e 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 d881ec74..c1a1121b 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 27311fac..41269488 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 8bcdd078..f8daf338 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 d4b67079..85514e43 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 d392e9f7..a72f0e05 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}
From 50f6addd6f0d5374972962a36da73dc87ba68f69 Mon Sep 17 00:00:00 2001 From: Jun Siang Cheah Date: Thu, 28 Mar 2024 10:34:57 +0000 Subject: [PATCH 2/6] feat: auto signup/login with WEBUI_AUTH_TRUSTED_EMAIL_HEADER --- backend/apps/web/routers/auths.py | 16 +++--------- src/routes/auth/+page.svelte | 41 +++++++++++++++++++------------ 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/backend/apps/web/routers/auths.py b/backend/apps/web/routers/auths.py index c1a1121b..822a6757 100644 --- a/backend/apps/web/routers/auths.py +++ b/backend/apps/web/routers/auths.py @@ -105,8 +105,9 @@ async def signin(request: Request, form_data: SigninForm): 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( - ) + 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(), @@ -150,17 +151,6 @@ 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/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index a72f0e05..6b662946 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -15,8 +15,6 @@ let email = ''; let password = ''; - let showPasswordField = !($config?.trusted_header_auth ?? false); - const setSessionUser = async (sessionUser) => { if (sessionUser) { console.log(sessionUser); @@ -58,6 +56,9 @@ await goto('/'); } loaded = true; + if ($config?.trusted_header_auth ?? false) { + await signInHandler(); + } }); @@ -92,7 +93,16 @@
-->
-
+ {#if ($config?.trusted_header_auth ?? false)} +
+
+ {$i18n.t('Signing in')} + {$i18n.t('to')} + {$WEBUI_NAME} +
+
+ {:else} +
{ @@ -143,19 +153,17 @@ />
- {#if showPasswordField} -
-
{$i18n.t('Password')}
- -
- {/if} +
+
{$i18n.t('Password')}
+ +
@@ -188,6 +196,7 @@
+ {/if} {/if} From 047c9fe82c0e9ec4e38234315e2144bc13f5f408 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Fri, 29 Mar 2024 13:02:38 -0700 Subject: [PATCH 3/6] fix: styling --- src/lib/components/common/Spinner.svelte | 37 ++--- src/routes/auth/+page.svelte | 175 ++++++++++++----------- 2 files changed, 111 insertions(+), 101 deletions(-) diff --git a/src/lib/components/common/Spinner.svelte b/src/lib/components/common/Spinner.svelte index 206c7f5c..4b7f5e39 100644 --- a/src/lib/components/common/Spinner.svelte +++ b/src/lib/components/common/Spinner.svelte @@ -1,24 +1,25 @@
- - - -
diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index 6b662946..2dc2a92b 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -1,6 +1,7 @@