feat: implement long-term user login with refresh tokens
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m23s
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m23s
- Introduced a dual-token system for user authentication: a short-lived access token and a long-lived rotating refresh token. - Created a new RefreshToken model to manage refresh tokens securely. - Updated auth_api.py to handle login, refresh, and logout processes with the new token system. - Enhanced security measures including token rotation and theft detection. - Updated frontend to handle token refresh on 401 errors and adjusted SSE authentication. - Removed CORS middleware as it's unnecessary behind the nginx proxy. - Added tests to ensure functionality and security of the new token system.
This commit is contained in:
@@ -1,11 +1,19 @@
|
||||
import os
|
||||
os.environ['DB_ENV'] = 'test'
|
||||
os.environ.setdefault('SECRET_KEY', 'test-secret-key')
|
||||
os.environ.setdefault('REFRESH_TOKEN_EXPIRY_DAYS', '90')
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
# Ensure backend root is in sys.path for imports like 'config.paths'
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
# Shared test constants — import these in test files instead of hardcoding
|
||||
TEST_SECRET_KEY = 'test-secret-key'
|
||||
TEST_REFRESH_TOKEN_EXPIRY_DAYS = 90
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def set_test_db_env():
|
||||
os.environ['DB_ENV'] = 'test'
|
||||
os.environ['DB_ENV'] = 'test'
|
||||
os.environ['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
os.environ['REFRESH_TOKEN_EXPIRY_DAYS'] = str(TEST_REFRESH_TOKEN_EXPIRY_DAYS)
|
||||
@@ -14,13 +14,13 @@ from models.user import User
|
||||
from db.db import users_db
|
||||
from config.deletion_config import MIN_THRESHOLD_HOURS, MAX_THRESHOLD_HOURS
|
||||
from tinydb import Query
|
||||
from tests.conftest import TEST_SECRET_KEY
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Create test client."""
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
with app.test_client() as client:
|
||||
yield client
|
||||
|
||||
@@ -45,7 +45,7 @@ def admin_user():
|
||||
users_db.insert(user.to_dict())
|
||||
|
||||
# Create JWT token
|
||||
token = jwt.encode({'user_id': 'admin_user'}, 'supersecretkey', algorithm='HS256')
|
||||
token = jwt.encode({'user_id': 'admin_user'}, TEST_SECRET_KEY, algorithm='HS256')
|
||||
return token
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class TestGetDeletionQueue:
|
||||
|
||||
def test_get_deletion_queue_success(self, client, admin_user, setup_deletion_queue):
|
||||
"""Test getting deletion queue returns correct users."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.get('/admin/deletion-queue')
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -147,7 +147,7 @@ class TestGetDeletionQueue:
|
||||
|
||||
def test_get_deletion_queue_invalid_token(self, client, setup_deletion_queue):
|
||||
"""Test that invalid token is rejected."""
|
||||
client.set_cookie('token', 'invalid_token')
|
||||
client.set_cookie('access_token', 'invalid_token')
|
||||
response = client.get('/admin/deletion-queue')
|
||||
|
||||
assert response.status_code == 401
|
||||
@@ -161,11 +161,11 @@ class TestGetDeletionQueue:
|
||||
# Create expired token
|
||||
expired_token = jwt.encode(
|
||||
{'user_id': 'admin_user', 'exp': datetime.now() - timedelta(hours=1)},
|
||||
'supersecretkey',
|
||||
TEST_SECRET_KEY,
|
||||
algorithm='HS256'
|
||||
)
|
||||
|
||||
client.set_cookie('token', expired_token)
|
||||
client.set_cookie('access_token', expired_token)
|
||||
response = client.get('/admin/deletion-queue')
|
||||
|
||||
assert response.status_code == 401
|
||||
@@ -192,7 +192,7 @@ class TestGetDeletionQueue:
|
||||
)
|
||||
users_db.insert(admin.to_dict())
|
||||
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.get('/admin/deletion-queue')
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -206,7 +206,7 @@ class TestGetDeletionThreshold:
|
||||
|
||||
def test_get_threshold_success(self, client, admin_user):
|
||||
"""Test getting current threshold configuration."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.get('/admin/deletion-threshold')
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -232,7 +232,7 @@ class TestUpdateDeletionThreshold:
|
||||
|
||||
def test_update_threshold_success(self, client, admin_user):
|
||||
"""Test updating threshold with valid value."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.put(
|
||||
'/admin/deletion-threshold',
|
||||
json={'threshold_hours': 168}
|
||||
@@ -245,7 +245,7 @@ class TestUpdateDeletionThreshold:
|
||||
|
||||
def test_update_threshold_validates_minimum(self, client, admin_user):
|
||||
"""Test that threshold below minimum is rejected."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.put(
|
||||
'/admin/deletion-threshold',
|
||||
json={'threshold_hours': 23}
|
||||
@@ -258,7 +258,7 @@ class TestUpdateDeletionThreshold:
|
||||
|
||||
def test_update_threshold_validates_maximum(self, client, admin_user):
|
||||
"""Test that threshold above maximum is rejected."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.put(
|
||||
'/admin/deletion-threshold',
|
||||
json={'threshold_hours': 721}
|
||||
@@ -271,7 +271,7 @@ class TestUpdateDeletionThreshold:
|
||||
|
||||
def test_update_threshold_missing_value(self, client, admin_user):
|
||||
"""Test that missing threshold value is rejected."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.put(
|
||||
'/admin/deletion-threshold',
|
||||
json={}
|
||||
@@ -284,7 +284,7 @@ class TestUpdateDeletionThreshold:
|
||||
|
||||
def test_update_threshold_invalid_type(self, client, admin_user):
|
||||
"""Test that non-integer threshold is rejected."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.put(
|
||||
'/admin/deletion-threshold',
|
||||
json={'threshold_hours': 'invalid'}
|
||||
@@ -310,7 +310,7 @@ class TestTriggerDeletionQueue:
|
||||
|
||||
def test_trigger_deletion_success(self, client, admin_user, setup_deletion_queue):
|
||||
"""Test manually triggering deletion queue."""
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.post('/admin/deletion-queue/trigger')
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -348,7 +348,7 @@ class TestTriggerDeletionQueue:
|
||||
)
|
||||
users_db.insert(admin.to_dict())
|
||||
|
||||
client.set_cookie('token', admin_user)
|
||||
client.set_cookie('access_token', admin_user)
|
||||
response = client.post('/admin/deletion-queue/trigger')
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -381,9 +381,9 @@ class TestAdminRoleValidation:
|
||||
users_db.insert(user.to_dict())
|
||||
|
||||
# Create token for non-admin
|
||||
token = jwt.encode({'user_id': 'regular_user'}, 'supersecretkey', algorithm='HS256')
|
||||
token = jwt.encode({'user_id': 'regular_user'}, TEST_SECRET_KEY, algorithm='HS256')
|
||||
|
||||
client.set_cookie('token', token)
|
||||
client.set_cookie('access_token', token)
|
||||
response = client.get('/admin/deletion-queue')
|
||||
|
||||
# Should return 403 Forbidden
|
||||
@@ -414,9 +414,9 @@ class TestAdminRoleValidation:
|
||||
users_db.insert(admin.to_dict())
|
||||
|
||||
# Create token for admin
|
||||
token = jwt.encode({'user_id': 'admin_user'}, 'supersecretkey', algorithm='HS256')
|
||||
token = jwt.encode({'user_id': 'admin_user'}, TEST_SECRET_KEY, algorithm='HS256')
|
||||
|
||||
client.set_cookie('token', token)
|
||||
client.set_cookie('access_token', token)
|
||||
response = client.get('/admin/deletion-queue')
|
||||
|
||||
# Should succeed
|
||||
@@ -439,9 +439,9 @@ class TestAdminRoleValidation:
|
||||
)
|
||||
users_db.insert(user.to_dict())
|
||||
|
||||
token = jwt.encode({'user_id': 'regular_user'}, 'supersecretkey', algorithm='HS256')
|
||||
token = jwt.encode({'user_id': 'regular_user'}, TEST_SECRET_KEY, algorithm='HS256')
|
||||
|
||||
client.set_cookie('token', token)
|
||||
client.set_cookie('access_token', token)
|
||||
response = client.put('/admin/deletion-threshold', json={'threshold_hours': 168})
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
@@ -2,10 +2,11 @@ import pytest
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from flask import Flask
|
||||
from api.auth_api import auth_api
|
||||
from db.db import users_db
|
||||
from db.db import users_db, refresh_tokens_db
|
||||
from tinydb import Query
|
||||
from models.user import User
|
||||
from datetime import datetime
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
@@ -13,7 +14,8 @@ def client():
|
||||
app = Flask(__name__)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
app.config['FRONTEND_URL'] = 'http://localhost:5173'
|
||||
with app.test_client() as client:
|
||||
yield client
|
||||
@@ -54,7 +56,10 @@ def test_login_with_correct_password(client):
|
||||
data = {'email': 'test@example.com', 'password': 'password123'}
|
||||
response = client.post('/auth/login', json=data)
|
||||
assert response.status_code == 200
|
||||
assert 'token' in response.headers.get('Set-Cookie', '')
|
||||
cookies = response.headers.getlist('Set-Cookie')
|
||||
cookie_str = ' '.join(cookies)
|
||||
assert 'access_token=' in cookie_str
|
||||
assert 'refresh_token=' in cookie_str
|
||||
|
||||
def test_login_with_incorrect_password(client):
|
||||
"""Test login fails with incorrect password."""
|
||||
@@ -116,18 +121,30 @@ def test_reset_password_invalidates_existing_jwt(client):
|
||||
|
||||
login_response = client.post('/auth/login', json={'email': 'test@example.com', 'password': 'oldpassword123'})
|
||||
assert login_response.status_code == 200
|
||||
login_cookie = login_response.headers.get('Set-Cookie', '')
|
||||
assert 'token=' in login_cookie
|
||||
old_token = login_cookie.split('token=', 1)[1].split(';', 1)[0]
|
||||
login_cookies = login_response.headers.getlist('Set-Cookie')
|
||||
login_cookie_str = ' '.join(login_cookies)
|
||||
assert 'access_token=' in login_cookie_str
|
||||
# Extract the old access token
|
||||
old_token = None
|
||||
for c in login_cookies:
|
||||
if c.startswith('access_token='):
|
||||
old_token = c.split('access_token=', 1)[1].split(';', 1)[0]
|
||||
break
|
||||
assert old_token
|
||||
|
||||
reset_response = client.post('/auth/reset-password', json={'token': 'validtoken2', 'password': 'newpassword123'})
|
||||
assert reset_response.status_code == 200
|
||||
reset_cookie = reset_response.headers.get('Set-Cookie', '')
|
||||
assert 'token=' in reset_cookie
|
||||
reset_cookies = reset_response.headers.getlist('Set-Cookie')
|
||||
reset_cookie_str = ' '.join(reset_cookies)
|
||||
assert 'access_token=' in reset_cookie_str
|
||||
|
||||
# Verify all refresh tokens for this user are deleted
|
||||
user_dict = users_db.get(Query().email == 'test@example.com')
|
||||
user_tokens = refresh_tokens_db.search(Query().user_id == user_dict['id'])
|
||||
assert len(user_tokens) == 0
|
||||
|
||||
# Set the old token as a cookie and test that it's now invalid
|
||||
client.set_cookie('token', old_token)
|
||||
client.set_cookie('access_token', old_token)
|
||||
me_response = client.get('/auth/me')
|
||||
assert me_response.status_code == 401
|
||||
assert me_response.json['code'] == 'INVALID_TOKEN'
|
||||
|
||||
@@ -7,13 +7,15 @@ from models.user import User
|
||||
from werkzeug.security import generate_password_hash
|
||||
from datetime import datetime, timedelta
|
||||
import jwt
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
app = Flask(__name__)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
yield client
|
||||
|
||||
@@ -70,12 +72,13 @@ def test_me_marked_for_deletion(client):
|
||||
payload = {
|
||||
'email': email,
|
||||
'user_id': user.id,
|
||||
'token_version': user.token_version,
|
||||
'exp': datetime.utcnow() + timedelta(hours=24)
|
||||
}
|
||||
token = jwt.encode(payload, 'supersecretkey', algorithm='HS256')
|
||||
token = jwt.encode(payload, TEST_SECRET_KEY, algorithm='HS256')
|
||||
|
||||
# Make request with token cookie
|
||||
client.set_cookie('token', token)
|
||||
client.set_cookie('access_token', token)
|
||||
response = client.get('/auth/me')
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
|
||||
from flask import Flask
|
||||
@@ -33,8 +34,9 @@ def login_and_set_cookie(client):
|
||||
resp = client.post('/auth/login', json={"email": TEST_EMAIL, "password": TEST_PASSWORD})
|
||||
assert resp.status_code == 200
|
||||
# Set cookie for subsequent requests
|
||||
token = resp.headers.get("Set-Cookie")
|
||||
assert token and "token=" in token
|
||||
cookies = resp.headers.getlist("Set-Cookie")
|
||||
cookie_str = ' '.join(cookies)
|
||||
assert cookie_str and "access_token=" in cookie_str
|
||||
# Flask test client automatically handles cookies
|
||||
|
||||
@pytest.fixture
|
||||
@@ -43,7 +45,8 @@ def client():
|
||||
app.register_blueprint(child_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
login_and_set_cookie(client)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Tests for child override API endpoints and integration."""
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from flask import Flask
|
||||
from unittest.mock import patch, MagicMock
|
||||
@@ -61,7 +62,8 @@ def client():
|
||||
app.register_blueprint(child_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
@@ -34,7 +35,8 @@ def client():
|
||||
app.register_blueprint(chore_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
login_and_set_cookie(client)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from werkzeug.security import generate_password_hash
|
||||
from datetime import date as date_type
|
||||
@@ -42,7 +43,8 @@ def client():
|
||||
app.register_blueprint(child_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
login_and_set_cookie(client)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
@@ -53,7 +54,8 @@ def client():
|
||||
app.register_blueprint(chore_schedule_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
add_test_child()
|
||||
|
||||
@@ -5,6 +5,7 @@ import time
|
||||
from config.paths import get_user_image_dir
|
||||
from PIL import Image as PILImage
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from flask import Flask
|
||||
@@ -38,8 +39,9 @@ def add_test_user():
|
||||
def login_and_set_cookie(client):
|
||||
resp = client.post('/auth/login', json={"email": TEST_EMAIL, "password": TEST_PASSWORD})
|
||||
assert resp.status_code == 200
|
||||
token = resp.headers.get("Set-Cookie")
|
||||
assert token and "token=" in token
|
||||
cookies = resp.headers.getlist("Set-Cookie")
|
||||
cookie_str = ' '.join(cookies)
|
||||
assert cookie_str and "access_token=" in cookie_str
|
||||
|
||||
def safe_remove(path):
|
||||
try:
|
||||
@@ -67,7 +69,8 @@ def client():
|
||||
app.register_blueprint(image_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as c:
|
||||
add_test_user()
|
||||
remove_test_data()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
@@ -34,7 +35,8 @@ def client():
|
||||
app.register_blueprint(kindness_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
login_and_set_cookie(client)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
@@ -34,7 +35,8 @@ def client():
|
||||
app.register_blueprint(penalty_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
login_and_set_cookie(client)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
@@ -30,8 +31,9 @@ def add_test_user():
|
||||
def login_and_set_cookie(client):
|
||||
resp = client.post('/auth/login', json={"email": TEST_EMAIL, "password": TEST_PASSWORD})
|
||||
assert resp.status_code == 200
|
||||
token = resp.headers.get("Set-Cookie")
|
||||
assert token and "token=" in token
|
||||
cookies = resp.headers.getlist("Set-Cookie")
|
||||
cookie_str = ' '.join(cookies)
|
||||
assert cookie_str and "access_token=" in cookie_str
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
@@ -39,7 +41,8 @@ def client():
|
||||
app.register_blueprint(reward_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
login_and_set_cookie(client)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
import os
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
@@ -29,8 +30,9 @@ def add_test_user():
|
||||
def login_and_set_cookie(client):
|
||||
resp = client.post('/auth/login', json={"email": TEST_EMAIL, "password": TEST_PASSWORD})
|
||||
assert resp.status_code == 200
|
||||
token = resp.headers.get("Set-Cookie")
|
||||
assert token and "token=" in token
|
||||
cookies = resp.headers.getlist("Set-Cookie")
|
||||
cookie_str = ' '.join(cookies)
|
||||
assert cookie_str and "access_token=" in cookie_str
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
@@ -38,7 +40,8 @@ def client():
|
||||
app.register_blueprint(task_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
with app.test_client() as client:
|
||||
add_test_user()
|
||||
login_and_set_cookie(client)
|
||||
|
||||
@@ -7,6 +7,7 @@ from db.db import users_db
|
||||
from tinydb import Query
|
||||
import jwt
|
||||
from werkzeug.security import generate_password_hash
|
||||
from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
|
||||
# Test user credentials
|
||||
TEST_EMAIL = "usertest@example.com"
|
||||
@@ -50,9 +51,10 @@ def login_and_get_token(client, email, password):
|
||||
"""Login and extract JWT token from response."""
|
||||
resp = client.post('/auth/login', json={"email": email, "password": password})
|
||||
assert resp.status_code == 200
|
||||
# Extract token from Set-Cookie header
|
||||
set_cookie = resp.headers.get("Set-Cookie")
|
||||
assert set_cookie and "token=" in set_cookie
|
||||
# Verify auth cookies are set
|
||||
cookies = resp.headers.getlist('Set-Cookie')
|
||||
cookie_str = ' '.join(cookies)
|
||||
assert 'access_token=' in cookie_str
|
||||
# Flask test client automatically handles cookies
|
||||
return resp
|
||||
|
||||
@@ -63,7 +65,8 @@ def client():
|
||||
app.register_blueprint(user_api)
|
||||
app.register_blueprint(auth_api, url_prefix='/auth')
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
app.config['SECRET_KEY'] = TEST_SECRET_KEY
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
|
||||
app.config['FRONTEND_URL'] = 'http://localhost:5173' # Needed for email_sender
|
||||
with app.test_client() as client:
|
||||
add_test_users()
|
||||
@@ -200,7 +203,7 @@ def test_mark_for_deletion_clears_tokens(authenticated_client):
|
||||
def test_mark_for_deletion_with_invalid_jwt(client):
|
||||
"""Test marking for deletion with invalid JWT token."""
|
||||
# Set invalid cookie manually
|
||||
client.set_cookie('token', 'invalid.jwt.token')
|
||||
client.set_cookie('access_token', 'invalid.jwt.token')
|
||||
|
||||
response = client.post('/user/mark-for-deletion', json={})
|
||||
assert response.status_code == 401
|
||||
|
||||
Reference in New Issue
Block a user