fix(auth): Address high and medium priority security and build findings
This commit is contained in:
@@ -39,6 +39,14 @@ async def get_current_user(request: Request, token: str = Depends(oauth2_scheme)
|
||||
payload = decode_access_token(token)
|
||||
if payload is None:
|
||||
raise credentials_exception
|
||||
|
||||
# Security Fix: Reject refresh tokens for standard API access
|
||||
if payload.get("type") == "refresh":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Cannot use refresh token for this endpoint",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
user_id: str | None = payload.get("sub")
|
||||
if user_id is None:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional, Any, List
|
||||
from jose import JWTError, jwt
|
||||
@@ -56,7 +57,9 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -
|
||||
to_encode.update({
|
||||
"exp": expire,
|
||||
"iat": now,
|
||||
"iss": "ea-chatbot-api"
|
||||
"iss": "ea-chatbot-api",
|
||||
"type": "access",
|
||||
"jti": str(uuid.uuid4())
|
||||
})
|
||||
encoded_jwt = jwt.encode(to_encode, settings.secret_key, algorithm=settings.algorithm)
|
||||
return encoded_jwt
|
||||
@@ -84,7 +87,8 @@ def create_refresh_token(data: dict, expires_delta: Optional[timedelta] = None)
|
||||
"exp": expire,
|
||||
"iat": now,
|
||||
"iss": "ea-chatbot-api",
|
||||
"type": "refresh"
|
||||
"type": "refresh",
|
||||
"jti": str(uuid.uuid4())
|
||||
})
|
||||
encoded_jwt = jwt.encode(to_encode, settings.secret_key, algorithm=settings.algorithm)
|
||||
return encoded_jwt
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AuthService } from '@/services/auth'
|
||||
* It proactively refreshes the session to prevent expiration while the user is active.
|
||||
*/
|
||||
export function useSilentRefresh(isAuthenticated: boolean) {
|
||||
const timerRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
try {
|
||||
|
||||
@@ -4,7 +4,6 @@ import './index.css'
|
||||
import App from './App.tsx'
|
||||
import { TooltipProvider } from "@/components/ui/tooltip"
|
||||
import { BrowserRouter } from "react-router-dom"
|
||||
import { ThemeProvider } from "./components/theme-provider"
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
|
||||
@@ -17,14 +17,23 @@ export const registerUnauthorizedCallback = (callback: () => void) => {
|
||||
// State to manage multiple concurrent refreshes
|
||||
let isRefreshing = false
|
||||
let refreshSubscribers: ((token: string) => void)[] = []
|
||||
let refreshErrorSubscribers: ((error: any) => void)[] = []
|
||||
|
||||
const subscribeTokenRefresh = (callback: (token: string) => void) => {
|
||||
refreshSubscribers.push(callback)
|
||||
const subscribeTokenRefresh = (onSuccess: (token: string) => void, onError: (error: any) => void) => {
|
||||
refreshSubscribers.push(onSuccess)
|
||||
refreshErrorSubscribers.push(onError)
|
||||
}
|
||||
|
||||
const onRefreshed = (token: string) => {
|
||||
refreshSubscribers.forEach((callback) => callback(token))
|
||||
refreshSubscribers = []
|
||||
refreshErrorSubscribers = []
|
||||
}
|
||||
|
||||
const onRefreshFailed = (error: any) => {
|
||||
refreshErrorSubscribers.forEach((callback) => callback(error))
|
||||
refreshSubscribers = []
|
||||
refreshErrorSubscribers = []
|
||||
}
|
||||
|
||||
// Add a response interceptor to handle 401s
|
||||
@@ -41,11 +50,11 @@ api.interceptors.response.use(
|
||||
if (error.response?.status === 401 && !isAuthEndpoint && !isRefreshEndpoint && !originalRequest._retry) {
|
||||
if (isRefreshing) {
|
||||
// Wait for the current refresh to complete
|
||||
return new Promise((resolve) => {
|
||||
subscribeTokenRefresh((token) => {
|
||||
// Re-run the original request
|
||||
resolve(api(originalRequest))
|
||||
})
|
||||
return new Promise((resolve, reject) => {
|
||||
subscribeTokenRefresh(
|
||||
(_token) => resolve(api(originalRequest)),
|
||||
(err) => reject(err)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -65,7 +74,7 @@ api.interceptors.response.use(
|
||||
return api(originalRequest)
|
||||
} catch (refreshError) {
|
||||
isRefreshing = false
|
||||
refreshSubscribers = []
|
||||
onRefreshFailed(refreshError)
|
||||
console.error("Reactive refresh failed:", refreshError)
|
||||
|
||||
// Final failure - session is dead
|
||||
|
||||
Reference in New Issue
Block a user