FastAPI - Beginner to Pro / Authenticate Requests

User Route

πŸ‘€ Get User detail's and Change password endpoint

In this session we will:

  1. Create a new route called users.py

  2. Create two API endpoints:

    • βœ… Get logged-in user information

    • βœ… Change user password (with verification)

This is extremely common in real-world backend systems.


πŸ“ Step 1 β€” Create users.py

Inside routers create users.py

routers/users.py

πŸ“¦ Step 2 β€” Copy Base Dependencies

From todos.py, copy the shared dependency setup.

But we will correct and clean it properly.

routes/users.py

from typing import Annotated
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from fastapi import APIRouter, Depends, HTTPException
from starlette import status
from passlib.context import CryptContext

from models import Users
from database import SessionLocal
from .auth import get_current_user

router = APIRouter(
    prefix="/user",
    tags=["user"]
)

πŸ—„ Database Dependency

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

db_dependency = Annotated[Session, Depends(get_db)]
user_dependency = Annotated[dict, Depends(get_current_user)]

πŸ” Step 3 β€” Password Hashing Context

We need the same hashing logic as routes/auth.py here.

bcrypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

πŸ‘€ Step 4 β€” Endpoint 1: Get Current User Info

We want:

  • Only authenticated users

  • Return the user record from the database

@router.get("/", status_code=status.HTTP_200_OK)
async def get_user(
    user: user_dependency,
    db: db_dependency
):
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication Failed"
        )

    user_model = db.query(Users).filter(
        Users.id == user.get("id")
    ).first()

    return user_model

🧠 What’s Happening?

  • Creates a GET endpoint that returns 200 OK.

  • Uses user_dependency to get the authenticated user from the JWT token.

  • Uses db_dependency to access the database session.

  • Checks if the user is authenticated; otherwise raises 401 Unauthorized.

  • Queries the Users table using the user ID from the token.

  • Returns the user record from the database.


🧩 Step 5 β€” Register Router in main.py

Inside main.py:

from routers import auth, todos, admin, users

Then include it:

app.include_router(auth.router)
app.include_router(todos.router)
app.include_router(admin.router)
app.include_router(users.router)

πŸ” Run the Application

uvicorn main:app --reload

You should now see:

/user

Authorize β†’ Try it β†’ It returns your user info.


πŸ”‘ Step 6 β€” Create Password Change Model

We need the user to send:

  • current password

  • new password

Create a Pydantic model:

class UserVerification(BaseModel):
    password: str
    new_password: str = Field(min_length=6)

πŸ”„ Step 7 β€” Change Password Endpoint

Now we create a PUT endpoint.

@router.put("/password", status_code=status.HTTP_204_NO_CONTENT)
async def change_password(
    user_verification: UserVerification,
    user: user_dependency,
    db: db_dependency
):
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication Failed"
        )

    user_model = db.query(Users).filter(
        Users.id == user.get("id")
    ).first()

    # Verify current password
    if not bcrypt_context.verify(
        user_verification.password,
        user_model.hashed_password
    ):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Error while changing password"
        )

    # Hash and update new password
    user_model.hashed_password = bcrypt_context.hash(
        user_verification.new_password
    )

    db.add(user_model)
    db.commit()

πŸ” How This Works

Step 1

User sends:

{
  "password": "current_password",
  "new_password": "new_secure_password"
}

Step 2

We verify:

bcrypt_context.verify()

Step 3

If correct β†’ hash new password

bcrypt_context.hash()

Step 4

Save to database


from typing import Annotated
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from fastapi import APIRouter, Depends, HTTPException
from starlette import status
from passlib.context import CryptContext

from models import Users
from database import SessionLocal
from .auth import get_current_user

router = APIRouter(
    prefix="/user",
    tags=["user"]
)

bcrypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

db_dependency = Annotated[Session, Depends(get_db)]
user_dependency = Annotated[dict, Depends(get_current_user)]

class UserVerification(BaseModel):
    password: str
    new_password: str = Field(min_length=6)

@router.get("/", status_code=status.HTTP_200_OK)
async def get_user(
    user: user_dependency,
    db: db_dependency
):
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication Failed"
        )

    user_model = db.query(Users).filter(
        Users.id == user.get("id")
    ).first()

    return user_model

@router.put("/password", status_code=status.HTTP_204_NO_CONTENT)
async def change_password(
    user_verification: UserVerification,
    user: user_dependency,
    db: db_dependency
):
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication Failed"
        )

    user_model = db.query(Users).filter(
        Users.id == user.get("id")
    ).first()

    # Verify current password
    if not bcrypt_context.verify(
        user_verification.password,
        user_model.hashed_password
    ):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Error while changing password"
        )

    # Hash and update new password
    user_model.hashed_password = bcrypt_context.hash(
        user_verification.new_password
    )

    db.add(user_model)
    db.commit()

πŸ§ͺ Testing Scenarios

Changing the password from test1234 to test123 and trying to login again