the37lab-authlib 0.1.1751371611__py3-none-any.whl → 0.1.1762438606__py3-none-any.whl

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.
the37lab_authlib/db.py CHANGED
@@ -1,15 +1,28 @@
1
1
  import psycopg2
2
2
  from psycopg2.extras import RealDictCursor
3
+ from psycopg2 import pool
3
4
  from contextlib import contextmanager
4
5
  from .models import UUIDGenerator, IntegerGenerator
5
6
 
6
7
  class Database:
7
- def __init__(self, dsn, id_type='uuid'):
8
+ def __init__(self, dsn, id_type='uuid', min_conn=1, max_conn=10):
8
9
  self.dsn = dsn
9
10
  self.id_generator = UUIDGenerator() if id_type == 'uuid' else IntegerGenerator()
10
11
  self.id_type = id_type
12
+ self.min_conn = min_conn
13
+ self.max_conn = max_conn
14
+ self._pool = None
15
+ self._init_pool()
11
16
  self._init_db()
12
17
 
18
+ def _init_pool(self):
19
+ self._pool = pool.ThreadedConnectionPool(
20
+ self.min_conn,
21
+ self.max_conn,
22
+ self.dsn,
23
+ cursor_factory=RealDictCursor
24
+ )
25
+
13
26
  def _init_db(self):
14
27
  with self.get_connection() as conn:
15
28
  with conn.cursor() as cur:
@@ -54,7 +67,7 @@ class Database:
54
67
 
55
68
  @contextmanager
56
69
  def get_connection(self):
57
- conn = psycopg2.connect(self.dsn, cursor_factory=RealDictCursor)
70
+ conn = self._pool.getconn()
58
71
  try:
59
72
  yield conn
60
73
  conn.commit()
@@ -62,7 +75,7 @@ class Database:
62
75
  conn.rollback()
63
76
  raise
64
77
  finally:
65
- conn.close()
78
+ self._pool.putconn(conn)
66
79
 
67
80
  @contextmanager
68
81
  def get_cursor(self):
@@ -71,4 +84,8 @@ class Database:
71
84
  yield cur
72
85
 
73
86
  def get_id_generator(self):
74
- return self.id_generator
87
+ return self.id_generator
88
+
89
+ def close(self):
90
+ if self._pool:
91
+ self._pool.closeall()
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: the37lab_authlib
3
+ Version: 0.1.1762438606
4
+ Summary: Python SDK for the Authlib
5
+ Author-email: the37lab <info@the37lab.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Operating System :: OS Independent
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: flask
11
+ Requires-Dist: psycopg2-binary
12
+ Requires-Dist: pyjwt
13
+ Requires-Dist: python-dotenv
14
+ Requires-Dist: requests
15
+ Requires-Dist: authlib
16
+ Requires-Dist: bcrypt
17
+ Requires-Dist: msal
@@ -0,0 +1,10 @@
1
+ the37lab_authlib/__init__.py,sha256=cFVTWL-0YIMqwOMVy1P8mOt_bQODJp-L9bfp2QQ8CTo,132
2
+ the37lab_authlib/auth.py,sha256=X-CVMIvSk3q03ccAjB1A0KGlKSVc1PI33flOdq5T0lQ,78219
3
+ the37lab_authlib/db.py,sha256=cmnmykKvq6V5e-D0HGiRN4DjFBOGB-SL1HpFjR5uyCw,3162
4
+ the37lab_authlib/decorators.py,sha256=L-gJUUwDUT2JXTptQ6XEey1LkI5RprbqzEfArWI7F8Y,1305
5
+ the37lab_authlib/exceptions.py,sha256=mdplK5sKNtagPAzSGq5NGsrQ4r-k03DKJBKx6myWwZc,317
6
+ the37lab_authlib/models.py,sha256=-PlvQlHGIsSdrH0H9Cdh_vTPlltGV8G1Z1mmGQvAg9Y,3422
7
+ the37lab_authlib-0.1.1762438606.dist-info/METADATA,sha256=zgzkQjfoi6cg5TVXEAJHU9Ui_-bh70rY1ieww7wodDQ,497
8
+ the37lab_authlib-0.1.1762438606.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ the37lab_authlib-0.1.1762438606.dist-info/top_level.txt,sha256=6Jmxw4UeLrhfJXgRKbXWY4OhxRSaMs0dKKhNCGWWSwc,17
10
+ the37lab_authlib-0.1.1762438606.dist-info/RECORD,,
@@ -1,250 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: the37lab_authlib
3
- Version: 0.1.1751371611
4
- Summary: Python SDK for the Authlib
5
- Author-email: the37lab <info@the37lab.com>
6
- Classifier: Programming Language :: Python :: 3
7
- Classifier: Operating System :: OS Independent
8
- Requires-Python: >=3.9
9
- Description-Content-Type: text/markdown
10
- Requires-Dist: flask
11
- Requires-Dist: psycopg2-binary
12
- Requires-Dist: pyjwt
13
- Requires-Dist: python-dotenv
14
- Requires-Dist: requests
15
- Requires-Dist: authlib
16
- Requires-Dist: bcrypt
17
-
18
- # AuthLib
19
-
20
- A Python authentication library that provides JWT, OAuth2, and API token authentication with PostgreSQL backend. This library is designed for seamless integration with Flask applications and provides a robust set of endpoints and utilities for user management, authentication, and API token handling.
21
-
22
- ## Table of Contents
23
- - [AuthLib](#authlib)
24
- - [Table of Contents](#table-of-contents)
25
- - [Installation](#installation)
26
- - [Quick Start](#quick-start)
27
- - [Configuration](#configuration)
28
- - [Required Parameters](#required-parameters)
29
- - [Optional Parameters](#optional-parameters)
30
- - [Example `oauth_config`:](#example-oauth_config)
31
- - [API Endpoints](#api-endpoints)
32
- - [Authentication](#authentication)
33
- - [User Management](#user-management)
34
- - [API Tokens](#api-tokens)
35
- - [Authentication Flow](#authentication-flow)
36
- - [User Object](#user-object)
37
- - [Token Management](#token-management)
38
- - [Development](#development)
39
- - [Setup](#setup)
40
- - [Database Setup](#database-setup)
41
- - [Running Tests](#running-tests)
42
- - [API Token Override for Testing](#api-token-override-for-testing)
43
- - [Usage](#usage)
44
- - [Warning](#warning)
45
- - [User Override for Testing](#user-override-for-testing)
46
- - [Usage](#usage-1)
47
- - [Warning](#warning-1)
48
-
49
- ## Installation
50
-
51
- ```bash
52
- pip install -e .
53
- ```
54
-
55
- ## Quick Start
56
-
57
- ```python
58
- from flask import Flask
59
- from authlib import AuthManager
60
-
61
- app = Flask(__name__)
62
-
63
- # Option 1: Explicit configuration
64
- auth = AuthManager(
65
- app=app,
66
- db_dsn="postgresql://user:pass@localhost/dbname",
67
- jwt_secret="your-secret-key",
68
- oauth_config={
69
- "google": {
70
- "client_id": "your-client-id",
71
- "client_secret": "your-client-secret"
72
- }
73
- }
74
- )
75
-
76
- # Option 2: Use environment variables with a prefix (e.g., AMPA_)
77
- # This will load:
78
- # AMPA_DATABASE_URL, AMPA_JWT_SECRET, AMPA_GOOGLE_CLIENT_ID, AMPA_GOOGLE_CLIENT_SECRET
79
- # auth = AuthManager(app=app, environment_prefix="AMPA")
80
-
81
- @app.route("/protected")
82
- @auth.require_auth(roles=["admin"])
83
- def protected_route():
84
- return "Protected content"
85
-
86
- @app.route("/public")
87
- @auth.public_endpoint
88
- def custom_public_route():
89
- return "Public content"
90
- ```
91
-
92
- `AuthManager`'s blueprint now registers a global error handler for
93
- `AuthError` and authenticates requests for all of its routes by default.
94
- Authenticated users are made available as `flask.g.requesting_user`.
95
- Only the login, OAuth, token refresh, registration and role listing
96
- endpoints are exempt from this check. Additional routes can be marked as
97
- public using the `@auth.public_endpoint` decorator or
98
- `auth.add_public_endpoint("auth.some_endpoint")`.
99
-
100
- ## Configuration
101
-
102
- ### Required Parameters
103
- - `app`: Flask application instance
104
- - `db_dsn`: PostgreSQL connection string
105
- - `jwt_secret`: Secret key for JWT signing
106
-
107
- ### Optional Parameters
108
- - `oauth_config`: Dictionary of OAuth provider configurations (see below)
109
- - `token_expiry`: JWT token expiry time in seconds (default: 3600)
110
- - `refresh_token_expiry`: Refresh token expiry time in seconds (default: 2592000)
111
- - `environment_prefix`: If set, loads all configuration from environment variables with this prefix (e.g., `AMPA_DATABASE_URL`, `AMPA_JWT_SECRET`, `AMPA_GOOGLE_CLIENT_ID`, `AMPA_GOOGLE_CLIENT_SECRET`). Overrides other config if set.
112
-
113
- #### Example `oauth_config`:
114
- ```python
115
- {
116
- "google": {
117
- "client_id": "...",
118
- "client_secret": "..."
119
- },
120
- "github": {
121
- "client_id": "...",
122
- "client_secret": "..."
123
- }
124
- }
125
- ```
126
-
127
- ## API Endpoints
128
-
129
- ### Authentication
130
- - `POST /api/v1/users/login` - Login with username/password
131
- - **Request:** `{ "username": "string", "password": "string" }`
132
- - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
133
- - `POST /api/v1/users/login/oauth` - Get OAuth redirect URL
134
- - **Request:** `{ "provider": "google|github|..." }`
135
- - **Response:** `{ "redirect_url": "string" }`
136
- - `GET /api/v1/users/login/oauth2callback` - OAuth callback
137
- - **Query Params:** `code`, `state`, `provider`
138
- - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
139
- - `POST /api/v1/users/token-refresh` - Refresh JWT token
140
- - **Request:** `{ "refresh_token": "jwt" }`
141
- - **Response:** `{ "token": "jwt", "refresh_token": "jwt" }`
142
-
143
- ### User Management
144
- - `POST /api/v1/users/register` - Register new user
145
- - **Request:** `{ "username": "string", "password": "string", "email": "string", ... }`
146
- - **Response:** `{ "user": { ... }, "token": "jwt", "refresh_token": "jwt" }`
147
- - `GET /api/v1/users/login/profile` - Get user profile
148
- - **Auth:** Bearer JWT
149
- - **Response:** `{ "user": { ... } }`
150
- - `GET /api/v1/users/roles` - Get available roles
151
- - **Response:** `[ "admin", "user", ... ]`
152
-
153
- ### API Tokens
154
- - `POST /api/v1/users/{user}/api-tokens` - Create API token
155
- - **Request:** `{ "name": "string", "scopes": [ ... ] }`
156
- - **Response:** `{ "token": "string", "id": "uuid", ... }`
157
- - `GET /api/v1/users/{user}/api-tokens` - List API tokens
158
- - **Response:** `[ { "id": "uuid", "name": "string", ... } ]`
159
- - `DELETE /api/v1/users/{user}/api-tokens/{token_id}` - Delete API token
160
- - **Response:** `{ "success": true }`
161
-
162
- ## Authentication Flow
163
-
164
- 1. **Login:**
165
- - User submits credentials to `/api/v1/users/login`.
166
- - Receives JWT and refresh token.
167
- 2. **Token Refresh:**
168
- - Use `/api/v1/users/token-refresh` with refresh token to get new JWT.
169
- 3. **OAuth:**
170
- - Get redirect URL from `/api/v1/users/login/oauth`.
171
- - Complete OAuth flow via `/api/v1/users/login/oauth2callback`.
172
- 4. **Protected Routes:**
173
- - All routes inside the provided blueprint are authenticated by default.
174
- The authenticated user can be accessed via `g.requesting_user`.
175
- Use `@auth.require_auth()` to protect custom routes in your application.
176
-
177
- ## User Object
178
-
179
- The user object returned by the API typically includes:
180
- ```json
181
- {
182
- "id": "uuid",
183
- "username": "string",
184
- "email": "string",
185
- "roles": ["user", "admin"],
186
- "created_at": "timestamp",
187
- "last_login": "timestamp"
188
- }
189
- ```
190
-
191
- ## Token Management
192
- - **JWT:** Used for authenticating API requests. Include in `Authorization: Bearer <token>` header.
193
- - **Refresh Token:** Used to obtain new JWTs without re-authenticating.
194
- - **API Tokens:** Long-lived tokens for programmatic access, managed per user.
195
-
196
- ## Development
197
-
198
- ### Setup
199
- 1. Clone the repository
200
- 2. Create virtual environment:
201
- ```bash
202
- python -m venv venv
203
- venv\Scripts\activate
204
- ```
205
- 3. Install dependencies:
206
- ```bash
207
- pip install -e ".[dev]"
208
- ```
209
-
210
- ### Database Setup
211
- ```bash
212
- createdb authlib
213
- python -m authlib.cli db init
214
- ```
215
-
216
- ### Running Tests
217
- ```bash
218
- pytest
219
- ```
220
-
221
- ## API Token Override for Testing
222
-
223
- For testing purposes, you can bypass the database and provide a static mapping of API tokens to usernames using the `api_tokens` argument to `AuthManager` or the `{PREFIX}API_TOKENS` environment variable.
224
-
225
- ### Usage
226
-
227
- - **Constructor argument:**
228
- ```python
229
- AuthManager(api_tokens={"token1": "user1", "token2": "user2"})
230
- ```
231
- - **Environment variable:**
232
- Set `{PREFIX}API_TOKENS` to a comma-separated list of `token:username` pairs, e.g.:
233
- ```
234
- export MYAPP_API_TOKENS="token1:user1,token2:user2"
235
- ```
236
- Replace `MYAPP` with your environment prefix.
237
-
238
- **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.
239
-
240
- ## User Override for Testing
241
-
242
- For testing purposes, you can force all authentication to return a specific user by setting the `{PREFIX}USER_OVERRIDE` environment variable:
243
-
244
- ```bash
245
- export MYAPP_USER_OVERRIDE="testuser"
246
- ```
247
-
248
- If set, all requests will be authenticated as the specified user, regardless of any tokens or credentials provided. This cannot be combined with `api_tokens` or `db_dsn`.
249
-
250
- **Warning:** This method is intended only for testing and development. Do not use this approach in production environments.
@@ -1,10 +0,0 @@
1
- the37lab_authlib/__init__.py,sha256=cFVTWL-0YIMqwOMVy1P8mOt_bQODJp-L9bfp2QQ8CTo,132
2
- the37lab_authlib/auth.py,sha256=NKQ-k2QDB4ddw6QKO76RL5WeRpX2gNcpVr6VSHX2yrc,23358
3
- the37lab_authlib/db.py,sha256=fTXxnfju0lmbFGPVbXpTMeDmJMeBgURVZTndyxyRyCc,2734
4
- the37lab_authlib/decorators.py,sha256=L-gJUUwDUT2JXTptQ6XEey1LkI5RprbqzEfArWI7F8Y,1305
5
- the37lab_authlib/exceptions.py,sha256=mdplK5sKNtagPAzSGq5NGsrQ4r-k03DKJBKx6myWwZc,317
6
- the37lab_authlib/models.py,sha256=-PlvQlHGIsSdrH0H9Cdh_vTPlltGV8G1Z1mmGQvAg9Y,3422
7
- the37lab_authlib-0.1.1751371611.dist-info/METADATA,sha256=J3mleZGwizcERDRBfrFnBW-21tTRh4nzfmWR575z3Bk,8319
8
- the37lab_authlib-0.1.1751371611.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- the37lab_authlib-0.1.1751371611.dist-info/top_level.txt,sha256=6Jmxw4UeLrhfJXgRKbXWY4OhxRSaMs0dKKhNCGWWSwc,17
10
- the37lab_authlib-0.1.1751371611.dist-info/RECORD,,