marriedtermiteblyi commited on
Commit
64ba842
·
verified ·
1 Parent(s): 7c8da2f

Upload 7 files

Browse files
Files changed (1) hide show
  1. auth.py +120 -110
auth.py CHANGED
@@ -1,110 +1,120 @@
1
- """
2
- Auth dependencies for the Space backend.
3
-
4
- Verifies JWT tokens by calling the Admin Worker's /auth/me endpoint.
5
- Results are cached in-memory (TTL-based) to avoid hitting the Worker on every request.
6
- No JWT_SECRET needed on the Space side.
7
- """
8
-
9
- import time
10
- from dataclasses import dataclass
11
-
12
- import httpx
13
- from fastapi import Request, WebSocket, HTTPException
14
-
15
- from config import ADMIN_API_URL
16
-
17
-
18
- @dataclass
19
- class AuthUser:
20
- """Authenticated user extracted from JWT."""
21
- sub: str # user ID
22
- username: str
23
- role: str # "admin" or "user"
24
-
25
-
26
- # ── Token verification cache ──
27
- # Maps token -> (AuthUser, expiry_timestamp)
28
- _token_cache: dict[str, tuple[AuthUser, float]] = {}
29
- _CACHE_TTL = 300 # 5 minutes
30
-
31
-
32
- def _cleanup_cache():
33
- """Remove expired entries from cache."""
34
- now = time.time()
35
- expired = [k for k, (_, exp) in _token_cache.items() if exp < now]
36
- for k in expired:
37
- del _token_cache[k]
38
-
39
-
40
- def verify_token(token: str) -> AuthUser | None:
41
- """Verify a token by calling Worker /auth/me, with caching."""
42
- if not token or not ADMIN_API_URL:
43
- return None
44
-
45
- # Check cache first
46
- now = time.time()
47
- cached = _token_cache.get(token)
48
- if cached:
49
- user, expiry = cached
50
- if expiry > now:
51
- return user
52
- else:
53
- del _token_cache[token]
54
-
55
- # Call Worker to verify
56
- try:
57
- resp = httpx.get(
58
- f"{ADMIN_API_URL}/auth/me",
59
- headers={"Authorization": f"Bearer {token}"},
60
- timeout=10,
61
- )
62
- if resp.status_code != 200:
63
- return None
64
-
65
- data = resp.json()
66
- user_data = data.get("user")
67
- if not user_data:
68
- return None
69
-
70
- user = AuthUser(
71
- sub=user_data.get("id", ""),
72
- username=user_data.get("username", ""),
73
- role=user_data.get("role", "user"),
74
- )
75
-
76
- if not user.sub or not user.username:
77
- return None
78
-
79
- # Cache the result
80
- _token_cache[token] = (user, now + _CACHE_TTL)
81
-
82
- # Periodic cleanup
83
- if len(_token_cache) > 100:
84
- _cleanup_cache()
85
-
86
- return user
87
-
88
- except Exception:
89
- return None
90
-
91
-
92
- def get_current_user(request: Request) -> AuthUser:
93
- """FastAPI dependency: extract and verify JWT from Authorization header."""
94
- auth_header = request.headers.get("Authorization", "")
95
- if not auth_header.startswith("Bearer "):
96
- raise HTTPException(401, "Chưa đăng nhập")
97
-
98
- token = auth_header[7:]
99
- user = verify_token(token)
100
- if not user:
101
- raise HTTPException(401, "Token không hợp lệ hoặc đã hết hạn")
102
- return user
103
-
104
-
105
- def get_ws_user(websocket: WebSocket) -> AuthUser | None:
106
- """Extract and verify JWT from WebSocket query parameter ?token=..."""
107
- token = websocket.query_params.get("token", "")
108
- if not token:
109
- return None
110
- return verify_token(token)
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Auth dependencies for the Space backend.
3
+
4
+ Verifies JWT tokens by calling the Admin Worker's /auth/me endpoint.
5
+ Results are cached in-memory (TTL-based) to avoid hitting the Worker on every request.
6
+ No JWT_SECRET needed on the Space side.
7
+ """
8
+
9
+ import time
10
+ from dataclasses import dataclass
11
+
12
+ import httpx
13
+ from fastapi import Request, WebSocket, HTTPException
14
+
15
+ from config import ADMIN_API_URL
16
+
17
+
18
+ @dataclass
19
+ class AuthUser:
20
+ """Authenticated user extracted from JWT."""
21
+ sub: str # user ID
22
+ username: str
23
+ role: str # "admin" or "user"
24
+
25
+
26
+ # ── Token verification cache ──
27
+ # Maps token -> (AuthUser, expiry_timestamp)
28
+ _token_cache: dict[str, tuple[AuthUser, float]] = {}
29
+ _CACHE_TTL = 300 # 5 minutes
30
+
31
+
32
+ def _cleanup_cache():
33
+ """Remove expired entries from cache."""
34
+ now = time.time()
35
+ expired = [k for k, (_, exp) in _token_cache.items() if exp < now]
36
+ for k in expired:
37
+ del _token_cache[k]
38
+
39
+
40
+ def verify_token(token: str) -> AuthUser | None:
41
+ """Verify a token by calling Worker /auth/me, with caching."""
42
+ if not token or not ADMIN_API_URL:
43
+ return None
44
+
45
+ # Check cache first
46
+ now = time.time()
47
+ cached = _token_cache.get(token)
48
+ if cached:
49
+ user, expiry = cached
50
+ if expiry > now:
51
+ return user
52
+ else:
53
+ del _token_cache[token]
54
+
55
+ # Call Worker to verify
56
+ try:
57
+ resp = httpx.get(
58
+ f"{ADMIN_API_URL}/auth/me",
59
+ headers={"Authorization": f"Bearer {token}"},
60
+ timeout=10,
61
+ )
62
+ if resp.status_code != 200:
63
+ return None
64
+
65
+ data = resp.json()
66
+ user_data = data.get("user")
67
+ if not user_data:
68
+ return None
69
+
70
+ user = AuthUser(
71
+ sub=user_data.get("id", ""),
72
+ username=user_data.get("username", ""),
73
+ role=user_data.get("role", "user"),
74
+ )
75
+
76
+ if not user.sub or not user.username:
77
+ return None
78
+
79
+ # Cache the result
80
+ _token_cache[token] = (user, now + _CACHE_TTL)
81
+
82
+ # Periodic cleanup
83
+ if len(_token_cache) > 100:
84
+ _cleanup_cache()
85
+
86
+ return user
87
+
88
+ except Exception:
89
+ return None
90
+
91
+
92
+ def get_current_user(request: Request) -> AuthUser:
93
+ """FastAPI dependency: extract and verify JWT from Authorization header, query token, or cookie."""
94
+ token = ""
95
+
96
+ auth_header = request.headers.get("Authorization", "")
97
+ if auth_header.startswith("Bearer "):
98
+ token = auth_header[7:]
99
+
100
+ if not token:
101
+ token = request.query_params.get("token", "")
102
+
103
+ if not token:
104
+ token = request.cookies.get("token", "")
105
+
106
+ if not token:
107
+ raise HTTPException(401, "Chưa đăng nhập")
108
+
109
+ user = verify_token(token)
110
+ if not user:
111
+ raise HTTPException(401, "Token không hợp lệ hoặc đã hết hạn")
112
+ return user
113
+
114
+
115
+ def get_ws_user(websocket: WebSocket) -> AuthUser | None:
116
+ """Extract and verify JWT from WebSocket query parameter ?token=..."""
117
+ token = websocket.query_params.get("token", "")
118
+ if not token:
119
+ return None
120
+ return verify_token(token)