Token Security
Best practices for managing JWT tokens.
Token Lifetimes
| Token Type | Default | Recommendation | |------------|---------|----------------| | Access Token | 1 hour | 15-60 minutes | | Refresh Token | 7 days | 7-30 days |
Set in environment:
JWT_ACCESS_TOKEN_EXPIRY=3600 # 1 hour
Secure Token Storage
Client-Side
Never store tokens in localStorage for sensitive applications.
// Use httpOnly cookies instead
// Server sets: Set-Cookie: token=...; HttpOnly; Secure; SameSite=Strict
Server-Side
# Use secure storage
token_store = {
'access_token': encrypted_token,
'refresh_token': encrypted_refresh
}
Token Refresh
Refresh tokens before expiry:
// Refresh 5 minutes before expiry
const refreshIfNeeded = async () => {
const expiresIn = token.exp - Date.now() / 1000;
if (expiresIn < 300) {
token = await client.refreshToken(token.refresh_token);
}
};
Token Revocation
Revoke compromised tokens immediately:
curl -X POST http://localhost:8080/oauth/revoke \
-H "Content-Type: application/json" \
-d '{"token": "COMPROMISED_TOKEN"}'
Scopes
Use minimal scopes:
{
"scope": "read" // Only what's needed
}
Token Claims
MachineAuth tokens include:
{
"sub": "client_id",
"agent_id": "uuid",
"org_id": "org_123",
"team_id": "team_456",
"scope": ["read", "write"],
"exp": 1709308800,
"iat": 1709305200
}
Use claims for authorization:
def has_permission(token, required_scope):
return required_scope in token['scope']
def get_org_id(token):
return token.get('org_id')
JWKS Verification
Verify tokens using JWKS:
import jwt
# Fetch JWKS
jwks = requests.get('https://auth.yourdomain.com/.well-known/jwks.json')
# Get public key
public_key = jwt.algorithms.RSA.from_jwk(jwks['keys'][0])
# Verify token
decoded = jwt.decode(
token,
public_key,
algorithms=['RS256'],
audience='machineauth-api'
)