user-auth-client 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- user_auth_client-0.1.0/LICENSE +21 -0
- user_auth_client-0.1.0/PKG-INFO +257 -0
- user_auth_client-0.1.0/README.md +220 -0
- user_auth_client-0.1.0/pyproject.toml +62 -0
- user_auth_client-0.1.0/setup.cfg +4 -0
- user_auth_client-0.1.0/src/user_auth_client/__init__.py +21 -0
- user_auth_client-0.1.0/src/user_auth_client/_utils.py +13 -0
- user_auth_client-0.1.0/src/user_auth_client/client.py +152 -0
- user_auth_client-0.1.0/src/user_auth_client/django.py +65 -0
- user_auth_client-0.1.0/src/user_auth_client/exceptions.py +20 -0
- user_auth_client-0.1.0/src/user_auth_client/fastapi.py +66 -0
- user_auth_client-0.1.0/src/user_auth_client/flask.py +95 -0
- user_auth_client-0.1.0/src/user_auth_client/models.py +42 -0
- user_auth_client-0.1.0/src/user_auth_client/py.typed +0 -0
- user_auth_client-0.1.0/src/user_auth_client.egg-info/PKG-INFO +257 -0
- user_auth_client-0.1.0/src/user_auth_client.egg-info/SOURCES.txt +18 -0
- user_auth_client-0.1.0/src/user_auth_client.egg-info/dependency_links.txt +1 -0
- user_auth_client-0.1.0/src/user_auth_client.egg-info/requires.txt +17 -0
- user_auth_client-0.1.0/src/user_auth_client.egg-info/top_level.txt +1 -0
- user_auth_client-0.1.0/tests/test_client.py +56 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: user-auth-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for user authentication: signup, login, reset password, and profile.
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/user-auth-client
|
|
8
|
+
Project-URL: Documentation, https://github.com/yourusername/user-auth-client#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/yourusername/user-auth-client
|
|
10
|
+
Keywords: auth,login,signup,password-reset,profile,api-client
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: requests>=2.28.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
28
|
+
Provides-Extra: django
|
|
29
|
+
Provides-Extra: fastapi
|
|
30
|
+
Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
|
|
31
|
+
Provides-Extra: flask
|
|
32
|
+
Requires-Dist: flask>=2.0.0; extra == "flask"
|
|
33
|
+
Provides-Extra: all
|
|
34
|
+
Requires-Dist: fastapi>=0.100.0; extra == "all"
|
|
35
|
+
Requires-Dist: flask>=2.0.0; extra == "all"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# User Auth Client
|
|
39
|
+
|
|
40
|
+
Python client for user authentication: **signup**, **login**, **reset password**, and **get profile**. Designed to work with any REST auth API and ready to publish on PyPI.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install user-auth-client
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For framework integrations (optional):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install user-auth-client[django] # Django
|
|
52
|
+
pip install user-auth-client[fastapi] # FastAPI
|
|
53
|
+
pip install user-auth-client[flask] # Flask
|
|
54
|
+
pip install user-auth-client[all] # FastAPI + Flask
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from user_auth_client import AuthClient, SignupData, LoginData
|
|
61
|
+
|
|
62
|
+
client = AuthClient(base_url="https://your-api.example.com")
|
|
63
|
+
|
|
64
|
+
# Sign up
|
|
65
|
+
client.signup(SignupData(email="user@example.com", password="secret", username="jane"))
|
|
66
|
+
|
|
67
|
+
# Log in (stores token automatically if API returns access_token)
|
|
68
|
+
client.login(LoginData(email="user@example.com", password="secret"))
|
|
69
|
+
|
|
70
|
+
# Get profile (uses stored token)
|
|
71
|
+
profile = client.get_profile()
|
|
72
|
+
print(profile.email, profile.username)
|
|
73
|
+
|
|
74
|
+
# Reset password
|
|
75
|
+
client.reset_password("user@example.com")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Django
|
|
81
|
+
|
|
82
|
+
Use the client in views with the request's `Authorization: Bearer <token>` header.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install user-auth-client[django]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**settings.py:**
|
|
89
|
+
```python
|
|
90
|
+
AUTH_API_URL = "https://your-auth-api.com"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**views.py:**
|
|
94
|
+
```python
|
|
95
|
+
from django.http import JsonResponse
|
|
96
|
+
from django.conf import settings
|
|
97
|
+
from user_auth_client.django import get_client_from_request, get_current_user
|
|
98
|
+
|
|
99
|
+
def login_view(request):
|
|
100
|
+
from user_auth_client import AuthClient, LoginData
|
|
101
|
+
client = AuthClient(base_url=settings.AUTH_API_URL)
|
|
102
|
+
data = client.login(LoginData(email=request.POST["email"], password=request.POST["password"]))
|
|
103
|
+
return JsonResponse(data)
|
|
104
|
+
|
|
105
|
+
def profile_view(request):
|
|
106
|
+
user = get_current_user(request, settings.AUTH_API_URL)
|
|
107
|
+
if not user:
|
|
108
|
+
return JsonResponse({"error": "Unauthorized"}, status=401)
|
|
109
|
+
return JsonResponse({"email": user.email, "username": user.username})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## FastAPI
|
|
115
|
+
|
|
116
|
+
Use dependency injection to get an `AuthClient` and the current user.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install user-auth-client[fastapi]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from fastapi import Depends
|
|
124
|
+
from user_auth_client import AuthClient
|
|
125
|
+
from user_auth_client.fastapi import auth_client_depends, get_current_user_depends
|
|
126
|
+
from user_auth_client.models import UserProfile
|
|
127
|
+
|
|
128
|
+
BASE_URL = "https://your-auth-api.com"
|
|
129
|
+
get_client = auth_client_depends(BASE_URL)
|
|
130
|
+
get_user = get_current_user_depends(BASE_URL)
|
|
131
|
+
|
|
132
|
+
@app.post("/auth/signup")
|
|
133
|
+
def signup(client: AuthClient = Depends(get_client)):
|
|
134
|
+
return {"ok": True}
|
|
135
|
+
|
|
136
|
+
@app.get("/profile")
|
|
137
|
+
def profile(user: UserProfile = Depends(get_user)):
|
|
138
|
+
return {"email": user.email, "id": user.id}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Flask
|
|
144
|
+
|
|
145
|
+
Attach the client and current user with `init_app`, or use helpers per view.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
pip install user-auth-client[flask]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Option A – init_app (g.auth_client, g.current_user):**
|
|
152
|
+
```python
|
|
153
|
+
from flask import Flask, g
|
|
154
|
+
from user_auth_client.flask import init_app
|
|
155
|
+
|
|
156
|
+
app = Flask(__name__)
|
|
157
|
+
app.config["AUTH_API_URL"] = "https://your-auth-api.com"
|
|
158
|
+
init_app(app)
|
|
159
|
+
|
|
160
|
+
@app.route("/profile")
|
|
161
|
+
def profile():
|
|
162
|
+
if g.current_user is None:
|
|
163
|
+
return {"error": "Unauthorized"}, 401
|
|
164
|
+
return {"email": g.current_user.email}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Option B – per-view helper:**
|
|
168
|
+
```python
|
|
169
|
+
from user_auth_client.flask import get_current_user
|
|
170
|
+
|
|
171
|
+
@app.route("/me")
|
|
172
|
+
def me():
|
|
173
|
+
user = get_current_user(app.config["AUTH_API_URL"])
|
|
174
|
+
if not user:
|
|
175
|
+
return {"error": "Unauthorized"}, 401
|
|
176
|
+
return {"email": user.email}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## API reference
|
|
182
|
+
|
|
183
|
+
### `AuthClient(base_url, timeout=30.0, headers=None)`
|
|
184
|
+
|
|
185
|
+
- **base_url** – Base URL of your auth API (e.g. `https://api.example.com`).
|
|
186
|
+
- **timeout** – Request timeout in seconds.
|
|
187
|
+
- **headers** – Optional default headers for every request.
|
|
188
|
+
|
|
189
|
+
### Methods
|
|
190
|
+
|
|
191
|
+
| Method | Description |
|
|
192
|
+
|--------|-------------|
|
|
193
|
+
| `signup(data: SignupData)` | Register a new user. Returns API response dict. |
|
|
194
|
+
| `login(data: LoginData)` | Log in with email/password. Sets token if response contains `access_token`. |
|
|
195
|
+
| `reset_password(email: str)` | Request password reset for the given email. |
|
|
196
|
+
| `get_profile() -> UserProfile` | Get current user profile (requires token). |
|
|
197
|
+
| `set_token(token: str)` | Set bearer token manually. |
|
|
198
|
+
| `clear_token()` | Clear stored token. |
|
|
199
|
+
|
|
200
|
+
### Models
|
|
201
|
+
|
|
202
|
+
- **SignupData** – `email`, `password`, optional `username`, optional `extra` dict.
|
|
203
|
+
- **LoginData** – `email`, `password`.
|
|
204
|
+
- **UserProfile** – `id`, `email`, `username`, `raw` (full response dict).
|
|
205
|
+
|
|
206
|
+
### Exceptions
|
|
207
|
+
|
|
208
|
+
- `AuthClientError` – Base error; has `message` and optional `status_code`.
|
|
209
|
+
- `AuthClientConnectionError` – Network/connection failure.
|
|
210
|
+
- `AuthClientAuthError` – Auth failure (401/403).
|
|
211
|
+
|
|
212
|
+
### Example with custom token
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
# If your login endpoint returns a different key:
|
|
216
|
+
resp = client.login(LoginData(email="u@x.com", password="pwd"))
|
|
217
|
+
client.set_token(resp["token"]) # or resp["jwt"], etc.
|
|
218
|
+
|
|
219
|
+
profile = client.get_profile()
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Expected API shape
|
|
223
|
+
|
|
224
|
+
The client assumes your backend exposes REST endpoints like:
|
|
225
|
+
|
|
226
|
+
| Action | Method | Path | Body (JSON) |
|
|
227
|
+
|--------|--------|------|--------------|
|
|
228
|
+
| Signup | POST | `/auth/signup` | `email`, `password`, optional `username` |
|
|
229
|
+
| Login | POST | `/auth/login` | `email`, `password` |
|
|
230
|
+
| Reset password | POST | `/auth/reset-password` | `email` |
|
|
231
|
+
| Get profile | GET | `/auth/profile` | — (Bearer token) |
|
|
232
|
+
|
|
233
|
+
Login response may include `access_token`; if present, the client stores it for `get_profile()`. You can also call `set_token()` after login with a different key.
|
|
234
|
+
|
|
235
|
+
## Development
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
git clone https://github.com/yourusername/user-auth-client
|
|
239
|
+
cd user-auth-client
|
|
240
|
+
pip install -e ".[dev]"
|
|
241
|
+
pytest
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Publishing to PyPI
|
|
245
|
+
|
|
246
|
+
1. Install build and twine:
|
|
247
|
+
`pip install build twine`
|
|
248
|
+
2. Build:
|
|
249
|
+
`python -m build`
|
|
250
|
+
3. Upload (use PyPI token):
|
|
251
|
+
`twine upload dist/*`
|
|
252
|
+
|
|
253
|
+
Update `pyproject.toml` (version, `authors`, `urls`) and `README` links before publishing.
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# User Auth Client
|
|
2
|
+
|
|
3
|
+
Python client for user authentication: **signup**, **login**, **reset password**, and **get profile**. Designed to work with any REST auth API and ready to publish on PyPI.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install user-auth-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
For framework integrations (optional):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install user-auth-client[django] # Django
|
|
15
|
+
pip install user-auth-client[fastapi] # FastAPI
|
|
16
|
+
pip install user-auth-client[flask] # Flask
|
|
17
|
+
pip install user-auth-client[all] # FastAPI + Flask
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from user_auth_client import AuthClient, SignupData, LoginData
|
|
24
|
+
|
|
25
|
+
client = AuthClient(base_url="https://your-api.example.com")
|
|
26
|
+
|
|
27
|
+
# Sign up
|
|
28
|
+
client.signup(SignupData(email="user@example.com", password="secret", username="jane"))
|
|
29
|
+
|
|
30
|
+
# Log in (stores token automatically if API returns access_token)
|
|
31
|
+
client.login(LoginData(email="user@example.com", password="secret"))
|
|
32
|
+
|
|
33
|
+
# Get profile (uses stored token)
|
|
34
|
+
profile = client.get_profile()
|
|
35
|
+
print(profile.email, profile.username)
|
|
36
|
+
|
|
37
|
+
# Reset password
|
|
38
|
+
client.reset_password("user@example.com")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Django
|
|
44
|
+
|
|
45
|
+
Use the client in views with the request's `Authorization: Bearer <token>` header.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install user-auth-client[django]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**settings.py:**
|
|
52
|
+
```python
|
|
53
|
+
AUTH_API_URL = "https://your-auth-api.com"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**views.py:**
|
|
57
|
+
```python
|
|
58
|
+
from django.http import JsonResponse
|
|
59
|
+
from django.conf import settings
|
|
60
|
+
from user_auth_client.django import get_client_from_request, get_current_user
|
|
61
|
+
|
|
62
|
+
def login_view(request):
|
|
63
|
+
from user_auth_client import AuthClient, LoginData
|
|
64
|
+
client = AuthClient(base_url=settings.AUTH_API_URL)
|
|
65
|
+
data = client.login(LoginData(email=request.POST["email"], password=request.POST["password"]))
|
|
66
|
+
return JsonResponse(data)
|
|
67
|
+
|
|
68
|
+
def profile_view(request):
|
|
69
|
+
user = get_current_user(request, settings.AUTH_API_URL)
|
|
70
|
+
if not user:
|
|
71
|
+
return JsonResponse({"error": "Unauthorized"}, status=401)
|
|
72
|
+
return JsonResponse({"email": user.email, "username": user.username})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## FastAPI
|
|
78
|
+
|
|
79
|
+
Use dependency injection to get an `AuthClient` and the current user.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install user-auth-client[fastapi]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from fastapi import Depends
|
|
87
|
+
from user_auth_client import AuthClient
|
|
88
|
+
from user_auth_client.fastapi import auth_client_depends, get_current_user_depends
|
|
89
|
+
from user_auth_client.models import UserProfile
|
|
90
|
+
|
|
91
|
+
BASE_URL = "https://your-auth-api.com"
|
|
92
|
+
get_client = auth_client_depends(BASE_URL)
|
|
93
|
+
get_user = get_current_user_depends(BASE_URL)
|
|
94
|
+
|
|
95
|
+
@app.post("/auth/signup")
|
|
96
|
+
def signup(client: AuthClient = Depends(get_client)):
|
|
97
|
+
return {"ok": True}
|
|
98
|
+
|
|
99
|
+
@app.get("/profile")
|
|
100
|
+
def profile(user: UserProfile = Depends(get_user)):
|
|
101
|
+
return {"email": user.email, "id": user.id}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Flask
|
|
107
|
+
|
|
108
|
+
Attach the client and current user with `init_app`, or use helpers per view.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install user-auth-client[flask]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Option A – init_app (g.auth_client, g.current_user):**
|
|
115
|
+
```python
|
|
116
|
+
from flask import Flask, g
|
|
117
|
+
from user_auth_client.flask import init_app
|
|
118
|
+
|
|
119
|
+
app = Flask(__name__)
|
|
120
|
+
app.config["AUTH_API_URL"] = "https://your-auth-api.com"
|
|
121
|
+
init_app(app)
|
|
122
|
+
|
|
123
|
+
@app.route("/profile")
|
|
124
|
+
def profile():
|
|
125
|
+
if g.current_user is None:
|
|
126
|
+
return {"error": "Unauthorized"}, 401
|
|
127
|
+
return {"email": g.current_user.email}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Option B – per-view helper:**
|
|
131
|
+
```python
|
|
132
|
+
from user_auth_client.flask import get_current_user
|
|
133
|
+
|
|
134
|
+
@app.route("/me")
|
|
135
|
+
def me():
|
|
136
|
+
user = get_current_user(app.config["AUTH_API_URL"])
|
|
137
|
+
if not user:
|
|
138
|
+
return {"error": "Unauthorized"}, 401
|
|
139
|
+
return {"email": user.email}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## API reference
|
|
145
|
+
|
|
146
|
+
### `AuthClient(base_url, timeout=30.0, headers=None)`
|
|
147
|
+
|
|
148
|
+
- **base_url** – Base URL of your auth API (e.g. `https://api.example.com`).
|
|
149
|
+
- **timeout** – Request timeout in seconds.
|
|
150
|
+
- **headers** – Optional default headers for every request.
|
|
151
|
+
|
|
152
|
+
### Methods
|
|
153
|
+
|
|
154
|
+
| Method | Description |
|
|
155
|
+
|--------|-------------|
|
|
156
|
+
| `signup(data: SignupData)` | Register a new user. Returns API response dict. |
|
|
157
|
+
| `login(data: LoginData)` | Log in with email/password. Sets token if response contains `access_token`. |
|
|
158
|
+
| `reset_password(email: str)` | Request password reset for the given email. |
|
|
159
|
+
| `get_profile() -> UserProfile` | Get current user profile (requires token). |
|
|
160
|
+
| `set_token(token: str)` | Set bearer token manually. |
|
|
161
|
+
| `clear_token()` | Clear stored token. |
|
|
162
|
+
|
|
163
|
+
### Models
|
|
164
|
+
|
|
165
|
+
- **SignupData** – `email`, `password`, optional `username`, optional `extra` dict.
|
|
166
|
+
- **LoginData** – `email`, `password`.
|
|
167
|
+
- **UserProfile** – `id`, `email`, `username`, `raw` (full response dict).
|
|
168
|
+
|
|
169
|
+
### Exceptions
|
|
170
|
+
|
|
171
|
+
- `AuthClientError` – Base error; has `message` and optional `status_code`.
|
|
172
|
+
- `AuthClientConnectionError` – Network/connection failure.
|
|
173
|
+
- `AuthClientAuthError` – Auth failure (401/403).
|
|
174
|
+
|
|
175
|
+
### Example with custom token
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# If your login endpoint returns a different key:
|
|
179
|
+
resp = client.login(LoginData(email="u@x.com", password="pwd"))
|
|
180
|
+
client.set_token(resp["token"]) # or resp["jwt"], etc.
|
|
181
|
+
|
|
182
|
+
profile = client.get_profile()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Expected API shape
|
|
186
|
+
|
|
187
|
+
The client assumes your backend exposes REST endpoints like:
|
|
188
|
+
|
|
189
|
+
| Action | Method | Path | Body (JSON) |
|
|
190
|
+
|--------|--------|------|--------------|
|
|
191
|
+
| Signup | POST | `/auth/signup` | `email`, `password`, optional `username` |
|
|
192
|
+
| Login | POST | `/auth/login` | `email`, `password` |
|
|
193
|
+
| Reset password | POST | `/auth/reset-password` | `email` |
|
|
194
|
+
| Get profile | GET | `/auth/profile` | — (Bearer token) |
|
|
195
|
+
|
|
196
|
+
Login response may include `access_token`; if present, the client stores it for `get_profile()`. You can also call `set_token()` after login with a different key.
|
|
197
|
+
|
|
198
|
+
## Development
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
git clone https://github.com/yourusername/user-auth-client
|
|
202
|
+
cd user-auth-client
|
|
203
|
+
pip install -e ".[dev]"
|
|
204
|
+
pytest
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Publishing to PyPI
|
|
208
|
+
|
|
209
|
+
1. Install build and twine:
|
|
210
|
+
`pip install build twine`
|
|
211
|
+
2. Build:
|
|
212
|
+
`python -m build`
|
|
213
|
+
3. Upload (use PyPI token):
|
|
214
|
+
`twine upload dist/*`
|
|
215
|
+
|
|
216
|
+
Update `pyproject.toml` (version, `authors`, `urls`) and `README` links before publishing.
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "user-auth-client"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for user authentication: signup, login, reset password, and profile."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Your Name", email = "your.email@example.com" }]
|
|
12
|
+
keywords = ["auth", "login", "signup", "password-reset", "profile", "api-client"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.8",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.8"
|
|
26
|
+
dependencies = [
|
|
27
|
+
"requests>=2.28.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=7.0",
|
|
33
|
+
"pytest-cov>=4.0",
|
|
34
|
+
]
|
|
35
|
+
django = []
|
|
36
|
+
fastapi = [
|
|
37
|
+
"fastapi>=0.100.0",
|
|
38
|
+
]
|
|
39
|
+
flask = [
|
|
40
|
+
"flask>=2.0.0",
|
|
41
|
+
]
|
|
42
|
+
all = [
|
|
43
|
+
"fastapi>=0.100.0",
|
|
44
|
+
"flask>=2.0.0",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://github.com/yourusername/user-auth-client"
|
|
49
|
+
Documentation = "https://github.com/yourusername/user-auth-client#readme"
|
|
50
|
+
Repository = "https://github.com/yourusername/user-auth-client"
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.packages.find]
|
|
53
|
+
where = ["src"]
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.package-dir]
|
|
56
|
+
"" = "src"
|
|
57
|
+
|
|
58
|
+
[tool.pytest.ini_options]
|
|
59
|
+
testpaths = ["tests"]
|
|
60
|
+
|
|
61
|
+
[tool.setuptools.package-data]
|
|
62
|
+
"*" = ["py.typed"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""User authentication client: signup, login, reset password, get profile."""
|
|
2
|
+
|
|
3
|
+
from user_auth_client.client import AuthClient
|
|
4
|
+
from user_auth_client.exceptions import (
|
|
5
|
+
AuthClientError,
|
|
6
|
+
AuthClientConnectionError,
|
|
7
|
+
AuthClientAuthError,
|
|
8
|
+
)
|
|
9
|
+
from user_auth_client.models import UserProfile, SignupData, LoginData
|
|
10
|
+
|
|
11
|
+
__version__ = "0.1.0"
|
|
12
|
+
__all__ = [
|
|
13
|
+
"AuthClient",
|
|
14
|
+
"AuthClientError",
|
|
15
|
+
"AuthClientConnectionError",
|
|
16
|
+
"AuthClientAuthError",
|
|
17
|
+
"UserProfile",
|
|
18
|
+
"SignupData",
|
|
19
|
+
"LoginData",
|
|
20
|
+
"__version__",
|
|
21
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Extract Bearer token from Authorization header."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_bearer_token(authorization: Optional[str]) -> Optional[str]:
|
|
7
|
+
"""Return token from 'Bearer <token>' or None."""
|
|
8
|
+
if not authorization or not isinstance(authorization, str):
|
|
9
|
+
return None
|
|
10
|
+
parts = authorization.strip().split()
|
|
11
|
+
if len(parts) == 2 and parts[0].lower() == "bearer":
|
|
12
|
+
return parts[1] or None
|
|
13
|
+
return None
|