How to Implement Refresh Token in FastAPI Securely
To implement
refresh token in FastAPI, create two JWT tokens: an access token with a short expiry and a refresh token with a longer expiry. Use an endpoint to verify the refresh token and issue a new access token without requiring user login again.Syntax
Implementing refresh tokens in FastAPI involves these parts:
- Create JWT tokens: Generate
access_tokenandrefresh_tokenwith different expiry times. - Store refresh tokens securely: Usually in HTTP-only cookies or a database.
- Refresh endpoint: An API route that accepts the refresh token, verifies it, and returns a new access token.
- Token verification: Decode and validate tokens using a secret key and algorithms.
python
from datetime import datetime, timedelta from jose import JWTError, jwt SECRET_KEY = "your-secret-key" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 15 REFRESH_TOKEN_EXPIRE_DAYS = 7 def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def create_refresh_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt
Example
This example shows a FastAPI app with login, token creation, and a refresh endpoint that issues a new access token using a valid refresh token.
python
from fastapi import FastAPI, HTTPException, Depends, status, Cookie from fastapi.security import OAuth2PasswordRequestForm from jose import JWTError, jwt from datetime import datetime, timedelta from typing import Optional app = FastAPI() SECRET_KEY = "your-secret-key" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 15 REFRESH_TOKEN_EXPIRE_DAYS = 7 fake_users_db = { "user1": { "username": "user1", "password": "password123" } } def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def create_refresh_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt @app.post("/login") async def login(form_data: OAuth2PasswordRequestForm = Depends()): user = fake_users_db.get(form_data.username) if not user or user["password"] != form_data.password: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password") access_token = create_access_token({"sub": user["username"]}) refresh_token = create_refresh_token({"sub": user["username"]}) return {"access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer"} @app.post("/refresh") async def refresh_token(refresh_token: Optional[str] = Cookie(None)): if refresh_token is None: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Refresh token missing") try: payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token") except JWTError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token") new_access_token = create_access_token({"sub": username}) return {"access_token": new_access_token, "token_type": "bearer"}
Output
POST /login with valid credentials returns JSON with access_token and refresh_token; POST /refresh with valid refresh_token cookie returns new access_token JSON.
Common Pitfalls
- Not setting different expiry times: Access tokens should expire quickly; refresh tokens last longer.
- Not verifying tokens properly: Always decode and check token validity and expiration.
- Storing refresh tokens insecurely: Use HTTP-only cookies or secure storage to prevent theft.
- Not handling token revocation: Consider ways to revoke refresh tokens if needed (e.g., logout).
python
## Wrong: Using same expiry for both tokens ACCESS_TOKEN_EXPIRE_MINUTES = 60 REFRESH_TOKEN_EXPIRE_DAYS = 0 # This is wrong, refresh token should last longer ## Right: Different expiry times ACCESS_TOKEN_EXPIRE_MINUTES = 15 REFRESH_TOKEN_EXPIRE_DAYS = 7
Quick Reference
Tips for implementing refresh tokens in FastAPI:
- Use
jose.jwtto encode and decode JWT tokens. - Set short expiry for access tokens and longer for refresh tokens.
- Store refresh tokens securely, preferably in HTTP-only cookies.
- Provide a refresh endpoint to issue new access tokens.
- Always validate tokens and handle errors gracefully.
Key Takeaways
Create separate access and refresh JWT tokens with different expiry times.
Use a dedicated refresh endpoint to issue new access tokens securely.
Store refresh tokens securely, ideally in HTTP-only cookies.
Always verify token validity and handle errors to prevent unauthorized access.
Implement token revocation strategies for better security.