Skip to content

Commit fc4fe21

Browse files
committed
Add Authentication Setup Guide
1 parent 78c20c0 commit fc4fe21

File tree

14 files changed

+263
-50
lines changed

14 files changed

+263
-50
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ docker run \
107107
hlohaus789/g4f:latest-slim \
108108
rm -r -f /app/g4f/ \
109109
&& pip install -U g4f[slim] \
110-
&& python -m g4f.cli api --gui --debug
110+
&& python -m g4f --debug
111111
```
112112
It also updates the `g4f` package at startup and installs any new required dependencies.
113113

@@ -134,7 +134,7 @@ By following these steps, you should be able to successfully install and run the
134134

135135
Run the **Webview UI** on other Platforms:
136136

137-
- [/docs/guides/webview](docs/webview.md)
137+
- [/docs/webview](docs/webview.md)
138138

139139
##### Use your smartphone:
140140

@@ -204,7 +204,7 @@ image_url = response.data[0].url
204204
print(f"Generated image URL: {image_url}")
205205
```
206206

207-
[![Image with cat](/docs/cat.jpeg)](docs/client.md)
207+
[![Image with cat](/docs/images/cat.jpeg)](docs/client.md)
208208

209209
#### **Full Documentation for Python API**
210210
- **New:**
@@ -241,6 +241,10 @@ This API is designed for straightforward implementation and enhanced compatibili
241241

242242
### Configuration
243243

244+
#### Authentication
245+
246+
Refer to the [G4F Authentication Setup Guide](docs/authentication.md) for detailed instructions on setting up authentication.
247+
244248
#### Cookies
245249

246250
Cookies are essential for using Meta AI and Microsoft Designer to create images.

docs/async_client.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ async def main():
145145
provider=g4f.Provider.CopilotAccount
146146
)
147147

148-
image = requests.get("https://raw.githubusercontent.com/xtekky/gpt4free/refs/heads/main/docs/cat.jpeg", stream=True).raw
148+
image = requests.get("https://raw.githubusercontent.com/xtekky/gpt4free/refs/heads/main/docs/images/cat.jpeg", stream=True).raw
149149

150150
response = await client.chat.completions.create(
151151
model=g4f.models.default,

docs/authentication.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# G4F Authentication Setup Guide
2+
3+
This documentation explains how to set up Basic Authentication for the GUI and API key authentication for the API when running the G4F server.
4+
5+
## Prerequisites
6+
7+
Before proceeding, ensure you have the following installed:
8+
- Python 3.x
9+
- G4F package installed (ensure it is set up and working)
10+
- Basic knowledge of using environment variables on your operating system
11+
12+
## Steps to Set Up Authentication
13+
14+
### 1. API Key Authentication for Both GUI and API
15+
16+
To secure both the GUI and the API, you'll authenticate using an API key. The API key should be injected via an environment variable and passed to both the GUI (via Basic Authentication) and the API.
17+
18+
#### Steps to Inject the API Key Using Environment Variables:
19+
20+
1. **Set the environment variable** for your API key:
21+
22+
On Linux/macOS:
23+
```bash
24+
export G4F_API_KEY="your-api-key-here"
25+
```
26+
27+
On Windows (Command Prompt):
28+
```bash
29+
set G4F_API_KEY="your-api-key-here"
30+
```
31+
32+
On Windows (PowerShell):
33+
```bash
34+
$env:G4F_API_KEY="your-api-key-here"
35+
```
36+
37+
Replace `your-api-key-here` with your actual API key.
38+
39+
2. **Run the G4F server with the API key injected**:
40+
41+
Use the following command to start the G4F server. The API key will be passed to both the GUI and the API:
42+
43+
```bash
44+
python -m g4f --debug --port 8080 --g4f-api-key $G4F_API_KEY
45+
```
46+
47+
- `--debug` enables debug mode for more verbose logs.
48+
- `--port 8080` specifies the port on which the server will run (you can change this if needed).
49+
- `--g4f-api-key` specifies the API key for both the GUI and the API.
50+
51+
#### Example:
52+
53+
```bash
54+
export G4F_API_KEY="my-secret-api-key"
55+
python -m g4f --debug --port 8080 --g4f-api-key $G4F_API_KEY
56+
```
57+
58+
Now, both the GUI and API will require the correct API key for access.
59+
60+
---
61+
62+
### 2. Accessing the GUI with Basic Authentication
63+
64+
The GUI uses **Basic Authentication**, where the **username** can be any value, and the **password** is your API key.
65+
66+
#### Example:
67+
68+
To access the GUI, open your web browser and navigate to `http://localhost:8080/chat/`. You will be prompted for a username and password.
69+
70+
- **Username**: You can use any username (e.g., `user` or `admin`).
71+
- **Password**: Enter your API key (the same key you set in the `G4F_API_KEY` environment variable).
72+
73+
---
74+
75+
### 3. Python Example for Accessing the API
76+
77+
To interact with the API, you can send requests by including the `g4f-api-key` in the headers. Here's an example of how to do this using the `requests` library in Python.
78+
79+
#### Example Code to Send a Request:
80+
81+
```python
82+
import requests
83+
84+
url = "http://localhost:8080/v1/chat/completions"
85+
86+
# Body of the request
87+
body = {
88+
"model": "your-model-name", # Replace with your model name
89+
"provider": "your-provider", # Replace with the provider name
90+
"messages": [
91+
{
92+
"role": "user",
93+
"content": "Hello"
94+
}
95+
]
96+
}
97+
98+
# API Key (can be set as an environment variable)
99+
api_key = "your-api-key-here" # Replace with your actual API key
100+
101+
# Send the POST request
102+
response = requests.post(url, json=body, headers={"g4f-api-key": api_key})
103+
104+
# Check the response
105+
print(response.status_code)
106+
print(response.json())
107+
```
108+
109+
In this example:
110+
- Replace `"your-api-key-here"` with your actual API key.
111+
- `"model"` and `"provider"` should be replaced with the appropriate model and provider you're using.
112+
- The `messages` array contains the conversation you want to send to the API.
113+
114+
#### Response:
115+
116+
The response will contain the output of the API request, such as the model's completion or other relevant data, which you can then process in your application.
117+
118+
---
119+
120+
### 4. Testing the Setup
121+
122+
- **Accessing the GUI**: Open a web browser and navigate to `http://localhost:8080/chat/`. The GUI will now prompt you for a username and password. You can enter any username (e.g., `admin`), and for the password, enter the API key you set up in the environment variable.
123+
124+
- **Accessing the API**: Use the Python code example above to send requests to the API. Ensure the correct API key is included in the `g4f-api-key` header.
125+
126+
---
127+
128+
### 5. Troubleshooting
129+
130+
- **GUI Access Issues**: If you're unable to access the GUI, ensure that you are using the correct API key as the password.
131+
- **API Access Issues**: If the API is rejecting requests, verify that the `G4F_API_KEY` environment variable is correctly set and passed to the server. You can also check the server logs for more detailed error messages.
132+
133+
---
134+
135+
## Summary
136+
137+
By following the steps above, you will have successfully set up Basic Authentication for the G4F GUI (using any username and the API key as the password) and API key authentication for the API. This ensures that only authorized users can access both the interface and make API requests.
138+
139+
[Return to Home](/)

docs/client.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ client = Client(
181181
)
182182

183183
response = client.images.create_variation(
184-
image=open("cat.jpg", "rb"),
184+
image=open("docs/images/cat.jpg", "rb"),
185185
model="dall-e-3",
186186
# Add any other necessary parameters
187187
)
@@ -235,7 +235,7 @@ client = Client(
235235
)
236236

237237
image = requests.get("https://raw.githubusercontent.com/xtekky/gpt4free/refs/heads/main/docs/cat.jpeg", stream=True).raw
238-
# Or: image = open("docs/cat.jpeg", "rb")
238+
# Or: image = open("docs/images/cat.jpeg", "rb")
239239

240240
response = client.chat.completions.create(
241241
model=g4f.models.default,
File renamed without changes.
File renamed without changes.
File renamed without changes.

g4f/Provider/Blackbox2.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66

77
from ..typing import AsyncResult, Messages
88
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
9+
from .. import debug
910

1011
class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
1112
url = "https://www.blackbox.ai"
1213
api_endpoint = "https://www.blackbox.ai/api/improve-prompt"
1314
working = True
1415
supports_system_message = True
1516
supports_message_history = True
16-
17+
supports_stream = False
1718
default_model = 'llama-3.1-70b'
1819
models = [default_model]
1920

@@ -62,8 +63,8 @@ async def create_async_generator(
6263
raise KeyError("'prompt' key not found in the response")
6364
except Exception as e:
6465
if attempt == max_retries - 1:
65-
yield f"Error after {max_retries} attempts: {str(e)}"
66+
raise RuntimeError(f"Error after {max_retries} attempts: {str(e)}")
6667
else:
6768
wait_time = delay * (2 ** attempt) + random.uniform(0, 1)
68-
print(f"Attempt {attempt + 1} failed. Retrying in {wait_time:.2f} seconds...")
69+
debug.log(f"Attempt {attempt + 1} failed. Retrying in {wait_time:.2f} seconds...")
6970
await asyncio.sleep(wait_time)

g4f/Provider/needs_auth/OpenaiChat.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ async def create_async_generator(
305305
if not cls.needs_auth:
306306
cls._create_request_args(cookies)
307307
RequestConfig.proof_token = get_config(cls._headers.get("user-agent"))
308-
async with session.get(cls.url, headers=INIT_HEADERS) as response:
308+
async with session.get(cls.url, headers=cls._headers) as response:
309309
cls._update_request_args(session)
310310
await raise_for_status(response)
311311
try:
@@ -538,6 +538,7 @@ def on_request(event: nodriver.cdp.network.RequestWillBeSent):
538538
await page.send(nodriver.cdp.network.enable())
539539
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
540540
page = await browser.get(cls.url)
541+
await asyncio.sleep(1)
541542
body = await page.evaluate("JSON.stringify(window.__remixContext)")
542543
if body:
543544
match = re.search(r'"accessToken":"(.*?)"', body)

g4f/__main__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from __future__ import annotations
2+
3+
from .cli import get_api_parser, run_api_args
4+
5+
parser = get_api_parser()
6+
args = parser.parse_args()
7+
if args.gui is None:
8+
args.gui = True
9+
run_api_args(args)

g4f/api/__init__.py

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
HTTP_500_INTERNAL_SERVER_ERROR,
2424
)
2525
from fastapi.encoders import jsonable_encoder
26-
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
26+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials, HTTPBasic
2727
from fastapi.middleware.cors import CORSMiddleware
2828
from starlette.responses import FileResponse
2929
from pydantic import BaseModel, Field
@@ -50,7 +50,7 @@ class Annotated:
5050

5151
DEFAULT_PORT = 1337
5252

53-
def create_app(g4f_api_key: str = None):
53+
def create_app():
5454
app = FastAPI()
5555

5656
# Add CORS middleware
@@ -62,7 +62,7 @@ def create_app(g4f_api_key: str = None):
6262
allow_headers=["*"],
6363
)
6464

65-
api = Api(app, g4f_api_key=g4f_api_key)
65+
api = Api(app)
6666

6767
if AppConfig.gui:
6868
@app.get("/")
@@ -86,9 +86,14 @@ async def home():
8686

8787
return app
8888

89-
def create_app_debug(g4f_api_key: str = None):
89+
def create_app_debug():
9090
g4f.debug.logging = True
91-
return create_app(g4f_api_key)
91+
return create_app()
92+
93+
def create_app_with_gui_and_debug():
94+
g4f.debug.logging = True
95+
AppConfig.gui = True
96+
return create_app()
9297

9398
class ChatCompletionsConfig(BaseModel):
9499
messages: Messages = Field(examples=[[{"role": "system", "content": ""}, {"role": "user", "content": ""}]])
@@ -156,8 +161,8 @@ def from_exception(cls, exception: Exception,
156161
return cls(format_exception(exception, config), status_code)
157162

158163
@classmethod
159-
def from_message(cls, message: str, status_code: int = HTTP_500_INTERNAL_SERVER_ERROR):
160-
return cls(format_exception(message), status_code)
164+
def from_message(cls, message: str, status_code: int = HTTP_500_INTERNAL_SERVER_ERROR, headers: dict = None):
165+
return cls(format_exception(message), status_code, headers=headers)
161166

162167
def render(self, content) -> bytes:
163168
return str(content).encode(errors="ignore")
@@ -184,26 +189,57 @@ def set_list_ignored_providers(ignored: list[str]):
184189
list_ignored_providers = ignored
185190

186191
class Api:
187-
def __init__(self, app: FastAPI, g4f_api_key=None) -> None:
192+
def __init__(self, app: FastAPI) -> None:
188193
self.app = app
189194
self.client = AsyncClient()
190-
self.g4f_api_key = g4f_api_key
191195
self.get_g4f_api_key = APIKeyHeader(name="g4f-api-key")
192196
self.conversations: dict[str, dict[str, BaseConversation]] = {}
193197

194198
security = HTTPBearer(auto_error=False)
199+
basic_security = HTTPBasic()
200+
201+
async def get_username(self, request: Request):
202+
credentials = await self.basic_security(request)
203+
current_password_bytes = credentials.password.encode()
204+
is_correct_password = secrets.compare_digest(
205+
current_password_bytes, AppConfig.g4f_api_key.encode()
206+
)
207+
if not is_correct_password:
208+
raise HTTPException(
209+
status_code=HTTP_401_UNAUTHORIZED,
210+
detail="Incorrect username or password",
211+
headers={"WWW-Authenticate": "Basic"},
212+
)
213+
return credentials.username
195214

196215
def register_authorization(self):
216+
if AppConfig.g4f_api_key:
217+
print(f"Register authentication key: {''.join(['*' for _ in range(len(AppConfig.g4f_api_key))])}")
197218
@self.app.middleware("http")
198219
async def authorization(request: Request, call_next):
199-
if self.g4f_api_key and request.url.path not in ("/", "/v1"):
220+
if AppConfig.g4f_api_key is not None:
200221
try:
201222
user_g4f_api_key = await self.get_g4f_api_key(request)
202-
except HTTPException as e:
203-
if e.status_code == 403:
223+
except HTTPException:
224+
user_g4f_api_key = None
225+
if request.url.path.startswith("/v1"):
226+
if user_g4f_api_key is None:
204227
return ErrorResponse.from_message("G4F API key required", HTTP_401_UNAUTHORIZED)
205-
if not secrets.compare_digest(self.g4f_api_key, user_g4f_api_key):
206-
return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
228+
if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
229+
return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
230+
else:
231+
path = request.url.path
232+
if user_g4f_api_key is not None and path.startswith("/images/"):
233+
if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
234+
return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
235+
elif path.startswith("/backend-api/") or path.startswith("/images/") or path.startswith("/chat/") and path != "/chat/":
236+
try:
237+
username = await self.get_username(request)
238+
except HTTPException as e:
239+
return ErrorResponse.from_message(e.detail, e.status_code, e.headers)
240+
response = await call_next(request)
241+
response.headers["X-Username"] = username
242+
return response
207243
return await call_next(request)
208244

209245
def register_validation_exception_handler(self):
@@ -512,8 +548,12 @@ def run_api(
512548
host, port = bind.split(":")
513549
if port is None:
514550
port = DEFAULT_PORT
551+
if AppConfig.gui and debug:
552+
method = "create_app_with_gui_and_debug"
553+
else:
554+
method = "create_app_debug" if debug else "create_app"
515555
uvicorn.run(
516-
f"g4f.api:create_app{'_debug' if debug else ''}",
556+
f"g4f.api:{method}",
517557
host=host,
518558
port=int(port),
519559
workers=workers,

0 commit comments

Comments
 (0)