YouTip LogoYouTip

Fastapi Security

FastAPI has built-in support for various security tools, including common authentication and authorization methods such as OAuth2, JWT tokens, and API Keys. This section introduces how to implement user authentication based on OAuth2 + JWT. * * * ## Overview of Security Authentication FastAPI supports the following security schemes: | Scheme | Use Case | Description | | --- | --- | --- | | HTTP Basic Auth | Simple internal services | Username and password are encoded in the request header with low security | | API Key | Inter-service calls | Pass keys via request headers, query parameters, or cookies | | OAuth2 + JWT | Front-end and back-end separated applications | Most commonly used scheme, secure and flexible | * * * ## OAuth2 Password Flow + JWT This is the most commonly used authentication scheme for front-end and back-end separated applications. The process: 1. The client sends username and password to the `/token` endpoint 2. The server verifies the credentials and returns a JWT access token 3. The client includes the token in subsequent requests (`Authorization: Bearer `) 4. The server validates the token to identify the user ### 1. Install Dependencies pip install "python-jose" passlib ### 2. Complete Example ## Example from datetime import datetime, timedelta from typing import Annotated from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import JWTError, jwt from passlib.context import CryptContext from pydantic import BaseModel # ===== Configuration ===== SECRET_KEY ="your-secret-key-keep-it-secret"# Use environment variables in production ALGORITHM ="HS256" ACCESS_TOKEN_EXPIRE_MINUTES =30 # ===== Password Hashing ===== pwd_context = CryptContext(schemes=, deprecated="auto") # ===== OAuth2 Scheme ===== oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # ===== Data Models ===== class Token(BaseModel): access_token: str token_type: str class TokenData(BaseModel): username: str | None=None class User(BaseModel): username: str email: str | None=None full_name: str | None=None disabled: bool | None=None class UserInDB(User): hashed_password: str # ===== Mock Database ===== fake_users_db ={ "alice": { "username": "alice", "full_name": "Alice Wonderson", "email": "alice@example.com", "hashed_password": pwd_context.hash("secret"),# Password: secret "disabled": False, } } # ===== Utility Functions ===== def verify_password(plain_password: str, hashed_password: str) ->bool: """Verify password""" return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) ->str: """Generate password hash""" return pwd_context.hash(password) def get_user(db: dict, username: str) -> UserInDB | None: """Get user from database""" if username in db: return UserInDB(**db) return None def authenticate_user(db: dict, username: str, password: str): """Validate user credentials""" user= get_user(db, username) if not user: return False if not verify_password(password,user.hashed_password): return False return user def create_access_token(data: dict, expires_delta: timedelta | None=None): """Create JWT access token""" to_encode = data.copy() expire =datetime.utcnow() + (expires_delta or timedelta(minutes=15)) to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): """Get current user from token (dependency function)""" 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=) username: str= payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception user= get_user(fake_users_db, username) if user is None: raise credentials_exception return user async def get_current_active_user( current_user: Annotated[User, Depends(get_current_user)], ): """Get current active user""" if current_user.disabled: raise HTTPException(status_code=400, detail="User is disabled") return current_user # ===== Routes ===== app = FastAPI() @app.post("/token") async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): """Login to get token""" user= authenticate_user(fake_users_db, form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) return{"access_token": access_token,"token_type": "bearer"} @app.get("/users/me") async def read_users_me( current_user: Annotated[User, Depends(get_current_active_user)], ): """Get current user info (requires authentication)""" return current_user @app.get("/users/me/items") async def read_own_items( current_user: Annotated[User, Depends(get_current_active_user)], ): """Get current user's items (requires authentication)""" return[{"item_id": "Foo","owner": current_user.username}] * * * ## Code Explanation ### Password Hashing Use `passlib`'s bcrypt algorithm to hash passwords, ensuring that plaintext passwords are not stored in the database: | Function | Description | | --- | --- | | `pwd_context.hash(password)` | Convert plaintext password to hash | | `pwd_context.verify(plain, hashed)` | Verify if plaintext password matches hash | ### JWT Tokens JWT (JSON Web Token) is a secure token format that contains user information and expiration time: | Operation | Function | Description | | --- | --- | --- | | Create token | `jwt.encode()` | Encode data into a JWT string | | Decode token | `jwt.decode()` | Parse and validate JWT string | | Set expiration | `"exp": expire` | Expiration time of the token | | Store user identifier | `"sub": username` | Subject of the token (usually username) | ### OAuth2PasswordBearer Tells FastAPI to extract the token from the `Authorization: Bearer ` request header: oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") `tokenUrl="token"` specifies the endpoint path where clients can obtain the token, which will appear in the API documentation. > The `SECRET_KEY` must be kept secret and sufficiently complex. In production environments, it should be stored using environment variables. If the key is leaked, attackers can forge tokens for any user. * * * ## Testing with API Documentation After configuring OAuth2, the Swagger UI will show an "Authorize" button: 1. Click **"Authorize"** 2. Enter username and password (e.g., `alice` / `secret`) 3. Click **"Authorize"** to get the token 4. All subsequent requests will automatically include the token * * * ## Summary * OAuth2 + JWT is the most commonly used authentication scheme for front-end and back-end separated applications * Passwords must be hashed and never stored in plain text * JWT tokens contain user identifiers and expiration times * Use dependency injection (`Depends`) to reuse authentication logic * The SECRET_KEY must be kept secret in production environments
← Fastapi Sql DatabaseFastapi Cors β†’