diff --git a/backend/tests/api/test_auth_refresh.py b/backend/tests/api/test_auth_refresh.py index 7607c19..96914e5 100644 --- a/backend/tests/api/test_auth_refresh.py +++ b/backend/tests/api/test_auth_refresh.py @@ -60,3 +60,17 @@ def test_refresh_token_wrong_type(client): response = client.post("/api/v1/auth/refresh") assert response.status_code == 401 assert response.json()["detail"] == "Invalid token type" + +def test_protected_endpoint_rejects_refresh_token(client): + """Regression test: Ensure refresh tokens cannot be used to access protected endpoints.""" + user_id = "test-user-id" + refresh_token = create_refresh_token({"sub": user_id}) + + # Attempt to access /auth/me with a refresh token in the cookie + client.cookies.set("access_token", refresh_token) + + response = client.get("/api/v1/auth/me") + + # Should be rejected with 401 + assert response.status_code == 401 + assert "Cannot use refresh token for this endpoint" in response.json()["detail"] diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index dec7513..b55e4a4 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -17,9 +17,9 @@ export const registerUnauthorizedCallback = (callback: () => void) => { // State to manage multiple concurrent refreshes let isRefreshing = false let refreshSubscribers: ((token: string) => void)[] = [] -let refreshErrorSubscribers: ((error: any) => void)[] = [] +let refreshErrorSubscribers: ((error: unknown) => void)[] = [] -const subscribeTokenRefresh = (onSuccess: (token: string) => void, onError: (error: any) => void) => { +const subscribeTokenRefresh = (onSuccess: (token: string) => void, onError: (error: unknown) => void) => { refreshSubscribers.push(onSuccess) refreshErrorSubscribers.push(onError) } @@ -30,7 +30,7 @@ const onRefreshed = (token: string) => { refreshErrorSubscribers = [] } -const onRefreshFailed = (error: any) => { +const onRefreshFailed = (error: unknown) => { refreshErrorSubscribers.forEach((callback) => callback(error)) refreshSubscribers = [] refreshErrorSubscribers = [] @@ -52,7 +52,7 @@ api.interceptors.response.use( // Wait for the current refresh to complete return new Promise((resolve, reject) => { subscribeTokenRefresh( - (_token) => resolve(api(originalRequest)), + () => resolve(api(originalRequest)), (err) => reject(err) ) }) @@ -72,7 +72,7 @@ api.interceptors.response.use( // Retry the original request return api(originalRequest) - } catch (refreshError) { + } catch (refreshError: unknown) { isRefreshing = false onRefreshFailed(refreshError) console.error("Reactive refresh failed:", refreshError)