-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.py
More file actions
179 lines (132 loc) · 4.98 KB
/
Copy pathauth.py
File metadata and controls
179 lines (132 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
import models
from database import get_db
# PASSWORD HASHING SETUP
# This creates a "password context" using argon2 algorithm
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
# JWT TOKEN SETTINGS
# Secret key to sign JWT tokens (like a password for creating tokens)
# IMPORTANT: In production, use a real secret key and keep it secret!
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256" # Algorithm to sign tokens
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # Token expires after 30 minutes
# This tells FastAPI where to look for the token
# tokenUrl="login" means tokens come from POST /login endpoint
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
#PASSWORD FUNCTIONS
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""
Check if plain password matches hashed password
Args:
plain_password: Password user typed (e.g., "secret123")
hashed_password: Hashed password from database (e.g., "$2b$12$...")
Returns:
True if passwords match, False otherwise
Example:
verify_password("secret123", "$2b$12$Eix...") → True
verify_password("wrong", "$2b$12$Eix...") → False
"""
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
"""
Hash a plain password for secure storage
Args:
password: Plain password (e.g., "secret123")
Returns:
Hashed password (e.g., "$2b$12$EixZaYVK1fsbw1ZfbX3OXe...")
Example:
get_password_hash("secret123") → "$2b$12$Eix..."
Note: Same password will create different hashes each time (salt)!
"""
return pwd_context.hash(password)
# JWT TOKEN FUNCTION
def create_access_token(data: dict) -> str:
"""
Create a JWT access token
Args:
data: Dictionary with data to encode in token (usually {"sub": username})
Returns:
JWT token as a string
Example:
create_access_token({"sub": "mukesh"})
→ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
The token includes:
- The data you provide
- Expiration time (30 minutes from now)
- Signature (so it can't be faked!)
"""
to_encode = data.copy()
# Add expiration time to token
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
# Create and sign the token
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def decode_token(token: str) -> dict:
"""
Decode and verify a JWT token
Args:
token: JWT token string
Returns:
Dictionary with decoded data
Raises:
JWTError: If token is invalid or expired
"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
# GET CURRENT USER
def get_current_user(
token: str = Depends(oauth2_scheme),
db: Session = Depends(get_db)
) -> models.User:
"""
Get the current logged-in user from JWT token
This is the MAGIC function that protects your endpoints!
How it works:
1. FastAPI extracts token from Authorization header
2. We decode the token to get username
3. We look up user in database
4. We return the user object
Args:
token: JWT token (automatically extracted from header)
db: Database session (automatically injected)
Returns:
User object of the logged-in user
Raises:
HTTPException 401: If token is invalid or user not found
Usage in endpoints:
@app.get("/tasks/")
def get_tasks(current_user: User = Depends(get_current_user)):
# current_user is now the logged-in user!
return current_user.tasks
"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
# Decode the token
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
# Look up user in database
user = db.query(models.User).filter(models.User.username == username).first()
if user is None:
raise credentials_exception
return user