JWTConfigΒΆ
Think of a JWT like a security badge at a tech company. When you arrive (log in), security gives you a badge with your photo, name, and access level encoded on it. Throughout the day, you show this badge to enter different areas - no need to go back to security each time.
The badge is:
- Self-contained - Has all your info encoded in it
- Tamper-proof - Security can tell if someone modified it
- Time-limited - Expires at end of day
JWTs work the same way. After login, the server gives you a token containing your user info and permissions. You include this token with each request, and the server verifies it without checking the database every time.
Configure JWT authentication in your Ravyn application for secure, stateless authentication.
What You'll LearnΒΆ
- What JWT is and how it works
- Configuring JWT in Ravyn
- Generating and validating tokens
- Access and refresh token patterns
- Best practices for JWT security
Quick StartΒΆ
from ravyn import Ravyn
from ravyn.core.config.jwt import JWTConfig
from ravyn.contrib.auth.edgy.middleware import JWTAuthMiddleware
app = Ravyn(
middleware=[JWTAuthMiddleware],
jwt_config=JWTConfig(
signing_key="your-secret-key-change-in-production",
algorithm="HS256"
)
)
Info
Install JWT support: pip install ravyn[jwt]
What is JWT?ΒΆ
JWT (JSON Web Token) is a compact, URL-safe token format for transmitting information between parties. Perfect for stateless authentication.
JWT StructureΒΆ
header.payload.signature
eyJhbGc... . eyJzdWI... . SflKxw...
- Header - Algorithm and token type
- Payload - Claims (user data, expiration, etc.)
- Signature - Cryptographic signature
Why Use JWT?ΒΆ
-
Stateless - No server-side session storage
-
Scalable - Works across multiple servers
-
Mobile-Friendly - Easy to use in mobile apps
-
Flexible - Include custom claims
Basic ConfigurationΒΆ
Minimal SetupΒΆ
from ravyn import Ravyn
from ravyn.core.config.jwt import JWTConfig
app = Ravyn(
jwt_config=JWTConfig(
signing_key="your-secret-key",
algorithm="HS256"
)
)
Complete ConfigurationΒΆ
app = Ravyn(
jwt_config=JWTConfig(
signing_key="your-secret-key",
algorithm="HS256",
access_token_lifetime=3600, # 1 hour
refresh_token_lifetime=86400, # 24 hours
issuer="https://api.example.com",
audience="https://example.com"
)
)
Configuration ParametersΒΆ
| Parameter | Type | Description | Default |
|---|---|---|---|
signing_key |
str | Secret key for signing tokens | Required |
algorithm |
str | Signing algorithm | "HS256" |
access_token_lifetime |
int | Access token TTL (seconds) | 3600 |
refresh_token_lifetime |
int | Refresh token TTL (seconds) | 86400 |
issuer |
str | Token issuer | None |
audience |
str | Token audience | None |
Generating TokensΒΆ
Using Token ClassΒΆ
from ravyn.security.jwt.token import Token
from ravyn.conf import settings
from datetime import datetime, timedelta
# Create token with claims
token = Token(
sub="user123", # Subject (user ID)
exp=datetime.utcnow() + timedelta(hours=1), # Expiration
iat=datetime.utcnow() # Issued at
)
# Encode to JWT string
jwt_string = token.encode(
key=settings.secret_key,
algorithm="HS256"
)
Custom ClaimsΒΆ
# Add custom claims
token = Token(
sub="user123",
exp=datetime.utcnow() + timedelta(hours=1),
iat=datetime.utcnow(),
email="user@example.com", # Custom claim
role="admin" # Custom claim
)
jwt_string = token.encode(
key=settings.secret_key,
algorithm="HS256"
)
Validating TokensΒΆ
Decode TokenΒΆ
from ravyn.security.jwt.token import Token
from ravyn.conf import settings
# Decode JWT string
token = Token.decode(
token=jwt_string,
key=settings.secret_key,
algorithms=["HS256"]
)
# Access claims
user_id = token.sub
email = token.email
With ValidationΒΆ
try:
token = Token.decode(
token=jwt_string,
key=settings.secret_key,
algorithms=["HS256"],
audience="https://example.com",
issuer="https://api.example.com"
)
except Exception as e:
# Token invalid or expired
print(f"Token validation failed: {e}")
Access & Refresh TokensΒΆ
Custom Token ClassΒΆ
from ravyn.security.jwt.token import Token
from typing import Literal
class AppToken(Token):
token_type: Literal["access", "refresh"]
def is_access_token(self) -> bool:
return self.token_type == "access"
def is_refresh_token(self) -> bool:
return self.token_type == "refresh"
Generate Both TokensΒΆ
from datetime import datetime, timedelta
def create_tokens(user_id: str) -> dict:
# Access token (short-lived)
access_token = AppToken(
sub=user_id,
token_type="access",
exp=datetime.utcnow() + timedelta(hours=1),
iat=datetime.utcnow()
)
# Refresh token (long-lived)
refresh_token = AppToken(
sub=user_id,
token_type="refresh",
exp=datetime.utcnow() + timedelta(days=7),
iat=datetime.utcnow()
)
return {
"access_token": access_token.encode(key=settings.secret_key, algorithm="HS256"),
"refresh_token": refresh_token.encode(key=settings.secret_key, algorithm="HS256")
}
Authentication MiddlewareΒΆ
Using Built-in MiddlewareΒΆ
from ravyn import Ravyn
from ravyn.contrib.auth.edgy.middleware import JWTAuthMiddleware
from ravyn.core.config.jwt import JWTConfig
app = Ravyn(
middleware=[JWTAuthMiddleware],
jwt_config=JWTConfig(
signing_key="your-secret-key",
algorithm="HS256"
)
)
Protected EndpointsΒΆ
from ravyn import get, Request
@get("/protected")
async def protected_route(request: Request) -> dict:
# User automatically authenticated by middleware
user = request.user
return {"user_id": user.id, "email": user.email}
Complete ExampleΒΆ
Login EndpointΒΆ
from ravyn import post
from pydantic import BaseModel
class LoginRequest(BaseModel):
email: str
password: str
@post("/auth/login")
async def login(data: LoginRequest) -> dict:
# Verify credentials (example)
user = await User.get(email=data.email)
if not user or not user.verify_password(data.password):
raise HTTPException(status_code=401, detail="Invalid credentials")
# Generate tokens
tokens = create_tokens(str(user.id))
return {
"access_token": tokens["access_token"],
"refresh_token": tokens["refresh_token"],
"token_type": "bearer"
}
Refresh EndpointΒΆ
@post("/auth/refresh")
async def refresh_token(refresh_token: str) -> dict:
try:
# Decode refresh token
token = AppToken.decode(
token=refresh_token,
key=settings.secret_key,
algorithms=["HS256"]
)
# Verify it's a refresh token
if not token.is_refresh_token():
raise HTTPException(status_code=401, detail="Invalid token type")
# Generate new access token
new_access_token = AppToken(
sub=token.sub,
token_type="access",
exp=datetime.utcnow() + timedelta(hours=1),
iat=datetime.utcnow()
).encode(key=settings.secret_key, algorithm="HS256")
return {"access_token": new_access_token}
except Exception as e:
raise HTTPException(status_code=401, detail="Invalid refresh token")
Using with SettingsΒΆ
from ravyn import RavynSettings
from ravyn.core.config.jwt import JWTConfig
class AppSettings(RavynSettings):
jwt_config: JWTConfig = JWTConfig(
signing_key="your-secret-key",
algorithm="HS256",
access_token_lifetime=3600,
refresh_token_lifetime=604800 # 7 days
)
app = Ravyn(settings_module=AppSettings)
Common Pitfalls & FixesΒΆ
Pitfall 1: Weak Secret KeyΒΆ
Problem: Using a weak or hardcoded secret.
# Wrong - weak secret
jwt_config = JWTConfig(
signing_key="secret" # Too simple!
)
Solution: Use strong, random secrets:
# Correct
import secrets
import os
jwt_config = JWTConfig(
signing_key=os.getenv("JWT_SECRET", secrets.token_urlsafe(32))
)
Pitfall 2: Long-Lived Access TokensΒΆ
Problem: Access tokens that don't expire.
# Wrong - never expires
token = Token(
sub="user123",
# No exp claim!
)
Solution: Always set expiration:
# Correct
from datetime import datetime, timedelta
token = Token(
sub="user123",
exp=datetime.utcnow() + timedelta(hours=1),
iat=datetime.utcnow()
)
Pitfall 3: Storing Tokens in LocalStorageΒΆ
Problem: XSS vulnerability.
// Wrong - vulnerable to XSS
localStorage.setItem('token', accessToken);
Solution: Use httpOnly cookies or secure storage:
# Correct - set httpOnly cookie
from ravyn import Response
response = Response({"message": "Logged in"})
response.set_cookie(
"access_token",
access_token,
httponly=True,
secure=True,
samesite="strict"
)
Best PracticesΒΆ
1. Use Environment VariablesΒΆ
# Good - configurable secrets
import os
jwt_config = JWTConfig(
signing_key=os.getenv("JWT_SECRET_KEY"),
algorithm="HS256"
)
2. Short Access Token LifetimeΒΆ
# Good - short-lived access tokens
jwt_config = JWTConfig(
signing_key=secret_key,
access_token_lifetime=900, # 15 minutes
refresh_token_lifetime=604800 # 7 days
)
3. Validate All ClaimsΒΆ
# Good - validate issuer and audience
token = Token.decode(
token=jwt_string,
key=settings.secret_key,
algorithms=["HS256"],
issuer="https://api.example.com",
audience="https://example.com"
)
Learn MoreΒΆ
Next StepsΒΆ
- SessionConfig - Session management
- CORSConfig - CORS configuration
- Security - Authentication & authorization