mirror of
				https://github.com/open-webui/open-webui
				synced 2025-06-26 18:26:48 +00:00 
			
		
		
		
	feat: add WEBUI_AUTH_TRUSTED_EMAIL_HEADER for authenticating users by a trusted header
This is very yolo code, use at your own risk
This commit is contained in:
		
							parent
							
								
									7d45d2762f
								
							
						
					
					
						commit
						29f13f34d3
					
				| @ -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 | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 = ( | ||||
|  | ||||
| @ -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), | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -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 @@ | ||||
| 							/> | ||||
| 						</div> | ||||
| 
 | ||||
| 						<div> | ||||
| 							<div class=" text-sm font-semibold text-left mb-1">{$i18n.t('Password')}</div> | ||||
| 							<input | ||||
| 								bind:value={password} | ||||
| 								type="password" | ||||
| 								class=" border px-4 py-2.5 rounded-2xl w-full text-sm" | ||||
| 								placeholder={$i18n.t('Enter Your Password')} | ||||
| 								autocomplete="current-password" | ||||
| 								required | ||||
| 							/> | ||||
| 						</div> | ||||
| 						{#if showPasswordField} | ||||
| 							<div> | ||||
| 								<div class=" text-sm font-semibold text-left mb-1">{$i18n.t('Password')}</div> | ||||
| 								<input | ||||
| 									bind:value={password} | ||||
| 									type="password" | ||||
| 									class=" border px-4 py-2.5 rounded-2xl w-full text-sm" | ||||
| 									placeholder={$i18n.t('Enter Your Password')} | ||||
| 									autocomplete="current-password" | ||||
| 									required | ||||
| 								/> | ||||
| 							</div> | ||||
| 						{/if} | ||||
| 					</div> | ||||
| 
 | ||||
| 					<div class="mt-5"> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user