update
Browse files- backend_api.py +21 -1
- frontend/src/lib/api.ts +19 -11
backend_api.py
CHANGED
|
@@ -294,7 +294,27 @@ def get_auth_from_header(authorization: Optional[str] = None):
|
|
| 294 |
username = parts[2] if len(parts) > 2 else "user"
|
| 295 |
return MockAuth(token, username)
|
| 296 |
|
| 297 |
-
# Regular
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
return MockAuth(token, None)
|
| 299 |
|
| 300 |
|
|
|
|
| 294 |
username = parts[2] if len(parts) > 2 else "user"
|
| 295 |
return MockAuth(token, username)
|
| 296 |
|
| 297 |
+
# Regular OAuth access token passed directly - try to fetch username from HF
|
| 298 |
+
# This happens when frontend sends OAuth token after OAuth callback
|
| 299 |
+
if token and len(token) > 20:
|
| 300 |
+
try:
|
| 301 |
+
from huggingface_hub import HfApi
|
| 302 |
+
hf_api = HfApi(token=token)
|
| 303 |
+
user_info = hf_api.whoami()
|
| 304 |
+
username = (
|
| 305 |
+
user_info.get("preferred_username") or
|
| 306 |
+
user_info.get("name") or
|
| 307 |
+
user_info.get("sub") or
|
| 308 |
+
"user"
|
| 309 |
+
)
|
| 310 |
+
print(f"[Auth] Fetched username from OAuth token: {username}")
|
| 311 |
+
return MockAuth(token, username)
|
| 312 |
+
except Exception as e:
|
| 313 |
+
print(f"[Auth] Could not fetch username from OAuth token: {e}")
|
| 314 |
+
# Return with token but no username - deployment will try to fetch it
|
| 315 |
+
return MockAuth(token, None)
|
| 316 |
+
|
| 317 |
+
# Fallback: token with no username
|
| 318 |
return MockAuth(token, None)
|
| 319 |
|
| 320 |
|
frontend/src/lib/api.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
// API client for AnyCoder backend
|
| 2 |
|
| 3 |
import axios, { AxiosInstance } from 'axios';
|
| 4 |
-
import { getStoredSessionToken } from './auth'; // NEW: Import session token
|
| 5 |
import type {
|
| 6 |
Model,
|
| 7 |
AuthStatus,
|
|
@@ -55,12 +54,8 @@ class ApiClient {
|
|
| 55 |
|
| 56 |
// Add auth token to requests if available
|
| 57 |
this.client.interceptors.request.use((config) => {
|
| 58 |
-
//
|
| 59 |
-
|
| 60 |
-
if (sessionToken) {
|
| 61 |
-
config.headers.Authorization = `Bearer ${sessionToken}`;
|
| 62 |
-
} else if (this.token) {
|
| 63 |
-
// Fallback to OAuth token if no session token
|
| 64 |
config.headers.Authorization = `Bearer ${this.token}`;
|
| 65 |
}
|
| 66 |
return config;
|
|
@@ -71,10 +66,24 @@ class ApiClient {
|
|
| 71 |
(response) => response,
|
| 72 |
(error) => {
|
| 73 |
// Handle 401 errors (expired/invalid authentication)
|
|
|
|
| 74 |
if (error.response && error.response.status === 401) {
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
localStorage.removeItem('hf_oauth_token');
|
|
|
|
| 78 |
localStorage.removeItem('hf_user_info');
|
| 79 |
this.token = null;
|
| 80 |
|
|
@@ -280,8 +289,7 @@ class ApiClient {
|
|
| 280 |
method: 'POST',
|
| 281 |
headers: {
|
| 282 |
'Content-Type': 'application/json',
|
| 283 |
-
...(
|
| 284 |
-
this.token ? { 'Authorization': `Bearer ${this.token}` } : {}),
|
| 285 |
},
|
| 286 |
body: JSON.stringify(request),
|
| 287 |
signal: abortController.signal,
|
|
|
|
| 1 |
// API client for AnyCoder backend
|
| 2 |
|
| 3 |
import axios, { AxiosInstance } from 'axios';
|
|
|
|
| 4 |
import type {
|
| 5 |
Model,
|
| 6 |
AuthStatus,
|
|
|
|
| 54 |
|
| 55 |
// Add auth token to requests if available
|
| 56 |
this.client.interceptors.request.use((config) => {
|
| 57 |
+
// ALWAYS use OAuth token primarily, session token is for backend tracking only
|
| 58 |
+
if (this.token) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
config.headers.Authorization = `Bearer ${this.token}`;
|
| 60 |
}
|
| 61 |
return config;
|
|
|
|
| 66 |
(response) => response,
|
| 67 |
(error) => {
|
| 68 |
// Handle 401 errors (expired/invalid authentication)
|
| 69 |
+
// ONLY log out on specific auth errors, not all 401s
|
| 70 |
if (error.response && error.response.status === 401) {
|
| 71 |
+
const errorData = error.response.data;
|
| 72 |
+
const errorMessage = errorData?.detail || errorData?.message || '';
|
| 73 |
+
|
| 74 |
+
// Only log out if it's an authentication/session issue
|
| 75 |
+
// Don't log out for permission errors on specific resources
|
| 76 |
+
const shouldLogout =
|
| 77 |
+
errorMessage.includes('Authentication required') ||
|
| 78 |
+
errorMessage.includes('Invalid token') ||
|
| 79 |
+
errorMessage.includes('Token expired') ||
|
| 80 |
+
errorMessage.includes('Session expired') ||
|
| 81 |
+
error.config?.url?.includes('/auth/');
|
| 82 |
+
|
| 83 |
+
if (shouldLogout && typeof window !== 'undefined') {
|
| 84 |
+
// Clear ALL authentication data including session token
|
| 85 |
localStorage.removeItem('hf_oauth_token');
|
| 86 |
+
localStorage.removeItem('hf_session_token');
|
| 87 |
localStorage.removeItem('hf_user_info');
|
| 88 |
this.token = null;
|
| 89 |
|
|
|
|
| 289 |
method: 'POST',
|
| 290 |
headers: {
|
| 291 |
'Content-Type': 'application/json',
|
| 292 |
+
...(this.token ? { 'Authorization': `Bearer ${this.token}` } : {}),
|
|
|
|
| 293 |
},
|
| 294 |
body: JSON.stringify(request),
|
| 295 |
signal: abortController.signal,
|