MukeshKapoor25 commited on
Commit
975cfc8
·
1 Parent(s): ed83da2

refactor(jwt): unify jwt configuration and token expiration handling

Browse files

- Consolidate JWT configuration in settings with unified environment variables
- Remove hardcoded expiration values and use settings defaults
- Add backward compatibility for legacy JWT config variables
- Update token generation to use new configuration approach

app/core/config.py CHANGED
@@ -12,9 +12,23 @@ class Settings:
12
  CACHE_URI: str = os.getenv("CACHE_URI")
13
  CACHE_K: str = os.getenv("CACHE_K")
14
 
15
- # JWT
16
- SECRET_KEY: str = os.getenv("SECRET_KEY", "B00Kmyservice@7")
17
- ALGORITHM: str = os.getenv("ALGORITHM", "HS256")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  # Twilio SMS
20
  TWILIO_ACCOUNT_SID: str = os.getenv("TWILIO_ACCOUNT_SID")
 
12
  CACHE_URI: str = os.getenv("CACHE_URI")
13
  CACHE_K: str = os.getenv("CACHE_K")
14
 
15
+ # JWT (Unified across services)
16
+ # Prefer JWT_* envs; fall back to legacy names to ensure compatibility
17
+ JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY") or os.getenv("SECRET_KEY", "B00Kmyservice@7")
18
+ JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM") or os.getenv("ALGORITHM", "HS256")
19
+ JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = int(
20
+ os.getenv("JWT_ACCESS_TOKEN_EXPIRE_MINUTES", os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "480"))
21
+ )
22
+ JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = int(
23
+ os.getenv("JWT_REFRESH_TOKEN_EXPIRE_DAYS", os.getenv("REFRESH_TOKEN_EXPIRE_DAYS", "7"))
24
+ )
25
+ JWT_TEMP_TOKEN_EXPIRE_MINUTES: int = int(
26
+ os.getenv("JWT_TEMP_TOKEN_EXPIRE_MINUTES", os.getenv("TEMP_TOKEN_EXPIRE_MINUTES", "10"))
27
+ )
28
+
29
+ # Backward compatibility: keep legacy attributes pointing to unified values
30
+ SECRET_KEY: str = JWT_SECRET_KEY
31
+ ALGORITHM: str = JWT_ALGORITHM
32
 
33
  # Twilio SMS
34
  TWILIO_ACCOUNT_SID: str = os.getenv("TWILIO_ACCOUNT_SID")
app/routers/user_router.py CHANGED
@@ -254,7 +254,7 @@ async def oauth_login_handler(payload: OAuthLoginRequest, request: Request):
254
  "verified": True,
255
  "provider": payload.provider,
256
  "user_info": user_info
257
- }, expires_minutes=10)
258
 
259
  # Log temporary OAuth session token (truncated)
260
  logger.info(f"OAuth temp token generated (first 25 chars): {temp_token[:25]}...")
@@ -327,19 +327,19 @@ async def refresh_token_handler(refresh_token: str = Depends(get_bearer_token)):
327
  # Create new access token
328
  access_token = create_access_token({
329
  "sub": customer_id
330
- }, expires_minutes=60) # 1 hour access token
331
 
332
  # Optionally create a new refresh token (refresh token rotation)
333
  new_refresh_token = create_refresh_token({
334
  "sub": customer_id
335
- }, expires_days=7) # 7 days refresh token
336
 
337
  logger.info(f"New access token generated for user: {customer_id}")
338
 
339
  return {
340
  "access_token": access_token,
341
  "token_type": "bearer",
342
- "expires_in": 3600, # 1 hour in seconds
343
  "refresh_token": new_refresh_token,
344
  "customer_id": customer_id
345
  }
 
254
  "verified": True,
255
  "provider": payload.provider,
256
  "user_info": user_info
257
+ })
258
 
259
  # Log temporary OAuth session token (truncated)
260
  logger.info(f"OAuth temp token generated (first 25 chars): {temp_token[:25]}...")
 
327
  # Create new access token
328
  access_token = create_access_token({
329
  "sub": customer_id
330
+ })
331
 
332
  # Optionally create a new refresh token (refresh token rotation)
333
  new_refresh_token = create_refresh_token({
334
  "sub": customer_id
335
+ })
336
 
337
  logger.info(f"New access token generated for user: {customer_id}")
338
 
339
  return {
340
  "access_token": access_token,
341
  "token_type": "bearer",
342
+ "expires_in": settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60,
343
  "refresh_token": new_refresh_token,
344
  "customer_id": customer_id
345
  }
app/schemas/user_schema.py CHANGED
@@ -121,7 +121,7 @@ class OAuthLoginRequest(BaseModel):
121
  class TokenResponse(BaseModel):
122
  access_token: str
123
  token_type: str = "bearer"
124
- expires_in: Optional[int] = 28800 # 8 hours in seconds
125
  refresh_token: Optional[str] = None
126
  customer_id: Optional[str] = None
127
  name: Optional[str] = None
 
121
  class TokenResponse(BaseModel):
122
  access_token: str
123
  token_type: str = "bearer"
124
+ expires_in: Optional[int] = None
125
  refresh_token: Optional[str] = None
126
  customer_id: Optional[str] = None
127
  name: Optional[str] = None
app/services/user_service.py CHANGED
@@ -108,18 +108,18 @@ class UserService:
108
  logger.info("Creating JWT token for authenticated user")
109
  token_data = {
110
  "sub": user.get("customer_id"),
111
- "exp": datetime.utcnow() + timedelta(hours=8)
112
  }
113
  logger.info(f"Token data: {token_data}")
114
 
115
- access_token = jwt.encode(token_data, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
116
 
117
  # Create refresh token
118
  refresh_token_data = {
119
  "sub": user.get("customer_id"),
120
- "exp": datetime.utcnow() + timedelta(days=7)
121
  }
122
- refresh_token = jwt.encode(refresh_token_data, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
123
 
124
  # Log generated tokens (truncated for security)
125
  logger.info(f"Access token generated (first 25 chars): {access_token}...")
@@ -131,7 +131,7 @@ class UserService:
131
  "access_token": access_token,
132
  "refresh_token": refresh_token,
133
  "token_type": "bearer",
134
- "expires_in": 28800,
135
  "customer_id": user.get("customer_id"),
136
  "name": user.get("name"),
137
  "email": user.get("email"),
@@ -218,16 +218,16 @@ class UserService:
218
 
219
  token_data = {
220
  "sub": existing_user["customer_id"],
221
- "exp": datetime.utcnow() + timedelta(hours=8)
222
  }
223
- access_token = jwt.encode(token_data, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
224
 
225
  # Create refresh token
226
  refresh_token_data = {
227
  "sub": existing_user["customer_id"],
228
- "exp": datetime.utcnow() + timedelta(days=7)
229
  }
230
- refresh_token = jwt.encode(refresh_token_data, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
231
 
232
  # Log generated tokens for existing linked user (truncated)
233
  logger.info(f"Access token for existing user (first 25 chars): {access_token[:25]}...")
@@ -237,7 +237,7 @@ class UserService:
237
  "access_token": access_token,
238
  "refresh_token": refresh_token,
239
  "token_type": "bearer",
240
- "expires_in": 28800
241
  }
242
 
243
  # Generate a new UUID for customer_id instead of provider-prefixed ID
@@ -284,17 +284,17 @@ class UserService:
284
 
285
  token_data = {
286
  "sub": customer_id,
287
- "exp": datetime.utcnow() + timedelta(hours=8)
288
  }
289
 
290
- access_token = jwt.encode(token_data, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
291
 
292
  # Create refresh token
293
  refresh_token_data = {
294
  "sub": customer_id,
295
- "exp": datetime.utcnow() + timedelta(days=7)
296
  }
297
- refresh_token = jwt.encode(refresh_token_data, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
298
 
299
  # Log generated tokens for new registration (truncated)
300
  logger.info(f"Access token on register (first 25 chars): {access_token[:25]}...")
@@ -304,7 +304,7 @@ class UserService:
304
  "access_token": access_token,
305
  "refresh_token": refresh_token,
306
  "token_type": "bearer",
307
- "expires_in": 28800,
308
  "customer_id": customer_id,
309
  "name": data.name,
310
  "email": data.email,
 
108
  logger.info("Creating JWT token for authenticated user")
109
  token_data = {
110
  "sub": user.get("customer_id"),
111
+ "exp": datetime.utcnow() + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
112
  }
113
  logger.info(f"Token data: {token_data}")
114
 
115
+ access_token = jwt.encode(token_data, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
116
 
117
  # Create refresh token
118
  refresh_token_data = {
119
  "sub": user.get("customer_id"),
120
+ "exp": datetime.utcnow() + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
121
  }
122
+ refresh_token = jwt.encode(refresh_token_data, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
123
 
124
  # Log generated tokens (truncated for security)
125
  logger.info(f"Access token generated (first 25 chars): {access_token}...")
 
131
  "access_token": access_token,
132
  "refresh_token": refresh_token,
133
  "token_type": "bearer",
134
+ "expires_in": settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60,
135
  "customer_id": user.get("customer_id"),
136
  "name": user.get("name"),
137
  "email": user.get("email"),
 
218
 
219
  token_data = {
220
  "sub": existing_user["customer_id"],
221
+ "exp": datetime.utcnow() + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
222
  }
223
+ access_token = jwt.encode(token_data, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
224
 
225
  # Create refresh token
226
  refresh_token_data = {
227
  "sub": existing_user["customer_id"],
228
+ "exp": datetime.utcnow() + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
229
  }
230
+ refresh_token = jwt.encode(refresh_token_data, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
231
 
232
  # Log generated tokens for existing linked user (truncated)
233
  logger.info(f"Access token for existing user (first 25 chars): {access_token[:25]}...")
 
237
  "access_token": access_token,
238
  "refresh_token": refresh_token,
239
  "token_type": "bearer",
240
+ "expires_in": settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60
241
  }
242
 
243
  # Generate a new UUID for customer_id instead of provider-prefixed ID
 
284
 
285
  token_data = {
286
  "sub": customer_id,
287
+ "exp": datetime.utcnow() + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
288
  }
289
 
290
+ access_token = jwt.encode(token_data, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
291
 
292
  # Create refresh token
293
  refresh_token_data = {
294
  "sub": customer_id,
295
+ "exp": datetime.utcnow() + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
296
  }
297
+ refresh_token = jwt.encode(refresh_token_data, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
298
 
299
  # Log generated tokens for new registration (truncated)
300
  logger.info(f"Access token on register (first 25 chars): {access_token[:25]}...")
 
304
  "access_token": access_token,
305
  "refresh_token": refresh_token,
306
  "token_type": "bearer",
307
+ "expires_in": settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60,
308
  "customer_id": customer_id,
309
  "name": data.name,
310
  "email": data.email,
app/utils/jwt.py CHANGED
@@ -6,11 +6,14 @@ from datetime import datetime, timedelta
6
  from fastapi import Depends, HTTPException, status
7
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
8
  from typing import Optional
9
- import os
10
  import logging
11
 
12
- SECRET_KEY = os.getenv("JWT_SECRET_KEY", "secret-key-placeholder")
13
- ALGORITHM = "HS256"
 
 
 
14
 
15
  # Security scheme
16
  security = HTTPBearer()
@@ -18,7 +21,7 @@ security = HTTPBearer()
18
  # Module logger (app-level logging config applies)
19
  logger = logging.getLogger(__name__)
20
 
21
- def create_access_token(data: dict, expires_minutes: int = 60):
22
  to_encode = data.copy()
23
  expire = datetime.utcnow() + timedelta(minutes=expires_minutes)
24
  to_encode.update({"exp": expire})
@@ -34,7 +37,7 @@ def create_access_token(data: dict, expires_minutes: int = 60):
34
  )
35
  return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
36
 
37
- def create_refresh_token(data: dict, expires_days: int = 7):
38
  to_encode = data.copy()
39
  expire = datetime.utcnow() + timedelta(days=expires_days)
40
  to_encode.update({"exp": expire, "type": "refresh"})
@@ -46,7 +49,7 @@ def create_refresh_token(data: dict, expires_days: int = 7):
46
  )
47
  return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
48
 
49
- def create_temp_token(data: dict, expires_minutes: int = 10):
50
  logger.info("Creating temporary access token with short expiry")
51
  return create_access_token(data, expires_minutes=expires_minutes)
52
 
 
6
  from fastapi import Depends, HTTPException, status
7
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
8
  from typing import Optional
9
+ from app.core.config import settings
10
  import logging
11
 
12
+ SECRET_KEY = settings.JWT_SECRET_KEY
13
+ ALGORITHM = settings.JWT_ALGORITHM
14
+ ACCESS_EXPIRE_MINUTES_DEFAULT = settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES
15
+ REFRESH_EXPIRE_DAYS_DEFAULT = settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS
16
+ TEMP_EXPIRE_MINUTES_DEFAULT = settings.JWT_TEMP_TOKEN_EXPIRE_MINUTES
17
 
18
  # Security scheme
19
  security = HTTPBearer()
 
21
  # Module logger (app-level logging config applies)
22
  logger = logging.getLogger(__name__)
23
 
24
+ def create_access_token(data: dict, expires_minutes: int = ACCESS_EXPIRE_MINUTES_DEFAULT):
25
  to_encode = data.copy()
26
  expire = datetime.utcnow() + timedelta(minutes=expires_minutes)
27
  to_encode.update({"exp": expire})
 
37
  )
38
  return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
39
 
40
+ def create_refresh_token(data: dict, expires_days: int = REFRESH_EXPIRE_DAYS_DEFAULT):
41
  to_encode = data.copy()
42
  expire = datetime.utcnow() + timedelta(days=expires_days)
43
  to_encode.update({"exp": expire, "type": "refresh"})
 
49
  )
50
  return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
51
 
52
+ def create_temp_token(data: dict, expires_minutes: int = TEMP_EXPIRE_MINUTES_DEFAULT):
53
  logger.info("Creating temporary access token with short expiry")
54
  return create_access_token(data, expires_minutes=expires_minutes)
55
 
scripts/generate_token.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from pathlib import Path
3
+ from jose import jwt
4
+ from datetime import datetime, timedelta, timezone
5
+
6
+ # Ensure project root is importable so 'app' package resolves correctly
7
+ ROOT = Path(__file__).resolve().parents[1]
8
+ if str(ROOT) not in sys.path:
9
+ sys.path.insert(0, str(ROOT))
10
+
11
+ from app.core.config import settings
12
+
13
+ def main():
14
+ payload = {
15
+ "sub": "test-customer-123",
16
+ "exp": datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES),
17
+ }
18
+ token = jwt.encode(payload, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
19
+ print(token)
20
+
21
+ if __name__ == "__main__":
22
+ main()