Implement /auth/refresh endpoint #37
@@ -66,8 +66,15 @@ class UserResponse(BaseModel):
|
|||||||
# --- Helpers ---
|
# --- Helpers ---
|
||||||
|
|
||||||
def _create_access_token(data: dict) -> str:
|
def _create_access_token(data: dict) -> str:
|
||||||
"""Create a signed access token using RS256."""
|
"""Create a signed access token using RS256.
|
||||||
|
|
||||||
|
Includes a unique ``jti`` claim so that concurrent/same-second calls
|
||||||
|
(e.g. back-to-back /refresh requests) still produce distinct tokens —
|
||||||
|
without it, two tokens minted for the same user within the same
|
||||||
|
second are byte-identical since ``sub``/``exp`` are the only claims.
|
||||||
|
"""
|
||||||
to_encode = data.copy()
|
to_encode = data.copy()
|
||||||
|
to_encode["jti"] = str(uuid.uuid4())
|
||||||
expire = datetime.now(timezone.utc) + timedelta(
|
expire = datetime.now(timezone.utc) + timedelta(
|
||||||
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
|
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class TestRefresh:
|
|||||||
|
|
||||||
def test_refresh_success(self):
|
def test_refresh_success(self):
|
||||||
"""A valid refresh token returns a new access token."""
|
"""A valid refresh token returns a new access token."""
|
||||||
refresh_token = self._register_user("refreshuser", "secret123")
|
refresh_token = self._register_user("refep_refreshuser", "secret123")
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
resp = client.post(
|
resp = client.post(
|
||||||
"/api/auth/refresh",
|
"/api/auth/refresh",
|
||||||
@@ -82,7 +82,7 @@ class TestRefresh:
|
|||||||
|
|
||||||
def test_refresh_returns_different_access_token(self):
|
def test_refresh_returns_different_access_token(self):
|
||||||
"""Refreshing produces a new access token (different from any previous)."""
|
"""Refreshing produces a new access token (different from any previous)."""
|
||||||
refresh_token = self._register_user("refreshtoken", "secret123")
|
refresh_token = self._register_user("refep_refreshtoken", "secret123")
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|
||||||
# First refresh
|
# First refresh
|
||||||
@@ -140,7 +140,7 @@ class TestRefresh:
|
|||||||
client.post(
|
client.post(
|
||||||
"/api/auth/register",
|
"/api/auth/register",
|
||||||
json={
|
json={
|
||||||
"username": "loginrefuser",
|
"username": "refep_loginrefuser",
|
||||||
"email": "loginref@example.com",
|
"email": "loginref@example.com",
|
||||||
"password": "secret123",
|
"password": "secret123",
|
||||||
"date_of_birth": "2000-01-01",
|
"date_of_birth": "2000-01-01",
|
||||||
@@ -149,7 +149,7 @@ class TestRefresh:
|
|||||||
# Login to get a refresh token
|
# Login to get a refresh token
|
||||||
login_resp = client.post(
|
login_resp = client.post(
|
||||||
"/api/auth/login",
|
"/api/auth/login",
|
||||||
data={"username": "loginrefuser", "password": "secret123"},
|
data={"username": "refep_loginrefuser", "password": "secret123"},
|
||||||
)
|
)
|
||||||
assert login_resp.status_code == 200
|
assert login_resp.status_code == 200
|
||||||
refresh_token = login_resp.json()["refresh_token"]
|
refresh_token = login_resp.json()["refresh_token"]
|
||||||
@@ -166,7 +166,7 @@ class TestRefresh:
|
|||||||
"""The access token returned by /refresh is a valid JWT."""
|
"""The access token returned by /refresh is a valid JWT."""
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
|
|
||||||
refresh_token = self._register_user("jwttoken", "secret123")
|
refresh_token = self._register_user("refep_jwttoken", "secret123")
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
resp = client.post(
|
resp = client.post(
|
||||||
"/api/auth/refresh",
|
"/api/auth/refresh",
|
||||||
@@ -190,7 +190,7 @@ class TestRefresh:
|
|||||||
|
|
||||||
def test_refresh_expires_minutes_in_response(self):
|
def test_refresh_expires_minutes_in_response(self):
|
||||||
"""The response includes access_token_expires_minutes."""
|
"""The response includes access_token_expires_minutes."""
|
||||||
refresh_token = self._register_user("expiretest", "secret123")
|
refresh_token = self._register_user("refep_expiretest", "secret123")
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
resp = client.post(
|
resp = client.post(
|
||||||
"/api/auth/refresh",
|
"/api/auth/refresh",
|
||||||
|
|||||||
Reference in New Issue
Block a user