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 @@
- - - -
diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index d392e9f77..2dc2a92b2 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -1,6 +1,7 @@ @@ -90,100 +94,118 @@ -->
-
-
{ - submitHandler(); - }} - > -
- {mode === 'signin' ? $i18n.t('Sign in') : $i18n.t('Sign up')} - {$i18n.t('to')} - {$WEBUI_NAME} -
- - {#if mode === 'signup'} -
- ⓘ {$WEBUI_NAME} - {$i18n.t( - 'does not make any external connections, and your data stays securely on your locally hosted server.' - )} + {#if $config?.trusted_header_auth ?? false} +
+
+
+ {$i18n.t('Signing in')} + {$i18n.t('to')} + {$WEBUI_NAME} +
+ +
+ +
+
+
+ {:else} +
+ { + submitHandler(); + }} + > +
+ {mode === 'signin' ? $i18n.t('Sign in') : $i18n.t('Sign up')} + {$i18n.t('to')} + {$WEBUI_NAME}
- {/if} -
{#if mode === 'signup'} -
-
{$i18n.t('Name')}
+
+ ⓘ {$WEBUI_NAME} + {$i18n.t( + 'does not make any external connections, and your data stays securely on your locally hosted server.' + )} +
+ {/if} + +
+ {#if mode === 'signup'} +
+
{$i18n.t('Name')}
+ +
+ +
+ {/if} + +
+
{$i18n.t('Email')}
-
- {/if} - -
-
{$i18n.t('Email')}
- +
+
{$i18n.t('Password')}
+ +
-
-
{$i18n.t('Password')}
- -
-
- -
- - -
- {mode === 'signin' - ? $i18n.t("Don't have an account?") - : $i18n.t('Already have an account?')} - +
+ +
+ {mode === 'signin' + ? $i18n.t("Don't have an account?") + : $i18n.t('Already have an account?')} + + +
-
- -
+ +
+ {/if}
{/if}