Authentication in FastAPI | Part 2

3 min read

Without a working token url endpoint authentication is incomplete, let's build the other half of the equation - the login endpoint that actually creates those JWT tokens! This is where users trade their username and password for a new access token.

The User Lookup Function

Before we move on to create a login/tokenUrl endpoint, let's first create a utility function which will help us identify whether an user exist with the entered email of not.

def get_user_by_email(email: str, db: Session = Depends(get_db)) -> User:
    statement = select(User).where(User.email == email)
    result = db.exec(statement)
    return result.first()

This function does exactly what it says - finds a user by their email address. Notice how we're using SQLModel's select() syntax instead of raw SQL or even SQLAlchemy.

The -> User return type annotation is super helpful here. Your IDE knows exactly what you're getting back, and if the user doesn't exist, result.first() returns None.

The Token Creation Endpoint

@api_router.post("/token")
def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    user = get_user_by_email(form_data.username, db)
    if not user:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    if not form_data.password == user.password:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    access_token = create_access_token(user.email)
    return {"access_token": access_token, "token_type": "bearer"}

OAuth2PasswordRequestForm Magic

form_data: OAuth2PasswordRequestForm = Depends()

OAuth2PasswordRequestForm is FastAPI's built-in way to handle login forms. When someone hits this endpoint, they send:

  • username (which we're treating as email)
  • password
  • Optional scope and other OAuth2 fields

FastAPI automatically parses the form data and gives you a clean object to work with. No manual form parsing needed! 🙌

Let us understand the Authentication flow

Step 1: Find the User

user = get_user_by_email(form_data.username, db)
if not user:
    raise HTTPException(status_code=401, detail="Invalid credentials")

We look up the user by their email (which comes in as form_data.username). If no user exists, return a 401. Notice we're saying "Invalid credentials" instead of "User not found" - this prevents attackers from figuring out which emails exist in your system.

Step 2: Verify Password

if not form_data.password == user.password:
    raise HTTPException(status_code=401, detail="Invalid credentials")

Simple password check! In production, you'd want to hash your passwords and use something like bcrypt for comparison, but this shows the core logic. We would try to implement hashing in the upcoming security section.

Step 3: Create the Token

access_token = create_access_token(user.email)
return {"access_token": access_token, "token_type": "bearer"}

If everything checks out, we create a JWT token with the user's email as the subject and return it in the exact format that OAuth2 expects.

Router Organization

Just building this function for login is not sufficient. We must register this router in our FastAPI app  or the api_router. So, let's add the below information in apis/main.py

from apis.v1.user import router as user_router
from apis.v1.blog import router as blog_router
from apis.v1.auth import api_router as auth_router

api_router = APIRouter()

api_router.include_router(user_router, prefix="/api/v1", tags=["users"])
api_router.include_router(blog_router, prefix="/api/v1", tags=["blogs"])
api_router.include_router(auth_router, prefix="/api/v1", tags=["auth"])

Prefixes: All routes get /api/v1 prepended automatically. So your /token endpoint becomes /api/v1/token.

Tags: These show up in your API documentation, grouping related endpoints together. Users, blogs, and auth each get their own section.

Clean Imports: Notice how we import routers with descriptive aliases? router as user_router makes it crystal clear what each one does.

Testing it out:

When you hit /docs now, you'll see:

  • A nice "Auth" section with your /token endpoint
  • Proper OAuth2 form with username/password fields
  • The "Authorize" button that uses your token endpoint automatically

FastAPITutorial

My priority is to help build a production-ready application using FastAPI

I prioritize quality over simplicity. Our challenging approach ensures you're prepared for real-world development.

Contacts

Refunds:

Refund Policy
Social

Follow us on our social media channels to stay updated with our latest tutorials and resources.

© Copyright 2022-2025 Team FastAPITutorial. All rights reserved.