22 : Authentication in FastAPI

Authentication means identifying a user. In simple words, it refers to the login functionality in our app. In the previous post, we implemented a logic to create these tokens. Our authentication logic will be relying on jwt tokens. Basically, our web app users will send their email and password, we will verify the credentials and send them a jwt token. Let's add these functions in a file apis > version1 > route_login.py

from fastapi.security import OAuth2PasswordRequestForm
from fastapi import Depends,APIRouter
from sqlalchemy.orm import Session
from datetime import timedelta
from fastapi import status,HTTPException

from db.session import get_db
from core.hashing import Hasher
from schemas.tokens import Token
from db.repository.login import get_user
from core.security import create_access_token
from core.config import settings


router = APIRouter()

def authenticate_user(username: str, password: str,db: Session):
    user = get_user(username=username,db=db)
    print(user)
    if not user:
        return False
    if not Hasher.verify_password(password, user.hashed_password):
        return False
    return user


@router.post("/token", response_model=Token)
def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(),db: Session= Depends(get_db)):
    user = authenticate_user(form_data.username, form_data.password,db)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
        )
    access_token_expires = timedelta(minutes=settings.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"}


Note: we will be working with email only, but our Outh2PasswordRequestForm is bound to have a field named 'username'. No issues, we will let our users enter their email and query the db using email only. The 'username' field does not restrict us to enter a username! We need a schema to verify that we are returning an access_token and token_type as defined in our response_model. Let's put this code in schemas > tokens.py

from pydantic import BaseModel


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

We also need a function that will return a user given the email. Lets create this function in db > repository > login.py. Again,we are doing this because we want to keep the database orm logic separate from our business logic. This is an example of separation of concerns.

#db >repository > login.py
from sqlalchemy.orm import Session

from db.models.users import User 


def get_user(username:str,db: Session):
    user = db.query(User).filter(User.email == username).first()
    return user

Almost there, now we need to tell our fastapi app that there is this functionality. For that, we need to put these 2 lines in apis > base.py

from fastapi import APIRouter

from apis.version1 import route_general_pages
from apis.version1 import route_jobs
from apis.version1 import route_users 
from apis.version1 import route_login  #new


api_router = APIRouter()
api_router.include_router(route_general_pages.general_pages_router,prefix="",tags=["general_pages"])
api_router.include_router(route_jobs.router,prefix="/jobs",tags=["jobs"])
api_router.include_router(route_users.router,prefix="/users",tags=["users"])
api_router.include_router(route_login.router,prefix="/login",tags=["login"])   #new

One last thing, since we are working with form data and not simple JSON, we need python-multipart library to handle the retrieval of form-data. So, let's add this in requirement.txt and do a pip install -r requirements.txt

#for creating jwt tokens
python-jose

#for  cleaning up codebase on each commit
pre-commit

#for accessing form data       #new
python-multipart

Time to test our implementation: I am using postman this time, you may also try it with automatics docs. Results will be the same.
 

Prev: 21 : JWT … Next: 23 : Authorization/Permissions …