Skip to content
PythonFastAPI

7. FastAPI JWT Authentication Hindi – Secure API Login

May 23, 2026 16 min read

नमस्ते दोस्तों!

स्वागत है The Easy Master पर!

क्या आप FastAPI में API बना रहे हैं? और अब सोच रहे हैं – “इस API को secure कैसे करूँ? Kaise sirf registered users hi access kar payein?”

Yeh bohot common problem hai.

Maine bhi jab pehli baar API banayi thi, toh sab endpoints public the. Koi bhi user delete kar sakta tha, koi bhi data change kar sakta tha. Ek din ek friend ne mazak mein saara data delete kar diya. Us din samajh aaya – authentication kitna important hai!

Tab maine JWT authentication implement kiya – aur ab API fully secure hai.

Is FastAPI authentication JWT Hindi article mein main aapko sikhata hoon:

✅ JWT kya hota hai – simple example ke saath
✅ Password hashing – kabhi bhi plain password mat rakhna
✅ User registration – signup endpoint
✅ User login – JWT token generate karna
✅ Protected routes – Depends() ke saath token verify
✅ Token refresh – access token + refresh token pattern
✅ Real project – complete auth system with database

End mein cheat sheet, practice prompts, aur feature image prompt bhi milega.

Chaliye secure API banana shuru karte hain! 🚀

1. JWT Authentication Kya Hai? – Simple Samjho

JWT = JSON Web Token.

Ek secure string (token) jo user ke identity ko represent karta hai. Jab user login karta hai, server ek token generate karta hai aur user ko deta hai. User har request mein yeh token bhejta hai (usually Authorization header mein). Server token verify karta hai – agar valid hai toh request allow, nahi toh reject.

Token ka structure (eye candy nahi, but important):

Code
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE3MDAwMDAwfQ.h6EbCuQ6B_7Q

Token ke 3 parts hote hain (dot se alag):

  • Header – algorithm info
  • Payload – data (user id, expiry time, etc.)
  • Signature – verification ke liye

Kaam kaise karta hai (step by step):

  1. User → POST /login with email & password
  2. Server → password check, agar sahi to generate JWT token
  3. Server → token send karta hai user ko (response mein)
  4. User → har protected endpoint mein token bhejta hai: Authorization: Bearer <token>
  5. Server → token verify karta hai, agar valid to data return, nahi to 401 Unauthorized

Token expiry (access token short-lived, refresh token long-lived):

  • Access token – 15 min to 1 hour (short)
  • Refresh token – 7 days to 30 days (long) – naya access token generate karne ke liye

2. Project Setup – Dependencies Install Karein

Code
# Virtual environment banao
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# Packages install karo
pip install fastapi "uvicorn[standard]" python-jose[cryptography] passlib[bcrypt] python-multipart bcrypt

Har package kya karta hai:

PackageRole
fastapi + uvicornWeb framework and server
python-jose[cryptography]JWT token create aur verify
passlib[bcrypt]Password hashing
bcryptHashing algorithm
python-multipartForm data support

Database ke liye aap SQLModel / SQLAlchemy bhi add kar sakte ho, lekin simplicity ke liye hum pehle in-memory storage use karenge.

3. Password Hashing – Argon2 / Bcrypt

Kabhi bhi plain password store mat karo! – Agar database leak hui toh sabki password public ho jayegi.

Hashing ka matlab – password ko ek irreversible string mein convert karna.

Code
from passlib.context import CryptContext

# Password hashing context
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def hash_password(password: str) -> str:
    """Plain password ko hash mein convert karein"""
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    """Check karein ki plain password hash se match karta hai ya nahi"""
    return pwd_context.verify(plain_password, hashed_password)

# Example
hashed = hash_password("mysecret123")
print(hashed)  # $2b$12$KxJqXcQ... (looks random)

# Verify
print(verify_password("mysecret123", hashed))  # True
print(verify_password("wrongpass", hashed))    # False

💡 Personal Experience: Maine pehle plain password store kiye the (learning phase mein). Ek din friend ne database dekhi toh sabki password pata chal gayi. Uske baad se hamesha hash karta hoon. Koi excuse nahi – hashing karna bohot easy hai.

4. User Registration – Signup Endpoint

Pehla step – user ko database mein register karna.

auth.py – (complete code):

Code
from fastapi import FastAPI, HTTPException, status, Depends
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, Dict
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
import os

app = FastAPI(title="JWT Auth API")

# ---------- Configuration ----------
SECRET_KEY = "your-super-secret-key-change-in-production"  # Use env variable!
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# OAuth2 scheme – for extracting token from Authorization header
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

# ---------- Models ----------
class UserBase(BaseModel):
    email: EmailStr
    username: str = Field(min_length=3, max_length=20)

class UserCreate(UserBase):
    password: str = Field(min_length=6)

class UserInDB(UserBase):
    id: int
    hashed_password: str
    created_at: datetime

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    email: Optional[str] = None

# ---------- Fake Database (In-memory) ----------
fake_users_db: Dict[int, UserInDB] = {}
user_id_counter = 1

# ---------- Helper Functions ----------
def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain: str, hashed: str) -> bool:
    return pwd_context.verify(plain, hashed)

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def get_user_by_email(email: str):
    for user in fake_users_db.values():
        if user.email == email:
            return user
    return None

# ---------- Register Endpoint ----------
@app.post("/register", response_model=UserBase, status_code=status.HTTP_201_CREATED)
async def register(user_data: UserCreate):
    global user_id_counter
    
    # Check if user already exists
    existing_user = get_user_by_email(user_data.email)
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Email already registered"
        )
    
    # Create new user
    hashed_password = hash_password(user_data.password)
    new_user = UserInDB(
        id=user_id_counter,
        email=user_data.email,
        username=user_data.username,
        hashed_password=hashed_password,
        created_at=datetime.utcnow()
    )
    fake_users_db[user_id_counter] = new_user
    user_id_counter += 1
    
    return new_user

Testing with Postman / curl:

Code
curl -X POST "http://localhost:8000/register" \
  -H "Content-Type: application/json" \
  -d '{"email":"rahul@example.com","username":"rahul123","password":"secret123"}'

Response:

Code
{"email":"rahul@example.com","username":"rahul123"}

5. User Login – JWT Token Generate Karein

Ab login endpoint – user email/password dega, server token return karega.

Code
from fastapi.security import OAuth2PasswordRequestForm

@app.post("/login", response_model=Token)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    """
    OAuth2PasswordRequestForm gives `username` and `password` fields.
    Note: `username` field mein email dalna hota hai (by default username hai, but we use email)
    """
    user = get_user_by_email(form_data.username)
    if not user or not verify_password(form_data.password, user.hashed_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.email}, expires_delta=access_token_expires
    )
    
    return {"access_token": access_token, "token_type": "bearer"}

Testing login:

Code
curl -X POST "http://localhost:8000/login" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=rahul@example.com&password=secret123"

Response:

Code
{"access_token":"eyJhbGc...","token_type":"bearer"}

🔐 Important: Token ko client-side (React, mobile app) mein store karna. Never store in localStorage if possible (httpOnly cookie is safer). But for learning, localStorage is fine.

6. Protected Routes – Token Verify Kaise Karein

Ab hum ek protected endpoint banayenge – sirf valid token wale users access kar sakte hain.

Code
# ---------- Token Verification ----------
async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        email: str = payload.get("sub")
        if email is None:
            raise credentials_exception
        token_data = TokenData(email=email)
    except JWTError:
        raise credentials_exception
    
    user = get_user_by_email(token_data.email)
    if user is None:
        raise credentials_exception
    return user

# ---------- Protected Endpoint ----------
@app.get("/users/me")
async def read_users_me(current_user: UserInDB = Depends(get_current_user)):
    """Only accessible with valid token. Returns current user info."""
    return {
        "id": current_user.id,
        "email": current_user.email,
        "username": current_user.username,
        "created_at": current_user.created_at
    }

Testing protected endpoint:

Code
# First login to get token
TOKEN=$(curl -s -X POST "http://localhost:8000/login" \
  -d "username=rahul@example.com&password=secret123" | jq -r '.access_token')

# Then access protected route
curl -X GET "http://localhost:8000/users/me" \
  -H "Authorization: Bearer $TOKEN"

Agar invalid token ya no token:

Code
{"detail":"Could not validate credentials"}

7. Current User Dependency – Har Jagah Use Karo

get_current_user dependency ko aap kisी भी endpoint mein use kar sakte ho. Isse user object automatically available ho jayega.

Code
@app.get("/users/me/orders")
async def get_my_orders(current_user: UserInDB = Depends(get_current_user)):
    # current_user ke paas logged-in user ki info hai
    return {"user_id": current_user.id, "orders": []}

# Admin only example
@app.get("/admin/users")
async def get_all_users(current_user: UserInDB = Depends(get_current_user)):
    # Admin check (example: email domain ya role field)
    if not current_user.email.endswith("@admin.com"):
        raise HTTPException(status_code=403, detail="Admin access required")
    return list(fake_users_db.values())

Agar har endpoint mein current_user chahiye, toh global dependency bana sakte ho:

Code
app = FastAPI(dependencies=[Depends(get_current_user)])
# Ab saare endpoints automatically require authentication

Pro Tip: get_current_user ke andar database se user fetch karna expensive ho sakta hai – cache karo ya token mein extra info (like user role) bhi include kar sakte ho.

8. Token Refresh – Access Token Expire Ho Jaye Toh

JWT access token short-lived hota hai (security ke liye). Lekin user ko baar-baar login karne se bachane ke liye Refresh Token use karte hain.

Refresh Token Pattern:

  1. Login: server access_token (15 min) + refresh_token (7 days) return karega.
  2. Jab access_token expire ho, client refresh_token bhej kar naya access_token le lega.
  3. Refresh_token bhi expire ho jaye toh user ko login karna padega.

Extended code (add to existing):

Code
# Add more config
REFRESH_TOKEN_EXPIRE_DAYS = 7

def create_refresh_token(data: dict):
    expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
    to_encode = data.copy()
    to_encode.update({"exp": expire, "type": "refresh"})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

# Modify login to return both tokens
@app.post("/login", response_model=Token)  # We'll adjust response model later
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    # ... same user verification ...
    access_token = create_access_token(data={"sub": user.email})
    refresh_token = create_refresh_token(data={"sub": user.email})
    return {
        "access_token": access_token,
        "refresh_token": refresh_token,  # new
        "token_type": "bearer"
    }

# Refresh endpoint
class RefreshTokenRequest(BaseModel):
    refresh_token: str

@app.post("/refresh", response_model=Token)
async def refresh_access_token(refresh_data: RefreshTokenRequest):
    try:
        payload = jwt.decode(refresh_data.refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
        if payload.get("type") != "refresh":
            raise HTTPException(status_code=401, detail="Invalid token type")
        email = payload.get("sub")
        if email is None:
            raise HTTPException(status_code=401, detail="Invalid refresh token")
        user = get_user_by_email(email)
        if user is None:
            raise HTTPException(status_code=401)
        
        new_access_token = create_access_token(data={"sub": user.email})
        return {"access_token": new_access_token, "token_type": "bearer"}
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid or expired refresh token")

Personal Experience: Refresh token implement karne ke baad users ko bar-bar login nahi karna padta, lekin security bhi maintain rehti hai. Lekin refresh tokens ko database mein store karna chahiye aur revoke karne ka mechanism hona chahiye (logout ke time). Beginners ke liye basic access token bhi sufficient hai.

9. Real Project – Complete Auth System (PostgreSQL + SQLModel)

Ab sab kuch milakar ek production-style auth system banate hain with database.<details> <summary>📁 Full project code – click to expand</summary>

models.py:

Code
from sqlmodel import SQLModel, Field
from datetime import datetime
from typing import Optional

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    email: str = Field(unique=True, index=True)
    username: str = Field(unique=True, index=True)
    hashed_password: str
    is_active: bool = Field(default=True)
    is_superuser: bool = Field(default=False)
    created_at: datetime = Field(default_factory=datetime.utcnow)

auth.py (full):

Code
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from sqlmodel import select
from sqlalchemy.ext.asyncio import AsyncSession
from .database import get_db
from .models import User
from .config import SECRET_KEY, ALGORITHM

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

async def get_current_user(
    token: str = Depends(oauth2_scheme),
    db: AsyncSession = Depends(get_db)
) -> User:
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        email: str = payload.get("sub")
        if email is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    
    result = await db.execute(select(User).where(User.email == email))
    user = result.scalar_one_or_none()
    if user is None:
        raise credentials_exception
    return user

routes/users.py: Protected endpoint to get current user.</details>

10. Common Mistakes (aur Unka Solution!)

MistakeWhy?Solution
Secret key hardcoded aur commit kar diyaKey leak ho jayegiUse .env file and never commit
Password hash karna bhool gayeDatabase leak ho gayi toh sabki password publicAlways use hash_password
Token expiry bohot lamba rakha (days)Security riskAccess token: 15-30 min, refresh token: days
Token store karne ke liye localStorage use karna (web)XSS attacks riskUse httpOnly cookies or secure storage
Refresh token revoke mechanism nahi haiLogout ke baad bhi token valid rahegaStore refresh tokens in DB, blacklist on logout
sub field mein email nahi daalaToken validation failAlways put unique identifier in sub
HTTPS nahi use kiyaToken MITM attack se leakProduction mein hamesha HTTPS

Personal Mistake: Maine ek baar secret key "secret" rakhi aur commit kar di. Kuch din baad kisi ne token forge kar liya. Tab se .env use karta hoon aur .gitignore mein daalta hoon.

11. Best Practices – Production Ready Auth

✅ Use environment variables for secrets:

Code
from dotenv import load_dotenv
import os
load_dotenv()
SECRET_KEY = os.getenv("SECRET_KEY")

✅ Set short access token expiry – 15 to 30 minutes

✅ Use refresh tokens stored in database – can revoke on logout

✅ Hash passwords with bcrypt – strong and slow

✅ Use HTTPS in production – never send tokens over HTTP

✅ Implement rate limiting – prevent brute force attacks

✅ Log suspicious activities – multiple failed logins, etc.

✅ Use OAuth2 scopes for permissions: (read, write, admin)

✅ Send token via httpOnly cookie (more secure for web):

Code
from fastapi.responses import JSONResponse
@app.post("/login")
async def login(response: JSONResponse, ...):
    # set cookie
    response.set_cookie(key="access_token", value=token, httponly=True)

12. Resources – Cheat Sheet + Practice Prompts

Cheat Sheet (Copy-Paste Ready)

Code
# ---------- HASHING ----------
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
hashed = pwd_context.hash("password")
verify = pwd_context.verify("password", hashed)

# ---------- JWT TOKEN ----------
from jose import jwt
from datetime import datetime, timedelta

token = jwt.encode({"sub": email, "exp": datetime.utcnow() + timedelta(minutes=30)}, SECRET_KEY)
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])

# ---------- DEPENDENCY ----------
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    # decode token, fetch user from DB
    return user

@app.get("/me")
async def me(current_user = Depends(get_current_user)):
    return current_user

Practice Prompts

  1. Beginner: Basic signup + login + protected /profile endpoint banao (in-memory storage).
  2. Intermediate: Refresh token implement karo. Ek /logout endpoint banao jo refresh token invalidate kare (store in DB blacklist).
  3. Advanced: Role-based access control (RBAC) implement karo – admin, moderator, user. Different endpoints ke liye different permissions.

13. FAQ

Q1: JWT aur Session authentication mein kya antar hai?

JWT stateless hota hai – server side mein session store nahi karna padta. Session stateful hai – server mein session store karna padta hai. JWT scaling mein easy hai, but revoke karna mushkil.

Q2: Token ko store kahan karein (web client mein)?

localStorage simple hai lekin XSS vulnerable. httpOnly cookie safer hai (cannot be accessed by JavaScript). Mobile apps mein secure storage use karo.

Q3: Token expire hone ke baad automatically refresh kaise karein?

Client har request mein token bhejta hai. Agar 401 aata hai, toh refresh token endpoint call karo and retry original request.

Q4: Kya token mein password ya sensitive data store kar sakte hain?

Token payload easily decode ho sakta hai (base64). Isliye sensitive data (password, credit card) kabhi mat store karo.

Q5: OAuth2PasswordBearer kyun use karte hain?

FastAPI ka built-in helper hai jo Authorization: Bearer <token> header se token extract karta hai. Agar token missing hai toh automatic 401 return karta hai.

Q6: JWT token ko blacklist kaise karein (logout)?

JWT inherently revoke nahi ho sakta. Common approach – maintain a blacklist of token IDs or use short expiry + refresh token revoke.

Q7: Kya mai JWT ke saath websockets use kar sakta hoon?

Haan – websocket connection establish karte time token query parameter ya header mein bhejo, pehle verify karo then connection accept karo.

14. Conclusion

Bahut badhiya! Aapne aaj seekh liya:

✅ FastAPI authentication JWT Hindi mein – complete flow
✅ Password hashing – kabhi plain password nahi
✅ User registration and login endpoints
✅ JWT token generation and verification
✅ Protected routes using Depends(get_current_user)
✅ Refresh token pattern for better UX

Authentication ek essential skill hai – har real-world API mein chahiye. Ab aap secure APIs bana sakte ho.

Aapki aaj ki challenge: Upar diye practice prompts mein se koi ek implement karo (beginner wala bhi chalega). Apna code comment mein share karo – main review karunga.

Next topic kya chahiye?

  • FastAPI WebSockets (real-time chat with JWT)?
  • FastAPI + Redis Caching?
  • FastAPI Background Tasks + Celery?

Comment mein batao!

The Easy Master ke saath secure coding seekhte raho. Happy authenticating! 🔐🚀

Resources

Additional Resources

Views: 427

TheEasyMaster

Author at The Easy Master.

Related posts

Leave a Reply

Your email address will not be published. Required fields are marked *