Skip to content

Commit fcb0fe6

Browse files
committed
refactor(flask): Replace _request_ctx_stack with g for claims storage
Updated the claims storage mechanism in the Flask integration to use Flask's `g` object instead of `_request_ctx_stack`. This change improves code readability and aligns with Flask's recommended practices for storing request-specific data. refactor(tests): Update tests to reflect claims storage change Modified test cases to accommodate the change in claims storage from `_request_ctx_stack` to `g`, ensuring that the tests accurately reflect the new implementation. chore(tox): Update poetry install commands to include Flask extras Adjusted the `tox.ini` configuration to install Flask-related dependencies during testing and type checking, ensuring that the environment is correctly set up for Flask integration.
1 parent ffed5c0 commit fcb0fe6

File tree

3 files changed

+28
-28
lines changed

3 files changed

+28
-28
lines changed

descope/flask/__init__.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import uuid
55
from functools import wraps
66

7-
from flask import Response, _request_ctx_stack, redirect, request
7+
from flask import Response, redirect, request, g
88

99
from .. import (
1010
COOKIE_DATA_NAME,
@@ -143,7 +143,7 @@ def decorated(*args, **kwargs):
143143
return Response("Access denied", 401)
144144

145145
# Save the claims on the context execute the original API
146-
_request_ctx_stack.top.claims = jwt_response
146+
g.claims = jwt_response
147147
response = f(*args, **kwargs)
148148

149149
if jwt_response.get(COOKIE_DATA_NAME, None):
@@ -181,7 +181,7 @@ def decorated(*args, **kwargs):
181181
return Response("Unauthorized", 401)
182182

183183
# Save the claims on the context execute the original API
184-
_request_ctx_stack.top.claims = jwt_response
184+
g.claims = jwt_response
185185
response = f(*args, **kwargs)
186186

187187
set_cookie_on_response(
@@ -194,7 +194,6 @@ def decorated(*args, **kwargs):
194194
jwt_response[REFRESH_SESSION_TOKEN_NAME],
195195
jwt_response[COOKIE_DATA_NAME],
196196
)
197-
198197
return response
199198

200199
return decorated
@@ -224,7 +223,7 @@ def decorated(*args, **kwargs):
224223
return Response("Unauthorized", 401)
225224

226225
# Save the claims on the context execute the original API
227-
_request_ctx_stack.top.claims = jwt_response
226+
g.claims = jwt_response
228227
response = f(*args, **kwargs)
229228

230229
set_cookie_on_response(
@@ -267,7 +266,7 @@ def decorated(*args, **kwargs):
267266
return Response("Unauthorized", 401)
268267

269268
# Save the claims on the context execute the original API
270-
_request_ctx_stack.top.claims = jwt_response
269+
g.claims = jwt_response
271270
response = f(*args, **kwargs)
272271

273272
set_cookie_on_response(
@@ -310,7 +309,7 @@ def decorated(*args, **kwargs):
310309
return Response("Unauthorized", 401)
311310

312311
# Save the claims on the context execute the original API
313-
_request_ctx_stack.top.claims = jwt_response
312+
g.claims = jwt_response
314313
response = f(*args, **kwargs)
315314

316315
set_cookie_on_response(
@@ -400,7 +399,7 @@ def decorated(*args, **kwargs):
400399
return Response("Unauthorized", 401)
401400

402401
# Save the claims on the context execute the original API
403-
_request_ctx_stack.top.claims = jwt_response
402+
g.claims = jwt_response
404403
response = f(*args, **kwargs)
405404

406405
set_cookie_on_response(

tests/test_flask.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22
from unittest.mock import patch
33

4-
from flask import Flask, Response, _request_ctx_stack
4+
from flask import Flask, Response, g
55

66
from descope import (
77
COOKIE_DATA_NAME,
@@ -58,11 +58,10 @@ def test_set_cookie_on_response(self):
5858
response = Response("test")
5959
token = {"drn": "DST", "jwt": "test-token"}
6060
cookie_data = {"domain": "localhost", "maxAge": 3600, "path": "/"}
61-
62-
result = set_cookie_on_response(response, token, cookie_data)
61+
set_cookie_on_response(response, token, cookie_data)
6362

6463
# Verify cookie was set (Flask sets cookies in headers)
65-
self.assertIsInstance(result, Response)
64+
self.assertIsInstance(response, Response)
6665

6766
def test_otp_signup_decorator_success(self):
6867
"""Test OTP signup decorator with valid data"""
@@ -426,7 +425,7 @@ def test_oauth_decorator_success(self):
426425

427426
@self.app.route("/oauth")
428427
@descope_oauth(self.descope_client)
429-
def oauth():
428+
def oauth(*args, **kwargs):
430429
return Response("OAuth initiated", 200)
431430

432431
with patch.object(self.descope_client.oauth, "start") as mock_start:
@@ -466,15 +465,15 @@ def logout():
466465
with patch.object(self.descope_client, "logout") as mock_logout:
467466
mock_logout.return_value = None
468467

468+
# Set the refresh and session cookies using the test client
469+
self.client.set_cookie(REFRESH_SESSION_COOKIE_NAME, "mock-refresh-token")
470+
self.client.set_cookie(SESSION_COOKIE_NAME, "mock-session-token")
469471
response = self.client.post(
470472
"/logout",
471-
headers={
472-
"Cookie": f"{REFRESH_SESSION_COOKIE_NAME}=mock-refresh-token",
473-
"Host": "localhost",
474-
},
475473
)
476474

477475
self.assertEqual(response.status_code, 200)
476+
# The decorator should extract the refresh token from cookies and call logout
478477
mock_logout.assert_called_once_with("mock-refresh-token")
479478

480479
def test_logout_decorator_auth_exception(self):
@@ -520,15 +519,17 @@ def login():
520519

521520
def test_full_login_decorator_missing_redirect_url(self):
522521
"""Test full login decorator with missing redirect URL"""
523-
with self.assertRaises(AuthException) as context:
524522

525-
@descope_full_login(
526-
project_id="test-project",
527-
flow_id="sign-up-or-in",
528-
success_redirect_url="",
529-
)
530-
def login():
531-
return Response("Login page", 200)
523+
@descope_full_login(
524+
project_id="test-project",
525+
flow_id="sign-up-or-in",
526+
success_redirect_url="",
527+
)
528+
def login():
529+
return Response("Login page", 200)
530+
531+
with self.assertRaises(AuthException) as context:
532+
login()
532533

533534
self.assertEqual(context.exception.status_code, 500)
534535
self.assertIn("Missing success_redirect_url", str(context.exception))
@@ -540,7 +541,7 @@ def test_request_context_claims_storage(self):
540541
@descope_validate_auth(self.descope_client)
541542
def context_test():
542543
# Access the claims stored in context
543-
claims = getattr(_request_ctx_stack.top, "claims", None)
544+
claims = getattr(g, "claims", None)
544545
if claims:
545546
return Response(f"Claims found: {claims.get('permissions', [])}", 200)
546547
return Response("No claims", 400)

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ skip_install = true
1212
pass_env =
1313
COVERAGE_FILE
1414
commands_pre =
15-
poetry install --only main,tests
15+
poetry install --only main,tests -E Flask
1616
commands =
1717
poetry run coverage run -m pytest {posargs:tests}
1818
allowlist_externals =
1919
poetry
2020

2121
[testenv:type]
2222
commands_pre =
23-
poetry install --only main,types
23+
poetry install --only main,types -E Flask
2424
commands =
2525
poetry run mypy {posargs:descope tests samples}
2626

0 commit comments

Comments
 (0)