mirror of
https://github.com/langgenius/dify.git
synced 2026-05-25 19:00:43 -04:00
fix(auth): preserve phase field in _TokenData so reset-password / change-email phase-bound checks don't 400 (#36116) (#36117)
Signed-off-by: vuko <alexander.vukovic@seqis.com>
This commit is contained in:
@@ -39,6 +39,7 @@ class _TokenData(TypedDict, total=False):
|
||||
token_type: str
|
||||
code: str
|
||||
old_email: str
|
||||
phase: str
|
||||
|
||||
|
||||
_token_data_adapter: TypeAdapter[_TokenData] = TypeAdapter(_TokenData)
|
||||
|
||||
58
api/tests/unit_tests/libs/test_token_manager.py
Normal file
58
api/tests/unit_tests/libs/test_token_manager.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Regression tests for the `_TokenData` TypedDict used by
|
||||
`libs.helper.TokenManager`.
|
||||
|
||||
These tests guard the contract that every field a caller writes via
|
||||
`generate_token` survives the TypedDict-validated round-trip performed
|
||||
by `get_token_data`. Specifically, the `phase` field that the console
|
||||
and web `forgot-password` + `change-email` controllers depend on for
|
||||
the security check introduced in PR #35425 (GHSA-4q3w-q5mc-45rq) must
|
||||
be preserved — otherwise downstream `if data.get("phase", "") != "reset"`
|
||||
checks always fail with `InvalidTokenError`.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
# pyright: reportPrivateUsage=false
|
||||
from libs.helper import _token_data_adapter
|
||||
|
||||
|
||||
def test_token_data_adapter_preserves_phase_field() -> None:
|
||||
"""`phase` written by callers like generate_reset_password_token must
|
||||
survive the TypedDict-validated round-trip in get_token_data.
|
||||
|
||||
Regression: PR #34380 introduced `_TokenData` but did not list
|
||||
`phase`, so the TypeAdapter silently dropped it and the security
|
||||
gate from PR #35425 (GHSA-4q3w-q5mc-45rq) always failed.
|
||||
"""
|
||||
payload = {
|
||||
"account_id": None,
|
||||
"email": "user@example.com",
|
||||
"token_type": "reset_password",
|
||||
"code": "123456",
|
||||
"phase": "reset",
|
||||
}
|
||||
data = dict(_token_data_adapter.validate_json(json.dumps(payload)))
|
||||
|
||||
assert data.get("phase") == "reset", (
|
||||
"phase field was stripped by the _TokenData TypedDict adapter; "
|
||||
"the forgot-password phase-bound check (PR #35425) will always fail."
|
||||
)
|
||||
|
||||
|
||||
def test_token_data_adapter_preserves_change_email_payload() -> None:
|
||||
"""Sanity round-trip for the change-email flow: every field set by
|
||||
`generate_change_email_token` must come back, including the phase
|
||||
string the controller branches on."""
|
||||
payload = {
|
||||
"account_id": "acc-1",
|
||||
"email": "new@example.com",
|
||||
"token_type": "change_email",
|
||||
"code": "654321",
|
||||
"old_email": "old@example.com",
|
||||
"phase": "verify_old_email",
|
||||
}
|
||||
data = dict(_token_data_adapter.validate_json(json.dumps(payload)))
|
||||
|
||||
assert data.get("old_email") == "old@example.com"
|
||||
assert data.get("phase") == "verify_old_email"
|
||||
Reference in New Issue
Block a user