Skip to content

Commit 011b46b

Browse files
committed
✅ refactor(auth): add decode_jwt/decode_jwt_async to Datasy; deprecate fs.decode (#50)
- Add AuthMixin (security/auth.py) with _active_key property and merged exception handling - Add decode_jwt and decode_jwt_async methods to Datasy - 🔴 Mark fs.decode and fs.decode_async as deprecated (FutureWarning) - Add utils.py with deprecated() decorator (sync/async support) - Update migration.py go_page for Flet 0.28.x and 0.80.x compatibility - Update tests to use data.decode_jwt_async() - 📝 Update docs: basic-jwt, route-protection, api/reference, showcase, migration guide, changelog
1 parent b5c3eae commit 011b46b

File tree

14 files changed

+529
-345
lines changed

14 files changed

+529
-345
lines changed

docs/advanced/basic-jwt.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ Kzuz8LYM/PJmIWIBTo2mqDwp/Iv2EbMKw0Jjn0cgnZINs9UciQqhxX4R49I3
115115
-----END RSA PRIVATE KEY-----"""
116116
```
117117

118-
In this example we are going to do very similar with the [`Route-protection`](route-protection.md#example) example, we have only configured the `secret_key`, used the [`login`](route-protection.md#login) method `time_expiry` parameter and used the [`decode`](#decode) function of `FletEasy` to get the payload stored in the decoded client storage.
118+
In this example we are going to do very similar with the [`Route-protection`](route-protection.md#example) example, we have only configured the `secret_key`, used the [`login`](route-protection.md#login) method `time_expiry` parameter and used the [`decode_jwt`](#decode_jwt) method of `Datasy` to get the payload stored in the decoded client storage.
119119

120120
```python title="main.py" hl_lines="12-15 22 42 65-70 78"
121121
from datetime import timedelta
@@ -137,9 +137,9 @@ app = fs.FletEasy(
137137
)
138138

139139
@app.login
140-
def login_x(data: fs.Datasy):
141-
# decode payload
142-
value = fs.decode(key_login="login", data=data)
140+
async def login_x(data: fs.Datasy):
141+
# decode payload using the new Datasy method
142+
value = await data.decode_jwt(key_login="login")
143143

144144
print("value:", value)
145145

@@ -221,18 +221,26 @@ app.run()
221221
<source src="../../assets/advanced/basic-jwt-web.webm" type="video/webm" alt="Flet-Easy - basic-jwt-web">
222222
</video>
223223

224-
## decode
224+
## decode_jwt
225225

226-
Decode the jwt and update the browser sessions.
226+
Decodes the JWT stored in client storage and returns the payload. Method of `Datasy` (`data`).
227227

228-
**Parameters to use:**
228+
**Parameters:**
229229

230-
* `key_login` : key used to store the data in the client, also used in the [`login`](route-protection.md#login) method of [`Datasy`](../guide/core/datasy.md).
231-
* `data` : Object instance of the [`Datasy`](../guide/core/datasy.md) class.
230+
* `key_login` : key used to store the data in the client, also used in the [`login`](route-protection.md#login) method.
232231

233232
!!! info
234-
*Support async, example: `decode_async`.
235-
* If the function to use is async it is recommended to use `decode_async` to avoid errors.
233+
Supports async: use `await data.decode_jwt_async(key_login="...")` if the login function is async (recommended).
234+
235+
```python title="config.py"
236+
@app.login
237+
async def login_required(data: fs.Datasy) -> bool:
238+
return await data.decode_jwt_async(key_login="login")
239+
```
236240

237241
!!! note
238-
The `decode` and `decode_async` functions can be used in other parts of the code, for example: [Middleware](middleware.md)
242+
The `decode_jwt` / `decode_jwt_async` methods can also be used in other parts of the code, for example in [Middleware](middleware.md).
243+
244+
!!! warning "Deprecated"
245+
`fs.decode()` and `fs.decode_async()` are **deprecated** since `v0.4.0` and will be removed in a future version.
246+
Use `data.decode_jwt()` / `data.decode_jwt_async()` instead.

docs/advanced/route-protection.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,4 @@ Closes the sessions of all browser tabs or the device used, which has been previ
9898
**Parameters [`data.logout`](../started/how-to-use.md#methods_1):**
9999

100100
* `key` : It is the identifier to store the value in the client storage.
101+
* `next_route` : Route to redirect to after logout. If not provided, uses the `route_login` configured in `FletEasy`. (Optional)

docs/api/reference.md

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ app.run()
6464
- history_routes
6565
- login
6666
- logout
67+
- decode_jwt
68+
- decode_jwt_async
6769
- route
6870
- redirect
6971

@@ -74,15 +76,19 @@ def my_page(data: fs.Datasy):
7476
# Navigation
7577
data.go("/other-page")
7678
data.go_back()
77-
79+
7880
# Authentication
7981
data.login("token", "jwt-value")
8082
data.logout("token", next_route="/login")
81-
83+
84+
# JWT decoding (replaces fs.decode / fs.decode_async)
85+
decoded = data.decode_jwt("token") # sync
86+
# decoded = await data.decode_jwt_async("token") # async
87+
8288
# Data sharing
8389
data.share.set("key", "value")
8490
value = data.share.get("key")
85-
91+
8692
# Page access
8793
data.page.title = "New Title"
8894
data.page.update()
@@ -190,6 +196,19 @@ app = fs.FletEasy(secret_key=SecretKey(secret))
190196

191197
### JWT Functions
192198

199+
> [!warning] Deprecated since v0.4.0
200+
> `fs.decode()` and `fs.decode_async()` are deprecated. Use `data.decode_jwt()` / `data.decode_jwt_async()` (methods on [`Datasy`](#datasy)) instead.
201+
202+
::: flet_easy.Datasy.decode_jwt
203+
options:
204+
show_root_heading: true
205+
show_source: true
206+
207+
::: flet_easy.Datasy.decode_jwt_async
208+
options:
209+
show_root_heading: true
210+
show_source: true
211+
193212
::: flet_easy.decode
194213
options:
195214
show_root_heading: true
@@ -213,16 +232,16 @@ app = fs.FletEasy(secret_key=SecretKey(secret))
213232
__Quick Reference:__
214233

215234
```python
216-
from flet_easy import encode_HS256, decode, SecretKey
235+
from flet_easy import SecretKey
236+
import flet_easy as fs
217237

218-
# Encode JWT
219-
secret = SecretKey("your-secret")
220-
payload = {"user_id": 123, "role": "admin"}
221-
token = encode_HS256(payload, secret)
238+
# --- New API (v0.4.0+) ---
239+
@app.login
240+
async def login_required(data: fs.Datasy) -> bool:
241+
return await data.decode_jwt_async(key_login="login")
222242

223-
# Decode JWT
224-
decoded = decode(token, secret)
225-
print(decoded["user_id"]) # 123
243+
# --- Deprecated (will be removed) ---
244+
# value = fs.decode(key_login="login", data=data)
226245
```
227246

228247
## Event Handling

docs/changelog.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Flet-Easy changelog
22

3-
## v0.3.0 (01/03/26)
3+
## v0.3.0 (../03/26)
44

55
* **Package Reorganization:** Restructured the `flet-easy` package into logical subpackages (`core/`, `security/`, `ui/`) to improve maintainability, while preserving 100% backward compatibility for existing imports.
66

@@ -13,6 +13,10 @@
1313

1414
* Fix for compatibility with Python 3.9 ([#47](https://github.com/Daxexs/flet-easy/issues/47))
1515

16+
* Improved and fixed error response when JWT libraries are not installed.
17+
18+
* Improved error handling for `login`, `login_async`, `decode`, and `decode_async` methods in `Datasy`. ([#50](https://github.com/Daxexs/flet-easy/issues/50))
19+
1620
### New features
1721

1822
* Support for rendering new Flet Declarative UI Components (`@ft.component`) as native routes, bridging URL parameters and `Datasy` seamlessly into declarative objects. (see [flet-easy/issues/51](https://github.com/Daxexs/flet-easy/issues/51)) [[Docs](https://daxexs.github.io/flet-easy/dev/guide/add-pages/through-decorators/#using-declarative-components-ftcomponent)]
@@ -45,6 +49,10 @@
4549

4650
* `go_route(route: str)` : Use this method to navigate to a specific route. It executes directly, unlike the `data.go()` method, which returns a lambda function. ([#50](https://github.com/Daxexs/flet-easy/issues/50)) [[Docs](https://daxexs.github.io/flet-easy/dev/guide/core/datasy/#go_route-route)]
4751

52+
* `decode_jwt(key)`: Decode JWT synchronously from `Datasy` (Replaces `fs.decode()`). ([#50](https://github.com/Daxexs/flet-easy/issues/50)) [[Docs](https://daxexs.github.io/flet-easy/dev/advanced/basic-jwt/#decode_jwt)]
53+
54+
* `decode_jwt_async(key)`: Decode JWT asynchronously — preferred in `async` login decorators (Replaces `fs.decode_async()`). ([#50](https://github.com/Daxexs/flet-easy/issues/50)) [[Docs](https://daxexs.github.io/flet-easy/dev/advanced/basic-jwt/#decode_jwt_async)]
55+
4856
#### Changes in the api
4957

5058
* `go_back()`: Use this method to return to the previous path. The method is executed directly without returning a lambda function. ([#50](https://github.com/Daxexs/flet-easy/issues/50)) [[Docs](https://daxexs.github.io/flet-easy/dev/guide/core/datasy/#go_back)]

docs/examples/migration-v0.2.0-to-v0.3.0.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ This guide helps you migrate your Flet-Easy applications from version 0.2.x to 0
1111
* 🆕 **Direct Method Execution**: Simplified API for common operations
1212
* 🆕 **Python 3.9 Support**: Extended compatibility
1313
***Performance Improvements**: Optimized routing and middleware execution
14+
* 🆕 **`decode_jwt` / `decode_jwt_async`** methods added to `Datasy`, replacing the standalone `fs.decode()` / `fs.decode_async()` functions.
15+
* ⚠️ **`fs.decode` and `fs.decode_async` are deprecated** — they still work but emit a `FutureWarning` and will be removed in a future version.
1416

1517
## Breaking Changes
1618

@@ -256,14 +258,38 @@ class MyMiddleware(fs.MiddlewareRequest):
256258
def before_request(self):
257259
# Your existing middleware logic here
258260
pass
259-
261+
260262
def after_request(self):
261263
# New: Add post-processing logic
262264
pass
263265

264266
app.add_middleware(MyMiddleware)
265267
```
266268

269+
### Migrate JWT Decoding (v0.3.0+)
270+
271+
Replace the deprecated standalone functions with the new `Datasy` methods:
272+
273+
```python
274+
# Before (deprecated — emits FutureWarning)
275+
@app.login
276+
def login_required(data: fs.Datasy):
277+
return fs.decode(key_login="login", data=data)
278+
279+
@app.login
280+
async def login_required(data: fs.Datasy):
281+
return await fs.decode_async(key_login="login", data=data)
282+
283+
# After (v0.3.0+)
284+
@app.login
285+
def login_required(data: fs.Datasy) -> bool:
286+
return data.decode_jwt(key_login="login")
287+
288+
@app.login
289+
async def login_required(data: fs.Datasy) -> bool:
290+
return await data.decode_jwt_async(key_login="login")
291+
```
292+
267293
## Compatibility Notes
268294

269295
* **Backward Compatibility**: All v0.2.x code continues to work
@@ -317,6 +343,23 @@ on_click=lambda _: data.go_back()
317343
@app.page("/form", cache=True) # Enable caching
318344
```
319345

346+
### Issue: `FutureWarning` about `fs.decode` or `fs.decode_async`
347+
348+
**Problem**: Using the deprecated standalone functions.
349+
350+
```python
351+
# Deprecated
352+
value = fs.decode(key_login="login", data=data)
353+
```
354+
355+
**Solution**: Use the `Datasy` methods instead.
356+
357+
```python
358+
# Correct (v0.4.0+)
359+
value = data.decode_jwt(key_login="login") # sync
360+
value = await data.decode_jwt_async(key_login="login") # async
361+
```
362+
320363
## Performance Benefits
321364

322365
After migration, you'll benefit from:

docs/examples/v0-3-0-showcase.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,37 @@ if __name__ == "__main__":
576576
app.run()
577577
```
578578

579+
### JWT Authentication with `decode_jwt`
580+
581+
!!! note "Replaced `fs.decode` and `fs.decode_async`"
582+
583+
* **`data.decode_jwt(key)`**: Decode JWT synchronously from `Datasy`
584+
* **`data.decode_jwt_async(key)`**: Decode JWT asynchronously — preferred in `async` login decorators
585+
586+
```python
587+
import flet_easy as fs
588+
589+
app = fs.FletEasy(
590+
route_init="/home",
591+
route_login="/login",
592+
secret_key=fs.SecretKey(
593+
algorithm=fs.Algorithm.HS256,
594+
secret="your-secret-key",
595+
),
596+
auto_logout=True,
597+
)
598+
599+
# Decode JWT in the login decorator (recommended: async)
600+
@app.login
601+
async def login_required(data: fs.Datasy) -> bool:
602+
return await data.decode_jwt_async(key_login="auth_token")
603+
604+
# Equivalent sync version (uses run_task internally)
605+
@app.login
606+
def login_required_sync(data: fs.Datasy) -> bool:
607+
return data.decode_jwt(key_login="auth_token")
608+
```
609+
579610
### 🎬 Demo
580611

581612
<video controls>
@@ -622,7 +653,7 @@ if __name__ == "__main__":
622653

623654
* **`go_route()`**: Immediate navigation without lambda wrappers
624655
* **`go_back()`**: Direct execution instead of returning functions
625-
* **`logout()`**: Immediate logout action
656+
* **`logout()`**: Immediate logout action with optional `next_route`
626657
* **`page_reload()`**: Instant page refresh and state reset
627658

628659
### Python 3.9 Support

0 commit comments

Comments
 (0)