Repository: airtai/fastagency

Issue:
Importing the WebSurferTool class requires the fastagency[openapi] submodule to be installed.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/airtai/fastagency/pull/556?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) Report
Attention: Patch coverage is `48.71795%` with `20 lines` in your changes missing coverage. Please review.

| [Files with missing lines](https://app.codecov.io/gh/airtai/fastagency/pull/556?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) | Patch % | Lines |
|---|---|---|
| [fastagency/ui/mesop/auth/basic\_auth/basic\_auth.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=fastagency%2Fui%2Fmesop%2Fauth%2Fbasic_auth%2Fbasic_auth.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZmFzdGFnZW5jeS91aS9tZXNvcC9hdXRoL2Jhc2ljX2F1dGgvYmFzaWNfYXV0aC5weQ==) | 36.36% | [14 Missing :warning: ](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) |
| [...s\_src/user\_guide/ui/mesop/main\_mesop\_basic\_auth.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=docs%2Fdocs_src%2Fuser_guide%2Fui%2Fmesop%2Fmain_mesop_basic_auth.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZG9jcy9kb2NzX3NyYy91c2VyX2d1aWRlL3VpL21lc29wL21haW5fbWVzb3BfYmFzaWNfYXV0aC5weQ==) | 61.53% | [5 Missing :warning: ](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) |
| [...y/ui/mesop/auth/basic\_auth/basic\_auth\_component.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=fastagency%2Fui%2Fmesop%2Fauth%2Fbasic_auth%2Fbasic_auth_component.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZmFzdGFnZW5jeS91aS9tZXNvcC9hdXRoL2Jhc2ljX2F1dGgvYmFzaWNfYXV0aF9jb21wb25lbnQucHk=) | 75.00% | [1 Missing :warning: ](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) |

| [Files with missing lines](https://app.codecov.io/gh/airtai/fastagency/pull/556?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai) | Coverage Δ | |
|---|---|---|
| [...rc/user\_guide/ui/mesop/main\_mesop\_firebase\_auth.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=docs%2Fdocs_src%2Fuser_guide%2Fui%2Fmesop%2Fmain_mesop_firebase_auth.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZG9jcy9kb2NzX3NyYy91c2VyX2d1aWRlL3VpL21lc29wL21haW5fbWVzb3BfZmlyZWJhc2VfYXV0aC5weQ==) | `66.66% <ø> (ø)` | |
| [fastagency/ui/mesop/auth/auth.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=fastagency%2Fui%2Fmesop%2Fauth%2Fauth.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZmFzdGFnZW5jeS91aS9tZXNvcC9hdXRoL2F1dGgucHk=) | `100.00% <ø> (ø)` | |
| [...y/ui/mesop/auth/basic\_auth/basic\_auth\_component.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=fastagency%2Fui%2Fmesop%2Fauth%2Fbasic_auth%2Fbasic_auth_component.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZmFzdGFnZW5jeS91aS9tZXNvcC9hdXRoL2Jhc2ljX2F1dGgvYmFzaWNfYXV0aF9jb21wb25lbnQucHk=) | `75.00% <75.00%> (ø)` | |
| [...s\_src/user\_guide/ui/mesop/main\_mesop\_basic\_auth.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=docs%2Fdocs_src%2Fuser_guide%2Fui%2Fmesop%2Fmain_mesop_basic_auth.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZG9jcy9kb2NzX3NyYy91c2VyX2d1aWRlL3VpL21lc29wL21haW5fbWVzb3BfYmFzaWNfYXV0aC5weQ==) | `61.53% <61.53%> (ø)` | |
| [fastagency/ui/mesop/auth/basic\_auth/basic\_auth.py](https://app.codecov.io/gh/airtai/fastagency/pull/556?src=pr&el=tree&filepath=fastagency%2Fui%2Fmesop%2Fauth%2Fbasic_auth%2Fbasic_auth.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airtai#diff-ZmFzdGFnZW5jeS91aS9tZXNvcC9hdXRoL2Jhc2ljX2F1dGgvYmFzaWNfYXV0aC5weQ==) | `36.36% <36.36%> (ø)` | |


Patch:
diff --git a/docs/docs/SUMMARY.md b/docs/docs/SUMMARY.md
index cef3cdbd..88440b23 100644
--- a/docs/docs/SUMMARY.md
+++ b/docs/docs/SUMMARY.md
@@ -187,6 +187,13 @@ search:
                         - [AuthProtocol](api/fastagency/ui/mesop/auth/AuthProtocol.md)
                         - auth
                             - [AuthProtocol](api/fastagency/ui/mesop/auth/auth/AuthProtocol.md)
+                        - basic_auth
+                            - [BasicAuth](api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md)
+                            - [basic_auth_component](api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component.md)
+                            - basic_auth
+                                - [BasicAuth](api/fastagency/ui/mesop/auth/basic_auth/basic_auth/BasicAuth.md)
+                            - basic_auth_component
+                                - [basic_auth_component](api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component/basic_auth_component.md)
                         - firebase
                             - [FirebaseAuth](api/fastagency/ui/mesop/auth/firebase/FirebaseAuth.md)
                             - [FirebaseConfig](api/fastagency/ui/mesop/auth/firebase/FirebaseConfig.md)
diff --git a/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md
new file mode 100644
index 00000000..8deccf00
--- /dev/null
+++ b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md
@@ -0,0 +1,11 @@
+---
+# 0.5 - API
+# 2 - Release
+# 3 - Contributing
+# 5 - Template Page
+# 10 - Default
+search:
+  boost: 0.5
+---
+
+::: fastagency.ui.mesop.auth.basic_auth.BasicAuth
diff --git a/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth/BasicAuth.md b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth/BasicAuth.md
new file mode 100644
index 00000000..f1f3a08e
--- /dev/null
+++ b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth/BasicAuth.md
@@ -0,0 +1,11 @@
+---
+# 0.5 - API
+# 2 - Release
+# 3 - Contributing
+# 5 - Template Page
+# 10 - Default
+search:
+  boost: 0.5
+---
+
+::: fastagency.ui.mesop.auth.basic_auth.basic_auth.BasicAuth
diff --git a/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component.md b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component.md
new file mode 100644
index 00000000..4255179a
--- /dev/null
+++ b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component.md
@@ -0,0 +1,11 @@
+---
+# 0.5 - API
+# 2 - Release
+# 3 - Contributing
+# 5 - Template Page
+# 10 - Default
+search:
+  boost: 0.5
+---
+
+::: fastagency.ui.mesop.auth.basic_auth.basic_auth_component
diff --git a/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component/basic_auth_component.md b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component/basic_auth_component.md
new file mode 100644
index 00000000..51e08db7
--- /dev/null
+++ b/docs/docs/en/api/fastagency/ui/mesop/auth/basic_auth/basic_auth_component/basic_auth_component.md
@@ -0,0 +1,11 @@
+---
+# 0.5 - API
+# 2 - Release
+# 3 - Contributing
+# 5 - Template Page
+# 10 - Default
+search:
+  boost: 0.5
+---
+
+::: fastagency.ui.mesop.auth.basic_auth.basic_auth_component.basic_auth_component
diff --git a/docs/docs/en/user-guide/ui/mesop/basics.md b/docs/docs/en/user-guide/ui/mesop/basics.md
index 32cc4910..da27f2db 100644
--- a/docs/docs/en/user-guide/ui/mesop/basics.md
+++ b/docs/docs/en/user-guide/ui/mesop/basics.md
@@ -1,24 +1,29 @@
 # Mesop
 
-[**`MesopUI`**](../../../../api/fastagency/ui/mesop/MesopUI.md) in FastAgency offers a web-based interface for interacting with [**multi-agent workflows**](https://microsoft.github.io/autogen/0.2/docs/Use-Cases/agent_chat){target="_blank"}. Unlike the [**`ConsoleUI`**](../../../../api/fastagency/ui/console/ConsoleUI.md), which is text-based and runs in the command line, MesopUI provides a user-friendly browser interface, making it ideal for applications that need a more engaging, graphical interaction. MesopUI is perfect for building interactive web applications and enabling users to interact with agents in a more intuitive way.
+[**`MesopUI`**](../../../../api/fastagency/ui/mesop/MesopUI.md) in FastAgency offers a web-based interface for interacting with [**multi-agent workflows**](https://microsoft.github.io/autogen/0.2/docs/Use-Cases/agent_chat){target="_blank"}. Unlike the [**`ConsoleUI`**](../../../../api/fastagency/ui/console/ConsoleUI.md), which is text-based and runs in the command line, MesopUI provides a user-friendly browser interface, making it ideal for applications that need a more engaging and rich user experience.
 
-When creating a Mesop application, you can choose between two modes:
+When creating a [**Mesop**](https://google.github.io/mesop/){target="_blank"} application, you can choose between the following modes:
 
-- **Without Authentication**: Open access to all users.
-- **With Authentication**: Access restricted to authenticated users, using [**Firebase**](https://firebase.google.com){target="_blank"} as the authentication provider.
+- **No Authentication**: Open access to all users.
+- **Basic Authentication**: Simple username and password authentication for rapid prototyping. Not recommended for production.
+- **Firebase Authentication**: A more robust authentication mechanism that uses [**Firebase**](https://firebase.google.com){target="_blank"} as the provider for authenticating users.
 
     !!! note
-        Currently, [**Firebase**](https://firebase.google.com){target="_blank"} is the only supported authentication provider, with Google as the available sign-in method. Future releases will introduce more sign-in options within Firebase and expand support for additional authentication providers.
+        Currently, [**Firebase**](https://firebase.google.com){target="_blank"} authentication supports only Google as sign-in method. Future releases will introduce more sign-in options within Firebase.
 
 Below, we’ll walk through the steps to set up a basic student-teacher conversation with **[MesopUI](../../../../api/fastagency/ui/mesop/MesopUI.md)**, highlighting the process for adding authentication.
 
 ## Prerequisites
 
-=== "Without Authentication"
+=== "No Authentication"
 
     No prerequisites are required for this mode
 
-=== "With Authentication"
+=== "Basic Authentication"
+
+    No prerequisites are required for this mode
+
+=== "Firebase Authentication"
 
     To enable Firebase authentication,  follow these steps to set up your Firebase project and configure access:
 
@@ -77,13 +82,19 @@ You can setup the project using Cookiecutter by following the [**project setup g
 
 Alternatively, you can use **pip + venv**. To install **FastAgency** with MesopUI support, use the following command:
 
-=== "Without Authentication"
+=== "No Authentication"
 
     ```bash
     pip install "fastagency[autogen,mesop]"
     ```
 
-=== "With Authentication"
+=== "Basic Authentication"
+
+    ```bash
+    pip install "fastagency[autogen,mesop,basic_auth]"
+    ```
+
+=== "Firebase Authentication"
 
     ```bash
     pip install "fastagency[autogen,mesop,firebase]"
@@ -125,9 +136,11 @@ You can pass a custom [SecurityPolicy](https://google.github.io/mesop/api/page/#
 ui = MesopUI(security_policy=security_policy)
 ```
 
-=== "Without Authentication"
+=== "No Authentication"
+
+=== "Basic Authentication"
 
-=== "With Authentication"
+=== "Firebase Authentication"
 
     !!! info
         To support [Firebase's JavaScript libraries](https://firebase.google.com/docs/web/learn-more){target="_blank"}, FastAgency internally adjusts its security policy to allow required resources. Here are the specific adjustments made:
@@ -143,7 +156,7 @@ ui = MesopUI(security_policy=security_policy)
 
         These adjustments ensure that Firebase scripts and services can load without conflicts.
 
-Please see the [Mesop documentation](https://google.github.io/mesop/api/page/#mesop.security.security_policy.SecurityPolicy){target="_blank"} for details.
+For more details on configuring security, see the official [Mesop documentation](https://google.github.io/mesop/api/page/#mesop.security.security_policy.SecurityPolicy){target="_blank"}.
 
 ### Modifying styles
 
@@ -168,16 +181,25 @@ This example shows how to create a simple learning chat where a student agent in
 #### 1. **Import Required Modules**
 We begin by importing the necessary modules from **FastAgency** and **AutoGen**. These imports provide the essential building blocks for creating agents, workflows, and integrating MesopUI.
 
-=== "Without Authentication"
+=== "No Authentication"
 
     ```python
     {!> docs_src/user_guide/ui/mesop/main_mesop.py [ln:1-14] !}
     ```
 
-=== "With Authentication"
+=== "Basic Authentication"
+
+    ```python
+    {!> docs_src/user_guide/ui/mesop/main_mesop_basic_auth.py [ln:1-15] !}
+    ```
+
+    - [**`BasicAuth`**](../../../../api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md): This class
+    enables you to integrate basic username/password authentication into your Mesop application.
+
+=== "Firebase Authentication"
 
     ```python
-    {!> docs_src/user_guide/ui/mesop/main_mesop_auth.py [ln:1-15] !}
+    {!> docs_src/user_guide/ui/mesop/main_mesop_firebase_auth.py [ln:1-15] !}
     ```
 
     - [**`FirebaseAuth`**](../../../../api/fastagency/ui/mesop/auth/firebase/FirebaseAuth.md) and [**`FirebaseConfig`**](../../../../api/fastagency/ui/mesop/auth/firebase/FirebaseConfig.md): These classes enable you to integrate Firebase authentication into your Mesop application.
@@ -209,16 +231,62 @@ Here, we define a simple workflow where the **Student Agent** interacts with the
 #### 4. **Using MesopUI**
 Finally, we instantiate **[MesopUI](../../../../api/fastagency/ui/mesop/MesopUI.md)** to link the workflow to a web-based interface. This allows the user to interact with the agents through a web browser.
 
-=== "Without Authentication"
+=== "No Authentication"
     ```python
     {!> docs_src/user_guide/ui/mesop/main_mesop.py [ln:59-90] !}
     ```
 
     - **Explanation**: Here, we set up the **MesopUI** as the user interface for the workflow, which will allow the entire agent interaction to take place through a web-based platform.
 
-=== "With Authentication"
+=== "Basic Authentication"
+    ```python hl_lines="29-37 39"
+    {!> docs_src/user_guide/ui/mesop/main_mesop_basic_auth.py [ln:61-101] !}
+    ```
+
+    The [**`BasicAuth`**](../../../../api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md) class allows you to define a set of allowed users with [**bcrypt-hashed**](https://en.wikipedia.org/wiki/Bcrypt){target="_blank"} passwords, providing secure access to your Mesop application. Only users listed in the **`allowed_users`** dictionary can successfully authenticate.
+
+    !!! note
+
+        Only the [**bcrypt**](https://en.wikipedia.org/wiki/Bcrypt){target="_blank"} algorithm is supported for password hashing. Other algorithms, like **MD5** or **SHA-256**, won’t work with this [**`BasicAuth`**](../../../../api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md) class. Ensure all passwords are hashed using bcrypt.
+
+    **BasicAuth Configuration:**
+
+    1. User Setup:
+
+        - The **`allowed_users`** parameter accepts a dictionary that maps usernames (e.g., emails) to their [**bcrypt-hashed**](https://en.wikipedia.org/wiki/Bcrypt){target="_blank"} passwords.
+        - Only bcrypt hashing is supported; other hashing algorithms (like MD5 or SHA-256) will not work with [**`BasicAuth`**](../../../../api/fastagency/ui/mesop/auth/basic_auth/BasicAuth.md) class.
+
+    2. Hashing Passwords with Bcrypt:
+
+        - For each user, generate a bcrypt hash of their password using tools like the [**Bcrypt Hash Generator**](https://bcrypt.online){target="_blank"}.
+
+    **Generating Bcrypt-Hashed Passwords**
+
+    To quickly create a bcrypt hash for a password, follow these steps:
+
+    1. Open the [**Bcrypt Hash Generator**](https://bcrypt.online){target="_blank"}.
+    2. In the `Plain Text Input` field, enter the password `(e.g., someStrongPassword)`.
+    3. Set the `Cost Factor` to `10` (default).
+    4. Click `GENERATE HASH`.
+    5. Copy the hash, which will start with `$2y$...`, and use it as the password for the corresponding user.
+
+    For Example:
+
+    ```py
+    allowed_users = {
+        "harish@airt.ai": "$2y$10$4aH/.C.WritjZAYskA0Dq.htlFDJTa49UuxSVUlp9JCa2K3PgUkaG"  # nosemgrep
+    }
+    ```
+
+    In this example, the hash is generated from `someStrongPassword` for the user `harish@airt.ai`.
+
+    **Authenticating in the Mesop Web App**
+
+    To log in, users should enter their **original passwords** (e.g., `someStrongPassword` for  `harish@airt.ai`) on the Mesop application’s login screen. The `BasicAuth` class then verifies the password by comparing its bcrypt hash with the stored hash in `allowed_users`. If the hashes match, the user is successfully authenticated.
+
+=== "Firebase Authentication"
     ```python hl_lines="29-36 39-44 46"
-    {!> docs_src/user_guide/ui/mesop/main_mesop_auth.py [ln:60-107] !}
+    {!> docs_src/user_guide/ui/mesop/main_mesop_firebase_auth.py [ln:60-107] !}
     ```
 
     - **Create Firebase Configuration**:
@@ -230,11 +298,9 @@ Finally, we instantiate **[MesopUI](../../../../api/fastagency/ui/mesop/MesopUI.
         Instiantiate the [**`FirebaseAuth`**](../../../../api/fastagency/ui/mesop/auth/firebase/FirebaseAuth.md) with Google as the sign-in method and pass the Firebase configuration.
 
         !!! note
-            Currently, [**Firebase**](https://firebase.google.com){target="_blank"} is the only supported authentication provider, with Google as the available sign-in method. Future releases will introduce more sign-in options within Firebase and expand support for additional authentication providers.
+            Currently, [**Firebase**](https://firebase.google.com){target="_blank"} is the only supported authentication provider, with Google as the available sign-in method. Future releases will introduce more sign-in options within Firebase.
 
-        - The `**allowed_users**` parameter:
-
-            The `allowed_users` parameter controls access to the application, with the following options:
+        - The `allowed_users` parameter controls access to the application, with the following options:
 
             - String (`str`):
 
@@ -256,7 +322,7 @@ Finally, we instantiate **[MesopUI](../../../../api/fastagency/ui/mesop/MesopUI.
 
 ### Complete Application Code
 
-=== "Without Authentication"
+=== "No Authentication"
 
     <details>
         <summary>main.py</summary>
@@ -265,12 +331,21 @@ Finally, we instantiate **[MesopUI](../../../../api/fastagency/ui/mesop/MesopUI.
         ```
     </details>
 
-=== "With Authentication"
+=== "Basic Authentication"
+
+    <details>
+        <summary>main.py</summary>
+        ```python
+        {!> docs_src/user_guide/ui/mesop/main_mesop_basic_auth.py !}
+        ```
+    </details>
+
+=== "Firebase Authentication"
 
     <details>
         <summary>main.py</summary>
         ```python
-        {!> docs_src/user_guide/ui/mesop/main_mesop_auth.py !}
+        {!> docs_src/user_guide/ui/mesop/main_mesop_firebase_auth.py !}
         ```
     </details>
 
@@ -317,11 +392,16 @@ The outputs will vary based on the interface, here is the output of the last ter
 [2024-10-15 16:57:44 +0530] [36365] [INFO] Using worker: sync
 [2024-10-15 16:57:44 +0530] [36366] [INFO] Booting worker with pid: 36366
 ```
-=== "Without Authentication"
+=== "No Authentication"
 
     ![Initial message](../../getting-started/images/chat.png)
 
-=== "With Authentication"
+=== "Basic Authentication"
+
+    ![Initial message](./images/basic_auth_login.png)
+    ![Initial message](./images/basic_auth_chat.png)
+
+=== "Firebase Authentication"
 
     ![Initial message](./images/auth_login.png)
     ![Initial message](./images/auth_chat.png)
diff --git a/docs/docs/en/user-guide/ui/mesop/images/basic_auth_chat.png b/docs/docs/en/user-guide/ui/mesop/images/basic_auth_chat.png
new file mode 100644
index 00000000..55e76bf6
--- /dev/null
+++ b/docs/docs/en/user-guide/ui/mesop/images/basic_auth_chat.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:07a02465ff008f5d6fc2170b7d5c5c4a9180080196ee1fa22c26994fdfd99c38
+size 109904
diff --git a/docs/docs/en/user-guide/ui/mesop/images/basic_auth_login.png b/docs/docs/en/user-guide/ui/mesop/images/basic_auth_login.png
new file mode 100644
index 00000000..bb1328f3
--- /dev/null
+++ b/docs/docs/en/user-guide/ui/mesop/images/basic_auth_login.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:541875648d5da2a4ca1f8156b7a6075983a531c75c53378ddd933d193d463e12
+size 27923
diff --git a/docs/docs_src/user_guide/ui/mesop/main_mesop_basic_auth.py b/docs/docs_src/user_guide/ui/mesop/main_mesop_basic_auth.py
new file mode 100644
index 00000000..9e6afe4f
--- /dev/null
+++ b/docs/docs_src/user_guide/ui/mesop/main_mesop_basic_auth.py
@@ -0,0 +1,101 @@
+import os
+from typing import Any
+
+import mesop as me
+from autogen.agentchat import ConversableAgent
+
+from fastagency import UI, FastAgency
+from fastagency.runtimes.autogen import AutoGenWorkflows
+from fastagency.ui.mesop import MesopUI
+from fastagency.ui.mesop.auth.basic_auth import BasicAuth
+from fastagency.ui.mesop.styles import (
+    MesopHomePageStyles,
+    MesopMessagesStyles,
+    MesopSingleChoiceInnerStyles,
+)
+
+llm_config = {
+    "config_list": [
+        {
+            "model": "gpt-4o-mini",
+            "api_key": os.getenv("OPENAI_API_KEY"),
+        }
+    ],
+    "temperature": 0.8,
+}
+
+wf = AutoGenWorkflows()
+
+
+@wf.register(name="simple_learning", description="Student and teacher learning chat")
+def simple_workflow(
+    ui: UI, params: dict[str, Any]
+) -> str:
+    initial_message = ui.text_input(
+        sender="Workflow",
+        recipient="User",
+        prompt="What do you want to learn today?",
+    )
+
+    student_agent = ConversableAgent(
+        name="Student_Agent",
+        system_message="You are a student willing to learn.",
+        llm_config=llm_config,
+    )
+    teacher_agent = ConversableAgent(
+        name="Teacher_Agent",
+        system_message="You are a math teacher.",
+        llm_config=llm_config,
+    )
+
+    chat_result = student_agent.initiate_chat(
+        teacher_agent,
+        message=initial_message,
+        summary_method="reflection_with_llm",
+        max_turns=5,
+    )
+
+    return chat_result.summary  # type: ignore[no-any-return]
+
+
+security_policy=me.SecurityPolicy(allowed_iframe_parents=["https://acme.com"], allowed_script_srcs=["https://cdn.jsdelivr.net"])
+
+styles=MesopHomePageStyles(
+    stylesheets=[
+        "https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"
+    ],
+    root=me.Style(
+        background="#e7f2ff",
+        height="100%",
+        font_family="Inter",
+        display="flex",
+        flex_direction="row",
+    ),
+    message=MesopMessagesStyles(
+        single_choice_inner=MesopSingleChoiceInnerStyles(
+            disabled_button=me.Style(
+                margin=me.Margin.symmetric(horizontal=8),
+                padding=me.Padding.all(16),
+                border_radius=8,
+                background="#64b5f6",
+                color="#fff",
+                font_size=16,
+            ),
+        )
+    ),
+)
+
+# Initialize auth with username and password
+auth = BasicAuth(
+    # TODO: Replace `allowed_users` with the desired usernames and their
+    # bcrypt-hashed passwords. One way to generate bcrypt-hashed passwords
+    # is by using online tools such as https://bcrypt.online
+    allowed_users={
+        "harish@airt.ai": "$2y$10$4aH/.C.WritjZAYskA0Dq.htlFDJTa49UuxSVUlp9JCa2K3PgUkaG",  # nosemgrep
+        "davor@airt.ai": "$2y$10$Yz9GuF/bWmRFmnXFkauOwePT/U.VSUHdpMOX7GPB8GiklJE4HJZmG"  # nosemgrep
+    }
+)
+
+ui = MesopUI(security_policy=security_policy, styles=styles, auth=auth)
+
+app = FastAgency(provider=wf, ui=ui, title="Learning Chat")
diff --git a/docs/docs_src/user_guide/ui/mesop/main_mesop_auth.py b/docs/docs_src/user_guide/ui/mesop/main_mesop_firebase_auth.py
similarity index 100%
rename from docs/docs_src/user_guide/ui/mesop/main_mesop_auth.py
rename to docs/docs_src/user_guide/ui/mesop/main_mesop_firebase_auth.py
diff --git a/fastagency/ui/mesop/auth/auth.py b/fastagency/ui/mesop/auth/auth.py
index 5f90605a..113bfc90 100644
--- a/fastagency/ui/mesop/auth/auth.py
+++ b/fastagency/ui/mesop/auth/auth.py
@@ -13,4 +13,4 @@ class AuthProtocol(Protocol):
     # maybe me.Component is wrong
     def auth_component(self) -> me.component: ...
 
-    def is_authorized(self, token: dict[str, Any]) -> bool: ...
+    def is_authorized(self, *args: Any, **kwargs: Any) -> bool: ...
diff --git a/fastagency/ui/mesop/auth/basic_auth/__init__.py b/fastagency/ui/mesop/auth/basic_auth/__init__.py
new file mode 100644
index 00000000..a8b1d6f4
--- /dev/null
+++ b/fastagency/ui/mesop/auth/basic_auth/__init__.py
@@ -0,0 +1,10 @@
+from .....helpers import check_imports
+
+check_imports(["bcrypt"], "basic_auth")
+
+from .basic_auth import BasicAuth  # noqa: E402
+from .basic_auth_component import (  # noqa: E402
+    basic_auth_component,
+)
+
+__all__ = ["BasicAuth", "basic_auth_component"]
diff --git a/fastagency/ui/mesop/auth/basic_auth/basic_auth.py b/fastagency/ui/mesop/auth/basic_auth/basic_auth.py
new file mode 100644
index 00000000..6bccb84a
--- /dev/null
+++ b/fastagency/ui/mesop/auth/basic_auth/basic_auth.py
@@ -0,0 +1,75 @@
+import typing
+
+import bcrypt
+import mesop as me
+import mesop.labs as mel
+
+from ...data_model import State
+from ...styles import MesopHomePageStyles
+from .basic_auth_component import basic_auth_component
+
+if typing.TYPE_CHECKING:
+    from ..auth import AuthProtocol
+
+
+class BasicAuth:  # implements AuthProtocol
+    def __init__(self, allowed_users: dict[str, str]) -> None:
+        """Initialize the authentication component with allowed users.
+
+        Args:
+        allowed_users (dict[str, str]): A dictionary where the keys are usernames and the values are passwords.
+
+        Initializes:
+        _self (AuthProtocol): Ensures the instance conforms to the AuthProtocol interface.
+        allowed_users (dict[str, str]): A dictionary where the keys are usernames and the values are hashed passwords.
+        """
+        # mypy check if self is AuthProtocol
+        _self: AuthProtocol = self
+
+        self.allowed_users = allowed_users
+
+    def create_security_policy(self, policy: me.SecurityPolicy) -> me.SecurityPolicy:
+        return policy
+
+    def _verify_password(self, password: str, password_hash: str) -> bool:
+        """Verify a password against its hash."""
+        return bcrypt.checkpw(password.encode("utf-8"), password_hash.encode("utf-8"))
+
+    def is_authorized(self, username: str, password: str) -> bool:
+        if username not in self.allowed_users:
+            return False
+
+        password_hash = self.allowed_users[username]
+        return self._verify_password(password, password_hash)
+
+    def on_auth_changed(self, e: mel.WebEvent) -> None:
+        state = me.state(State)
+
+        if e.value is None:
+            state.authenticated_user = ""
+            return
+
+        username, password = e.value["username"], e.value["password"]
+
+        if not self.is_authorized(username, password):
+            raise me.MesopUserException("Invalid username or password")
+
+        state.authenticated_user = username
+
+    # maybe me.Component is wrong
+    def auth_component(self) -> me.component:
+        styles = MesopHomePageStyles()
+        state = me.state(State)
+        if state.authenticated_user:
+            with me.box(style=styles.logout_btn_container):
+                basic_auth_component(
+                    on_auth_changed=self.on_auth_changed,
+                    authenticated_user=state.authenticated_user,
+                )
+        else:
+            with me.box(style=styles.login_box):  # noqa: SIM117
+                with me.box(style=styles.login_btn_container):
+                    basic_auth_component(
+                        on_auth_changed=self.on_auth_changed,
+                        authenticated_user=state.authenticated_user,
+                    )
diff --git a/fastagency/ui/mesop/auth/basic_auth/basic_auth_component.py b/fastagency/ui/mesop/auth/basic_auth/basic_auth_component.py
new file mode 100644
index 00000000..af3ec4a0
--- /dev/null
+++ b/fastagency/ui/mesop/auth/basic_auth/basic_auth_component.py
@@ -0,0 +1,19 @@
+from typing import Any, Callable, Optional
+
+import mesop.labs as mel
+
+MEL_WEB_COMPONENT_PATH = "/__fast_agency_internal__/javascript/basic_auth_component.js"
+
+
+@mel.web_component(path=MEL_WEB_COMPONENT_PATH)  # type: ignore[misc]
+def basic_auth_component(
+    on_auth_changed: Callable[[mel.WebEvent], Any],
+    authenticated_user: Optional[str] = None,
+) -> Any:
+    return mel.insert_web_component(
+        name="basic-auth-component",
+        events={
+            "authChanged": on_auth_changed,
+        },
+        properties={"authenticatedUser": authenticated_user},
+    )
diff --git a/javascript/basic_auth_component.js b/javascript/basic_auth_component.js
new file mode 100644
index 00000000..2e08d462
--- /dev/null
+++ b/javascript/basic_auth_component.js
@@ -0,0 +1,287 @@
+import {
+  LitElement,
+  css,
+  html,
+} from "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js";
+
+class BasicAuthComponent extends LitElement {
+  static styles = css`
+    :host {
+      display: block;
+      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+    }
+
+    .auth-container {
+      width: 100%;
+      max-width: 400px;
+      margin: 1rem auto;
+      padding: 2rem;
+      border-radius: 8px;
+      background: white;
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    }
+
+    form {
+      display: flex;
+      flex-direction: column;
+      gap: 1.5rem;
+    }
+
+    .input-group {
+      position: relative;
+    }
+
+    .input-group input {
+      width: 96%;
+      padding: 0.75rem 0 0.75rem 0.5rem;
+      font-size: 1rem;
+      border: 1px solid #e2e8f0;
+      border-radius: 6px;
+      background: #f8fafc;
+      transition: all 0.2s ease;
+    }
+
+    .input-group input:focus {
+      outline: none;
+      border-color: #3b82f6;
+      background: white;
+      box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
+    }
+
+    .input-group input::placeholder {
+      color: #94a3b8;
+    }
+
+    .auth-button {
+      width: 100%;
+      padding: 0.75rem 1.5rem;
+      font-size: 1rem;
+      font-weight: 500;
+      color: white;
+      background: #3b82f6;
+      border: none;
+      border-radius: 6px;
+      cursor: pointer;
+      transition: all 0.2s ease;
+    }
+
+    .auth-button:hover {
+      background: #2563eb;
+      transform: translateY(-1px);
+      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+    }
+
+    .auth-button:active {
+      transform: translateY(0);
+      box-shadow: none;
+    }
+
+    .auth-button:disabled {
+      background: #94a3b8;
+      cursor: not-allowed;
+      transform: none;
+      box-shadow: none;
+    }
+
+    .error {
+      padding: 0.75rem 1rem;
+      margin-bottom: 1rem;
+      color: #dc2626;
+      background: #fee2e2;
+      border: 1px solid #fecaca;
+      border-radius: 6px;
+      font-size: 0.875rem;
+    }
+
+    /* Sign out button specific styles */
+    .auth-container:has(> .auth-button) {
+      padding: 0rem;
+      box-shadow: none;
+      background: transparent;
+      margin-top:0rem;
+    }
+
+    .auth-container:has(> .auth-button) .auth-button {
+      width: auto;
+      max-width: 200px;
+      background: #ef4444;
+      border: 1px solid #dc2626;
+      padding: 0.5rem 0.75rem;
+      font-size: 0.75rem;
+    }
+
+    .auth-container:has(> .auth-button) .auth-button:hover {
+      background: #dc2626;
+    }
+
+    /* Loading state styles */
+    .auth-button.loading {
+      position: relative;
+      color: transparent;
+    }
+
+    .auth-button.loading::after {
+      content: '';
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      width: 20px;
+      height: 20px;
+      border: 2px solid white;
+      border-radius: 50%;
+      border-top-color: transparent;
+      animation: spin 0.8s linear infinite;
+    }
+
+    @keyframes spin {
+      to {
+        transform: translate(-50%, -50%) rotate(360deg);
+      }
+    }
+  `;
+  static properties = {
+    isSignedIn: { type: Boolean },
+    authChanged: { type: String },
+    error: { type: String },
+    username: { type: String },
+    password: { type: String },
+    authenticatedUser: { type: String }
+  };
+
+  constructor() {
+    super();
+    this.isSignedIn = false;
+    this.error = '';
+    this.username = '';
+    this.password = '';
+  }
+
+  firstUpdated() {
+    // Initialize form state
+    const form = this.shadowRoot.querySelector('form');
+    if (form) {
+      form.reset();
+    }
+
+    if (this.authenticatedUser === "" || this.authenticatedUser === null) {
+      // Initial auth state notification
+      this.dispatchEvent(new MesopEvent(this.authChanged, null));
+    } else {
+      this.isSignedIn = true;
+      this.username = this.authenticatedUser;
+    }
+  }
+
+  async handleSubmit(e) {
+    e.preventDefault();
+
+    if (!this.username || !this.password) {
+      this.error = 'Please fill in all fields';
+      return;
+    }
+
+    try {
+      const credentials = {
+        username: this.username,
+        password: this.password
+      };
+
+      // Dispatch auth event with credentials using MesopEvent
+      this.dispatchEvent(new MesopEvent(this.authChanged, credentials));
+
+      this.username = '';
+      this.password = '';
+      // this.error = '';
+      // this.isSignedIn = true;
+
+    } catch (error) {
+      this.error = error.message || 'Authentication failed';
+      console.error("Auth error:", error);
+    }
+  }
+
+  handleUsernameChange(e) {
+    this.username = e.target.value;
+    this.error = '';
+  }
+
+  handlePasswordChange(e) {
+    this.password = e.target.value;
+    this.error = '';
+  }
+
+  signOut() {
+    try {
+      // this.isSignedIn = false;
+      this.username = '';
+      this.password = '';
+
+      // Notify about sign out using MesopEvent
+      this.dispatchEvent(new MesopEvent(this.authChanged, null));
+
+      // Reset form
+      const form = this.shadowRoot.querySelector('form');
+      if (form) {
+        form.reset();
+      }
+    } catch (error) {
+      console.error("Sign out error:", error);
+    }
+  }
+
+  render() {
+    // Use conditional rendering without style display properties
+    return html`
+    <div
+      class="auth-container"
+      style="${this.isSignedIn ? "" : "display: none"}"
+    >
+      <button
+        class="auth-button"
+        @click="${this.signOut}"
+      >
+        <span class="button-text">Sign out</span>
+      </button>
+    </div>
+    <div
+      class="auth-container"
+      style="${this.isSignedIn ? "display: none" : ""}"
+    >
+      <h1>Sign in to your account</h1>
+      <form @submit="${this.handleSubmit}">
+        ${this.error ? html`<div class="error">${this.error}</div>` : ''}
+
+        <div class="input-group">
+          <input
+            type="text"
+            placeholder="Username"
+            .value="${this.username}"
+            @input="${this.handleUsernameChange}"
+            required
+          />
+        </div>
+
+        <div class="input-group">
+          <input
+            type="password"
+            placeholder="Password"
+            .value="${this.password}"
+            @input="${this.handlePasswordChange}"
+            required
+          />
+        </div>
+
+        <button
+          type="submit"
+          class="auth-button ${this.loading ? 'loading' : ''}"
+          ?disabled="${!this.username || !this.password}"
+        >
+          <span class="button-text">Sign in</span>
+        </button>
+      </form>
+    </div>
+  `;
+  }
+}
+
+customElements.define("basic-auth-component", BasicAuthComponent);
diff --git a/pyproject.toml b/pyproject.toml
index b86fc880..2b198818 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -85,12 +85,16 @@ server = [
     "waitress>=3.0.0 ; platform_system == 'Windows'",
 ]
 
+basic_auth = [
+    "bcrypt>=4.2.0"
+]
+
 firebase = [
     "firebase-admin>=6.5.0"
 ]
 
 submodules = [
-    "fastagency[mesop,autogen,openapi,fastapi,nats,server,firebase]"
+    "fastagency[mesop,autogen,openapi,fastapi,nats,server,firebase,basic_auth]"
 ]
 
 # dev dependencies


Test patch:
diff --git a/tests/docs_src/test_import.py b/tests/docs_src/test_import.py
index 8361bb97..b20cb687 100644
--- a/tests/docs_src/test_import.py
+++ b/tests/docs_src/test_import.py
@@ -13,7 +13,8 @@ module_name = "docs_src"
 
 # Constants for module paths
 MESOP_AUTH_MODULES = {
-    "docs_src.user_guide.ui.mesop.main_mesop_auth",
+    "docs_src.user_guide.ui.mesop.main_mesop_firebase_auth",
+    "docs_src.user_guide.ui.mesop.main_mesop_basic_auth",
 }
 
 MESOP_NON_AUTH_MODULES = {
diff --git a/tests/ui/mesop/test_basic_auth.py b/tests/ui/mesop/test_basic_auth.py
new file mode 100644
index 00000000..c1864663
--- /dev/null
+++ b/tests/ui/mesop/test_basic_auth.py
@@ -0,0 +1,36 @@
+import sys
+
+import bcrypt
+import pytest
+
+from fastagency.logging import get_logger
+
+logger = get_logger(__name__)
+
+if sys.version_info >= (3, 10):
+    from fastagency.ui.mesop.auth.basic_auth import BasicAuth
+
+    class TestBasicAuthMesopHomePage:
+        @pytest.fixture
+        def basic_auth(self):
+            """Fixture to create a BasicAuth instance with test users."""
+            # Create test users with hashed passwords
+            test_password = "test_password"  # pragma: allowlist secret
+            hashed_password = bcrypt.hashpw(
+                test_password.encode("utf-8"), bcrypt.gensalt()
+            ).decode("utf-8")
+
+            allowed_users = {
+                "test_user": hashed_password,
+                "another_user": hashed_password,
+            }
+
+            return BasicAuth(allowed_users)
+
+        def test_is_authorized_valid_credentials(self, basic_auth):
+            """Test authorization with valid username and password."""
+            assert basic_auth.is_authorized("test_user", "test_password") is True
+
+        def test_is_authorized_invalid_username(self, basic_auth):
+            """Test authorization with invalid username."""
+            assert basic_auth.is_authorized("invalid_user", "test_password") is False