Skip to content

Commit 2d1e94d

Browse files
author
esfiam
committed
feat: Add direct token authentication support (v0.2.0)
- Add access_token parameter to MarzbanAPI constructor - Support both username/password and direct token authentication - Maintain full backward compatibility - Update documentation and examples - Add comprehensive changelog
1 parent 4251266 commit 2d1e94d

File tree

8 files changed

+132
-16
lines changed

8 files changed

+132
-16
lines changed

CHANGELOG.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.2.0] - 2024-12-19
9+
10+
### Added
11+
- **Token Authentication Support**: You can now authenticate directly using a pre-existing JWT access token
12+
- New optional parameter `access_token` in `MarzbanAPI` constructor
13+
- Choose between username/password authentication or direct token authentication
14+
- Backward compatible with existing code
15+
16+
### Changed
17+
- `username` and `password` parameters are now optional in `MarzbanAPI` constructor (when `access_token` is provided)
18+
- Updated documentation and examples to show both authentication methods
19+
20+
### Examples
21+
```python
22+
# New: Direct token authentication
23+
async with MarzbanAPI("http://127.0.0.1:8000", access_token="your_jwt_token") as api:
24+
users = await api.user.get_users()
25+
26+
# Still works: Username/password authentication
27+
async with MarzbanAPI("http://127.0.0.1:8000", "admin", "password") as api:
28+
users = await api.user.get_users()
29+
```
30+
31+
## [0.1.0] - 2024-12-18
32+
33+
### Added
34+
- Initial release of MarzbanAPILib
35+
- Modular API client with separate sections for User, Admin, System, Core, and Node management
36+
- Full async/await support with modern Python patterns
37+
- Complete type hints and comprehensive error handling
38+
- Support for all Marzban API endpoints (35+ methods)
39+
- Context manager support for automatic connection management
40+
- Detailed documentation and usage examples

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include LICENSE
22
include README.md
3+
include CHANGELOG.md
34
include requirements.txt
45
include marzbanapilib/py.typed
56
recursive-include marzbanapilib *.py

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ pip install marzbanapilib
2121

2222
## Quick Start
2323

24+
### Authentication with Username/Password
25+
2426
```python
2527
import asyncio
2628
from marzbanapilib import MarzbanAPI
2729

2830
async def main():
29-
# Create API client
31+
# Create API client using username and password
3032
async with MarzbanAPI(
3133
base_url="http://127.0.0.1:8000",
3234
username="admin",
@@ -49,6 +51,25 @@ async def main():
4951
asyncio.run(main())
5052
```
5153

54+
### Authentication with Access Token
55+
56+
```python
57+
import asyncio
58+
from marzbanapilib import MarzbanAPI
59+
60+
async def main():
61+
# Use pre-existing access token (no username/password needed)
62+
async with MarzbanAPI(
63+
base_url="http://127.0.0.1:8000",
64+
access_token="your_jwt_token_here"
65+
) as api:
66+
# Get system statistics
67+
stats = await api.system.get_stats()
68+
print(f"Total users: {stats['total_user']}")
69+
70+
asyncio.run(main())
71+
```
72+
5273
## Architecture
5374

5475
The library is organized into modular sections:
@@ -86,9 +107,14 @@ async with MarzbanAPI(...) as api:
86107
### Manual Authentication
87108

88109
```python
110+
# With username/password
89111
api = MarzbanAPI("http://127.0.0.1:8000", "admin", "password")
90112
await api.authenticate()
91113

114+
# Or with access token
115+
api = MarzbanAPI("http://127.0.0.1:8000", access_token="your_jwt_token")
116+
await api.authenticate()
117+
92118
# Use the API
93119
users = await api.user.get_users()
94120

examples/usage_example.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Example usage of MarzbanAPILib
33
44
This example demonstrates basic operations with the Marzban API:
5-
- Authentication
5+
- Authentication (both username/password and direct token methods)
66
- Getting system statistics
77
- Creating a new user
88
- Listing users
@@ -159,6 +159,32 @@ async def manual_authentication_example():
159159
print(" ✅ Connection closed")
160160

161161

162+
async def direct_token_authentication_example():
163+
"""Example of authentication using pre-existing access token"""
164+
print("\n🔑 Direct Token Authentication Example:")
165+
166+
# In a real scenario, you would have obtained this token from a previous authentication
167+
# or from secure storage. Here we first get a token for demonstration.
168+
169+
# Step 1: Get a token using traditional method (for demo purposes)
170+
async with MarzbanAPI("http://127.0.0.1:8000", "admin", "password") as temp_api:
171+
print(" • Getting token for demonstration...")
172+
demo_token = temp_api.token
173+
print(f" • Token obtained: {demo_token[:20]}...")
174+
175+
# Step 2: Use the token directly for authentication
176+
print(" • Using token for direct authentication:")
177+
async with MarzbanAPI("http://127.0.0.1:8000", access_token=demo_token) as api:
178+
print(" ✅ Authenticated directly with token!")
179+
180+
# Use the API normally
181+
stats = await api.system.get_stats()
182+
print(f" • Total users: {stats['total_user']}")
183+
print(f" • Active users: {stats['users_active']}")
184+
185+
print(" ✅ Direct token authentication completed!")
186+
187+
162188
async def error_handling_example():
163189
"""Example of error handling"""
164190
print("\n⚠️ Error Handling Example:")
@@ -186,4 +212,5 @@ async def error_handling_example():
186212

187213
# Run additional examples
188214
asyncio.run(manual_authentication_example())
215+
asyncio.run(direct_token_authentication_example())
189216
asyncio.run(error_handling_example())

marzbanapilib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
with the Marzban VPN panel through its REST API.
66
"""
77

8-
__version__ = "0.1.0"
8+
__version__ = "0.2.0"
99
__author__ = "Mohammad Rasol Esfandiari"
1010
__email__ = "[email protected]"
1111
__license__ = "MIT"

marzbanapilib/marzban.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,41 @@ class MarzbanAPI:
1515
This class provides a unified interface to all API sections through
1616
modular sub-APIs for better organization and maintainability.
1717
18-
Usage:
18+
You can authenticate in two ways:
19+
1. Using username and password (traditional method)
20+
2. Using a pre-existing access token (direct token method)
21+
22+
Usage with username/password:
1923
async with MarzbanAPI("http://127.0.0.1:8000", "admin", "password") as api:
20-
# Access different API sections
2124
users = await api.user.get_users()
2225
stats = await api.system.get_stats()
23-
nodes = await api.node.get_all()
26+
27+
Usage with access token:
28+
async with MarzbanAPI("http://127.0.0.1:8000", access_token="your_jwt_token") as api:
29+
users = await api.user.get_users()
30+
stats = await api.system.get_stats()
2431
"""
2532

26-
def __init__(self, base_url: str, username: str, password: str):
33+
def __init__(
34+
self,
35+
base_url: str,
36+
username: Optional[str] = None,
37+
password: Optional[str] = None,
38+
access_token: Optional[str] = None
39+
):
2740
"""
2841
Initialize MarzbanAPI client.
2942
3043
Args:
3144
base_url: Base URL of Marzban API (e.g. http://127.0.0.1:8000)
32-
username: Admin username for authentication
33-
password: Admin password for authentication
45+
username: Admin username for authentication (optional if access_token provided)
46+
password: Admin password for authentication (optional if access_token provided)
47+
access_token: Pre-existing JWT token for direct authentication (optional)
3448
"""
3549
self.base_url = base_url.rstrip('/')
3650
self.username = username
3751
self.password = password
52+
self.access_token = access_token
3853
self.token: Optional[str] = None
3954
self.client = httpx.AsyncClient(timeout=30.0)
4055

@@ -56,11 +71,18 @@ async def __aexit__(self, exc_type, exc_val, exc_tb):
5671

5772
async def authenticate(self):
5873
"""Authenticate with Marzban API and initialize all sections."""
59-
self.token = await get_token_from_credentials(
60-
self.username,
61-
self.password,
62-
self.base_url
63-
)
74+
# Use provided access token or get new token from credentials
75+
if self.access_token:
76+
self.token = self.access_token
77+
else:
78+
if not self.username or not self.password:
79+
raise ValueError("Either access_token or both username and password must be provided")
80+
81+
self.token = await get_token_from_credentials(
82+
self.username,
83+
self.password,
84+
self.base_url
85+
)
6486

6587
# Initialize all API sections with shared client and token
6688
self.user = UserAPI(self.client, self.base_url, self.token)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "marzbanapilib"
7-
version = "0.1.0"
7+
version = "0.2.0"
88
description = "A modern async Python client library for Marzban VPN panel API"
99
readme = "README.md"
1010
authors = [

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = marzbanapilib
3-
version = 0.1.0
3+
version = 0.2.0
44
author = Mohammad Rasol Esfandiari
55
author_email = [email protected]
66
description = A modern async Python client library for Marzban VPN panel API

0 commit comments

Comments
 (0)