This guide covers the fundamental steps for implementing JWT authentication with AuthX. You’ll learn how to configure AuthX, create tokens, and protect routes.
Initial setup
Install AuthX
Install the package using pip:
Configure AuthX
Create an AuthX configuration with your secret key and token settings: from authx import AuthX, AuthXConfig
auth_config = AuthXConfig(
JWT_ALGORITHM = "HS256" ,
JWT_SECRET_KEY = "your-secret-key" , # Store in environment variables
JWT_TOKEN_LOCATION = [ "headers" ],
JWT_HEADER_TYPE = "Bearer" ,
)
auth = AuthX( config = auth_config)
Never hardcode your JWT_SECRET_KEY in production. Use environment variables or a secure secrets management system.
Register error handlers
Enable automatic error handling for AuthX exceptions: from fastapi import FastAPI
app = FastAPI()
auth.handle_errors(app)
This automatically handles all AuthX exceptions with appropriate HTTP status codes and error messages.
Creating tokens
Generate JWT tokens for authenticated users:
from fastapi import HTTPException
from pydantic import BaseModel
class LoginRequest ( BaseModel ):
username: str
password: str
@app.post ( "/login" )
def login ( data : LoginRequest):
# Validate credentials (use proper password hashing in production)
if data.username in USERS and USERS [data.username][ "password" ] == data.password:
# Create access token with username as subject
access_token = auth.create_access_token( uid = data.username)
return { "access_token" : access_token, "token_type" : "bearer" }
raise HTTPException( status_code = 401 , detail = "Invalid credentials" )
The uid parameter (user identifier) becomes the sub (subject) claim in the JWT. This can be any unique identifier like a username, email, or user ID.
Protecting routes
There are multiple ways to protect your routes with AuthX:
Method 1: Manual token verification
Manually retrieve and verify tokens in your route:
from fastapi import Request
@app.get ( "/protected" )
async def protected_route ( request : Request):
# Get the token from the request
token = await auth.get_access_token_from_request(request)
# Verify the token
payload = auth.verify_token(token)
# Access token claims
username = payload.sub
return {
"message" : "Access granted" ,
"username" : username
}
Method 2: Using dependencies
Use FastAPI’s dependency injection for cleaner code:
from fastapi import Depends
from authx.schema import TokenPayload
@app.get ( "/protected" )
async def protected_route (
payload : TokenPayload = Depends(auth.access_token_required)
):
return {
"message" : "Access granted" ,
"username" : payload.sub
}
Method 3: Using the ACCESS_REQUIRED property
AuthX provides convenient property shortcuts:
@app.get ( "/protected" )
async def protected_route ( payload : TokenPayload = auth. ACCESS_REQUIRED ):
return {
"message" : "Access granted" ,
"username" : payload.sub,
"is_fresh" : payload.fresh
}
Working with token payloads
The TokenPayload object contains all JWT claims:
@app.get ( "/profile" )
async def get_profile ( payload : TokenPayload = auth. ACCESS_REQUIRED ):
return {
"sub" : payload.sub, # Subject (user identifier)
"jti" : payload.jti, # JWT ID (unique token identifier)
"type" : payload.type, # Token type ("access" or "refresh")
"fresh" : payload.fresh, # Whether token is fresh
"iat" : payload.iat, # Issued at timestamp
"exp" : payload.exp, # Expiration timestamp
"iss" : payload.iss, # Issuer
"aud" : payload.aud, # Audience
}
Adding custom data to tokens
Store additional information in your tokens:
@app.post ( "/login" )
def login ( data : LoginRequest):
user = USERS [data.username]
# Add custom data to the token
access_token = auth.create_access_token(
uid = data.username,
data = {
"email" : user[ "email" ],
"role" : user[ "role" ],
"plan" : user[ "plan" ]
}
)
return { "access_token" : access_token, "token_type" : "bearer" }
Access custom data from the payload:
@app.get ( "/dashboard" )
async def dashboard ( payload : TokenPayload = auth. ACCESS_REQUIRED ):
# Access custom data using extra_dict
custom_data = payload.extra_dict
return {
"username" : payload.sub,
"email" : custom_data.get( "email" ),
"role" : custom_data.get( "role" ),
"plan" : custom_data.get( "plan" )
}
Complete example
Here’s a complete working example:
from fastapi import FastAPI, HTTPException, Request, Depends
from pydantic import BaseModel
from authx import AuthX, AuthXConfig
from authx.schema import TokenPayload
app = FastAPI()
# Configure AuthX
auth_config = AuthXConfig(
JWT_ALGORITHM = "HS256" ,
JWT_SECRET_KEY = "your-secret-key" ,
JWT_TOKEN_LOCATION = [ "headers" ],
JWT_HEADER_TYPE = "Bearer" ,
)
auth = AuthX( config = auth_config)
auth.handle_errors(app)
# User model
class LoginRequest ( BaseModel ):
username: str
password: str
# Sample user database
USERS = {
"user1" : { "password" : "password1" , "email" : "user1@example.com" },
"user2" : { "password" : "password2" , "email" : "user2@example.com" },
}
@app.post ( "/login" )
def login ( data : LoginRequest):
"""Login endpoint that returns an access token."""
if data.username in USERS and USERS [data.username][ "password" ] == data.password:
access_token = auth.create_access_token( uid = data.username)
return { "access_token" : access_token, "token_type" : "bearer" }
raise HTTPException( status_code = 401 , detail = "Invalid credentials" )
@app.get ( "/protected" )
async def protected_route ( payload : TokenPayload = auth. ACCESS_REQUIRED ):
"""Protected route that requires authentication."""
return {
"message" : "Access granted" ,
"username" : payload.sub,
"email" : USERS .get(payload.sub, {}).get( "email" )
}
@app.get ( "/" )
def read_root ():
"""Public endpoint."""
return { "message" : "Welcome to AuthX" }
Testing with curl
Login to get a token
curl -X POST http://localhost:8000/login \
-H "Content-Type: application/json" \
-d '{"username":"user1","password":"password1"}'
Response: {
"access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ,
"token_type" : "bearer"
}
Access protected route
curl http://localhost:8000/protected \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response: {
"message" : "Access granted" ,
"username" : "user1" ,
"email" : "user1@example.com"
}
Next steps
Access and refresh tokens Learn how to implement long-lived sessions with refresh tokens
Fresh tokens Require recent authentication for sensitive operations
Scopes and permissions Implement fine-grained access control with scopes
Cookie authentication Use secure HTTP-only cookies for token storage