Ali2206 commited on
Commit
655896f
·
verified ·
1 Parent(s): 563a11a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -48
app.py CHANGED
@@ -1,69 +1,83 @@
1
- from fastapi import FastAPI, HTTPException, status, Request
2
  from fastapi.middleware.cors import CORSMiddleware
3
- from pydantic import BaseModel, EmailStr, constr
4
- import hashlib
5
- import os
6
- import certifi
7
  from motor.motor_asyncio import AsyncIOMotorClient
 
 
 
8
 
9
- app = FastAPI()
10
-
11
- # === CORS Configuration ===
12
- app.add_middleware(
13
- CORSMiddleware,
14
- allow_origins=["*"], # In production, replace with specific domains
15
- allow_credentials=True,
16
- allow_methods=["POST", "GET"],
17
- allow_headers=["Authorization", "Content-Type"],
18
- )
19
 
20
- # === Secure MongoDB Setup ===
21
  MONGO_URI = os.getenv("MONGO_URI")
22
  if not MONGO_URI:
23
- raise RuntimeError("Missing MONGO_URI in environment")
24
 
 
25
  client = AsyncIOMotorClient(MONGO_URI, tls=True, tlsCAFile=certifi.where())
26
  db = client["cps_db"]
27
- users_collection = db.get_collection("users")
28
 
29
- # === Utility: Strong Hashing ===
30
- def hash_password(password: str) -> str:
31
- return hashlib.sha256(password.encode()).hexdigest()
 
 
 
 
 
 
32
 
33
- # === Pydantic Models with Validation ===
34
- class SignupForm(BaseModel):
35
- email: EmailStr
36
- password: constr(min_length=8)
37
 
38
- class LoginForm(BaseModel):
39
- email: EmailStr
40
- password: str
41
 
42
- # === Signup Endpoint ===
43
- @app.post("/signup", status_code=status.HTTP_201_CREATED)
44
- async def signup(data: SignupForm):
45
- email = data.email.lower().strip()
46
- hashed_password = hash_password(data.password)
47
 
48
- if await users_collection.find_one({"email": email}):
49
- raise HTTPException(status_code=409, detail="Email already registered")
 
 
 
 
50
 
51
- await users_collection.insert_one({"email": email, "password": hashed_password})
52
- return {"success": True, "message": "Account created successfully"}
 
 
53
 
54
- # === Login Endpoint ===
55
- @app.post("/login")
56
- async def login(data: LoginForm):
 
 
 
 
57
  email = data.email.lower().strip()
58
- hashed_password = hash_password(data.password)
 
 
 
 
 
59
 
60
- user = await users_collection.find_one({"email": email, "password": hashed_password})
61
- if not user:
62
- raise HTTPException(status_code=401, detail="Invalid email or password")
 
 
63
 
64
- return {"success": True, "message": "Login successful"}
 
65
 
66
- # === Health Check ===
67
  @app.get("/")
68
- async def root():
69
- return {"message": " FastAPI MongoDB Auth API is live"}
 
1
+ from fastapi import FastAPI, HTTPException, Depends
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.security import OAuth2PasswordRequestForm
4
+ from pydantic import BaseModel
5
+ from jose import JWTError, jwt
6
+ from passlib.context import CryptContext
7
  from motor.motor_asyncio import AsyncIOMotorClient
8
+ import certifi
9
+ import os
10
+ import datetime
11
 
12
+ # ENV
13
+ SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key") # 🔐 Use HuggingFace secrets
14
+ ALGORITHM = "HS256"
15
+ ACCESS_TOKEN_EXPIRE_MINUTES = 30
 
 
 
 
 
 
16
 
 
17
  MONGO_URI = os.getenv("MONGO_URI")
18
  if not MONGO_URI:
19
+ raise RuntimeError("MONGO_URI not set")
20
 
21
+ # MongoDB
22
  client = AsyncIOMotorClient(MONGO_URI, tls=True, tlsCAFile=certifi.where())
23
  db = client["cps_db"]
24
+ users_collection = db["users"]
25
 
26
+ # App setup
27
+ app = FastAPI()
28
+ app.add_middleware(
29
+ CORSMiddleware,
30
+ allow_origins=["*"], # Limit in production
31
+ allow_credentials=True,
32
+ allow_methods=["*"],
33
+ allow_headers=["*"],
34
+ )
35
 
36
+ # Hashing
37
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
 
 
38
 
39
+ def verify_password(plain, hashed):
40
+ return pwd_context.verify(plain, hashed)
 
41
 
42
+ def hash_password(password):
43
+ return pwd_context.hash(password)
 
 
 
44
 
45
+ # JWT utils
46
+ def create_access_token(data: dict, expires_delta: datetime.timedelta = None):
47
+ to_encode = data.copy()
48
+ expire = datetime.datetime.utcnow() + (expires_delta or datetime.timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
49
+ to_encode.update({"exp": expire})
50
+ return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
51
 
52
+ # Schemas
53
+ class SignupForm(BaseModel):
54
+ email: str
55
+ password: str
56
 
57
+ class TokenResponse(BaseModel):
58
+ access_token: str
59
+ token_type: str
60
+
61
+ # Routes
62
+ @app.post("/signup")
63
+ async def signup(data: SignupForm):
64
  email = data.email.lower().strip()
65
+ existing = await users_collection.find_one({"email": email})
66
+ if existing:
67
+ raise HTTPException(status_code=409, detail="Email already exists")
68
+ hashed_pw = hash_password(data.password)
69
+ await users_collection.insert_one({"email": email, "password": hashed_pw})
70
+ return {"success": True, "message": "Account created"}
71
 
72
+ @app.post("/login", response_model=TokenResponse)
73
+ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
74
+ user = await users_collection.find_one({"email": form_data.username.lower().strip()})
75
+ if not user or not verify_password(form_data.password, user["password"]):
76
+ raise HTTPException(status_code=401, detail="Invalid credentials")
77
 
78
+ access_token = create_access_token(data={"sub": user["email"]})
79
+ return {"access_token": access_token, "token_type": "bearer"}
80
 
 
81
  @app.get("/")
82
+ def root():
83
+ return {"message": "🛡️ Auth-secured FastAPI + MongoDB running"}