FastAPI JWT (Json Web Tokens)
What is JWT (JSON Web Token)?
JWT (JSON Web Token) is an open standard for securely transmitting information between parties as a JSON object. It is often used in web applications for authentication and session management. In FastAPI, JWT is commonly used to implement token-based authentication, where a token is generated and returned to the client after login, and the client sends the token in subsequent requests to authenticate itself.
How do you install dependencies for JWT in FastAPI?
To implement JWT in FastAPI, you need to install the python-jose package for encoding and decoding JWTs, as well as passlib for securely hashing passwords.
Example of installing dependencies:
pip install python-jose[cryptography] passlib[bcrypt]In this example, python-jose is used for handling JWTs, and passlib[bcrypt] is used for password hashing.
How do you generate a JWT in FastAPI?
To generate a JWT in FastAPI, you create a function that uses the jwt.encode() method from the python-jose package. This function takes a payload, a secret key, and an algorithm, and returns a signed JWT.
Example of generating a JWT:
from datetime import datetime, timedelta
from jose import jwt
SECRET_KEY = "mysecret"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
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
@app.post("/token")
async def login():
access_token = create_access_token(data={"sub": "user_id"})
return {"access_token": access_token, "token_type": "bearer"}
In this example, the create_access_token function generates a JWT with an expiration time. The token is returned in the /token route when the user logs in.
How do you verify a JWT in FastAPI?
To verify a JWT in FastAPI, you use the jwt.decode() method to decode the token and verify its signature. You also check the claims in the token, such as the expiration time, to ensure the token is valid.
Example of verifying a JWT:
from jose import JWTError, jwt
from fastapi import Depends, HTTPException
def verify_token(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
if user_id is None:
raise HTTPException(status_code=401, detail="Invalid token")
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
return user_id
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
user_id = verify_token(token)
return {"user_id": user_id}
In this example, the verify_token function decodes the JWT and checks that the sub claim (user ID) is present. If the token is invalid, a 401 Unauthorized error is raised.
How do you protect routes using JWT authentication in FastAPI?
To protect routes using JWT authentication in FastAPI, you use the OAuth2PasswordBearer class to extract the token from the Authorization header and then verify the token in the route. If the token is invalid or missing, a 401 Unauthorized response is returned.
Example of protecting routes with JWT:
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/protected-route")
async def protected_route(token: str = Depends(oauth2_scheme)):
user_id = verify_token(token)
return {"message": "Access granted", "user_id": user_id}
In this example, the protected_route requires a valid JWT token. The token is extracted using OAuth2PasswordBearer and verified using the verify_token function. If the token is valid, the user is granted access to the route.
How do you handle token expiration in JWT in FastAPI?
JWTs often include an expiration time (exp claim) that specifies when the token should expire. When verifying the token, you check whether the expiration time has passed, and if it has, the token is considered expired.
Example of handling token expiration:
from datetime import datetime, timedelta
from jose import jwt
SECRET_KEY = "mysecret"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
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 verify_token(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
exp = payload.get("exp")
if datetime.utcnow() > datetime.utcfromtimestamp(exp):
raise HTTPException(status_code=401, detail="Token expired")
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
In this example, the create_access_token function adds an expiration time to the JWT, and the verify_token function checks whether the token has expired before allowing access.
How do you refresh JWTs in FastAPI?
To refresh JWTs in FastAPI, you create a separate route for refreshing tokens. The client sends a valid refresh token, and the server generates a new access token. The refresh token typically has a longer expiration time than the access token.
Example of refreshing JWTs:
@app.post("/refresh-token")
async def refresh_token(refresh_token: str):
if refresh_token == "valid_refresh_token":
new_access_token = create_access_token(data={"sub": "user_id"})
return {"access_token": new_access_token, "token_type": "bearer"}
raise HTTPException(status_code=401, detail="Invalid refresh token")
In this example, the /refresh-token route validates the refresh token and generates a new access token if the refresh token is valid.
How do you store JWTs securely on the client side?
JWTs can be stored on the client side in local storage or session storage. However, for security reasons, it is recommended to store JWTs in cookies with the HttpOnly and Secure flags enabled, preventing client-side scripts from accessing the token and ensuring that it is only sent over HTTPS.
Example of storing JWTs in cookies:
from fastapi.responses import JSONResponse
@app.post("/login")
async def login():
access_token = create_access_token(data={"sub": "user_id"})
response = JSONResponse(content={"message": "Login successful"})
response.set_cookie(key="access_token", value=access_token, httponly=True, secure=True)
return response
In this example, the JWT is stored in a cookie with the HttpOnly and Secure flags, ensuring that it is not accessible by JavaScript and is transmitted only over HTTPS.
How do you revoke JWTs in FastAPI?
To revoke JWTs, you typically maintain a blacklist of revoked tokens. During token verification, you check whether the token is in the blacklist. If the token is blacklisted, access is denied.
Example of revoking JWTs:
revoked_tokens = set()
@app.post("/revoke-token")
async def revoke_token(token: str):
revoked_tokens.add(token)
return {"message": "Token revoked"}
def verify_token(token: str):
if token in revoked_tokens:
raise HTTPException(status_code=401, detail="Token revoked")
# Continue token verification process
In this example, tokens are revoked by adding them to a blacklist, and during verification, the token is checked against the blacklist before proceeding.
How do you implement JWTs with scopes in FastAPI?
JWTs can include scopes to define the level of access a user has. These scopes are included in the token as part of the payload, and when verifying the token, you check if the required scopes are present before granting access.
Example of using JWTs with scopes:
from jose import jwt
def create_access_token(data: dict, scopes: list):
to_encode = data.copy()
to_encode.update({"scopes": scopes})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_token(token: str, required_scope: str):
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
scopes = payload.get("scopes", [])
if required_scope not in scopes:
raise HTTPException(status_code=403, detail="Insufficient permissions")
@app.get("/admin/")
async def read_admin_data(token: str = Depends(oauth2_scheme)):
verify_token(token, "admin")
return {"message": "Welcome, admin!"}
In this example, the JWT includes a list of scopes, and during token verification, the required scope (e.g., "admin") is checked to ensure the user has the necessary permissions.