JWT Tokens¶
BlockAuth uses JWT (JSON Web Tokens) for stateless authentication. Tokens are signed with HS256 by default, with RS256 and ES256 support for asymmetric signing.
Token Structure¶
Access Token Payload¶
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"exp": 1640995200,
"iat": 1640991600,
"type": "access"
}
Refresh Token Payload¶
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"exp": 1640995200,
"iat": 1640991600,
"type": "refresh"
}
Access tokens are short-lived (default: 1 hour). Refresh tokens are longer-lived (default: 1 day) and used to obtain new token pairs.
Token Generation¶
from blockauth.utils.token import generate_auth_token, AUTH_TOKEN_CLASS
access_token, refresh_token = generate_auth_token(
token_class=AUTH_TOKEN_CLASS(),
user_id=str(user.id),
)
Token Decoding¶
from blockauth.utils.token import AUTH_TOKEN_CLASS
token_instance = AUTH_TOKEN_CLASS()
payload = token_instance.decode_token(access_token)
user_id = payload['user_id']
token_type = payload['type'] # "access" or "refresh"
Custom Token Configuration¶
from blockauth.utils.token import Token
from datetime import timedelta
custom_token = Token(
secret_key="your-custom-secret",
algorithm="HS256",
)
access_token = custom_token.generate_token(
user_id="550e8400-e29b-41d4-a716-446655440000",
token_type="access",
token_lifetime=timedelta(hours=2),
)
Custom Claims¶
BlockAuth's claims provider architecture lets you add custom data to JWT tokens without modifying the core package.
Create a Claims Provider¶
# myapp/jwt_claims.py
from blockauth.jwt.interfaces import CustomClaimsProvider
class MyClaimsProvider(CustomClaimsProvider):
def get_custom_claims(self, user):
return {
"role": user.role,
"org_id": str(user.organization_id),
"permissions": list(user.get_permissions()),
}
def validate_custom_claims(self, claims):
return "role" in claims
Register the Provider¶
Register in your Django app's ready() method:
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
from blockauth.jwt.token_manager import jwt_manager
from .jwt_claims import MyClaimsProvider
jwt_manager.register_claims_provider(MyClaimsProvider())
Multiple Providers¶
You can register multiple providers. Claims are merged into the final token:
jwt_manager.register_claims_provider(AuthClaimsProvider())
jwt_manager.register_claims_provider(BillingClaimsProvider())
Resulting Token¶
{
"user_id": "123",
"exp": 1704067200,
"iat": 1704063600,
"type": "access",
"role": "admin",
"org_id": "org-456",
"permissions": ["read", "write"]
}
Warning
Custom claims cannot override base claims (user_id, exp, iat, type). Keep claims minimal -- large tokens impact performance and may exceed header size limits.
Token Validation in External Services¶
Python¶
import jwt
def validate_blockauth_token(token_string, secret_key):
try:
payload = jwt.decode(
token_string,
secret_key,
algorithms=["HS256"],
options={"verify_signature": True, "verify_exp": True},
)
if "user_id" not in payload or "type" not in payload:
return None
return payload
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidSignatureError:
return None
JavaScript¶
const jwt = require('jsonwebtoken');
function validateBlockAuthToken(token, secretKey) {
try {
const payload = jwt.verify(token, secretKey, {
algorithms: ['HS256'],
});
if (!payload.user_id || !payload.type) return null;
return payload;
} catch (error) {
return null;
}
}
Microservice Patterns¶
Centralized Auth, Distributed Validation¶
Each service validates tokens independently using the shared secret or public key:
# middleware.py
from blockauth.utils.token import AUTH_TOKEN_CLASS
class BlockAuthMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.token_instance = AUTH_TOKEN_CLASS()
def __call__(self, request):
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
if auth_header.startswith('Bearer '):
token = auth_header.split(' ')[1]
try:
payload = self.token_instance.decode_token(token)
request.user_id = payload['user_id']
except Exception:
pass
return self.get_response(request)
Token Refresh Rotation¶
When ROTATE_REFRESH_TOKENS is True (default), each refresh request:
- Validates the refresh token
- Blacklists the old refresh token
- Issues a new access + refresh token pair
This limits the damage if a refresh token is compromised.
Security Best Practices¶
- Store access tokens in memory only (never
localStorage) - Store refresh tokens in secure HTTP-only cookies
- Use short access token lifetimes (15-60 minutes)
- Always pin the algorithm on decode:
algorithms=["HS256"] - For asymmetric signing, keep private keys in a secrets manager