Debug Utilities
Tools for troubleshooting authentication and verifying your security context.
Debug Authentication Claims
Inspect the claims and permissions decoded from your JWT authentication token.
Endpoint: GET /debug/auth
Request
curl -X GET "https://dev.api.authsec.dev/exsvc/debug/auth" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json"
Response
Status: 200 OK
{
"authenticated": true,
"claims": {
"sub": "user_123abc",
"email": "[email protected]",
"tenant_id": "tenant_xyz789",
"roles": ["admin", "service_manager"],
"permissions": [
"services:read",
"services:write",
"services:delete",
"credentials:read"
],
"scopes": ["api:full_access"],
"iat": 1707662400,
"exp": 1707748800,
"iss": "https://dev.api.authsec.dev/authmgr",
"aud": "external-service-api"
},
"token_info": {
"type": "Bearer",
"expires_in": 86400,
"issued_at": "2026-02-11T13:00:00Z",
"expires_at": "2026-02-12T13:00:00Z",
"is_expired": false
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
authenticated | boolean | Whether authentication was successful |
claims | object | Decoded JWT claims |
token_info | object | Token metadata and validity info |
Claims Object
| Claim | Type | Description |
|---|---|---|
sub | string | Subject (user ID) |
email | string | User email address |
tenant_id | string | Associated tenant |
roles | array | User roles |
permissions | array | Granular permissions |
scopes | array | OAuth scopes |
iat | integer | Issued at (Unix timestamp) |
exp | integer | Expiration time (Unix timestamp) |
iss | string | Token issuer |
aud | string | Intended audience |
Token Info
| Field | Type | Description |
|---|---|---|
type | string | Token type (Bearer) |
expires_in | integer | Seconds until expiration |
issued_at | string | ISO 8601 timestamp |
expires_at | string | ISO 8601 timestamp |
is_expired | boolean | Whether token is expired |
Common Use Cases
1. Verify Token Permissions
Check if your token has required permissions for an operation:
import requests
def verify_permissions(token: str, required_permissions: list) -> bool:
"""
Verify token has required permissions
"""
response = requests.get(
"https://dev.api.authsec.dev/exsvc/debug/auth",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code != 200:
return False
data = response.json()
user_permissions = data['claims'].get('permissions', [])
# Check if all required permissions are present
has_all = all(perm in user_permissions for perm in required_permissions)
if not has_all:
missing = set(required_permissions) - set(user_permissions)
print(f"Missing permissions: {missing}")
return has_all
# Usage
token = "your_jwt_token"
required = ["services:write", "credentials:read"]
if verify_permissions(token, required):
print("✅ Token has all required permissions")
else:
print("❌ Token missing required permissions")
2. Check Token Expiration
Monitor token validity and refresh if needed:
from datetime import datetime
def check_token_expiration(token: str) -> dict:
"""
Check if token is expiring soon
"""
response = requests.get(
"https://dev.api.authsec.dev/exsvc/debug/auth",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code != 200:
return {"valid": False, "reason": "Invalid token"}
data = response.json()
token_info = data['token_info']
if token_info['is_expired']:
return {"valid": False, "reason": "Token expired"}
expires_in = token_info['expires_in']
if expires_in < 300: # Less than 5 minutes
return {
"valid": True,
"warning": "Token expiring soon",
"expires_in_seconds": expires_in,
"action": "refresh_recommended"
}
return {
"valid": True,
"expires_in_seconds": expires_in
}
# Usage
status = check_token_expiration("your_jwt_token")
if status.get('warning'):
print(f"⚠️ {status['warning']}")
print(f"Expires in: {status['expires_in_seconds']} seconds")
3. Debug Authorization Failures
When receiving 403 Forbidden errors:
def debug_authorization_failure(token: str, required_role: str = None):
"""
Debug why authorization is failing
"""
response = requests.get(
"https://dev.api.authsec.dev/exsvc/debug/auth",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code != 200:
print("❌ Token is invalid or malformed")
return
data = response.json()
claims = data['claims']
print("🔍 Token Debug Information:")
print(f"User: {claims.get('email', 'N/A')}")
print(f"Tenant: {claims.get('tenant_id', 'N/A')}")
print(f"Roles: {', '.join(claims.get('roles', []))}")
print(f"Permissions: {', '.join(claims.get('permissions', []))}")
if required_role:
user_roles = claims.get('roles', [])
if required_role not in user_roles:
print(f"\n❌ Missing required role: {required_role}")
print(f"Available roles: {', '.join(user_roles)}")
else:
print(f"\n✅ Has required role: {required_role}")
# Check token expiration
if data['token_info']['is_expired']:
print("\n❌ Token is EXPIRED")
print(f"Expired at: {data['token_info']['expires_at']}")
Troubleshooting Guide
Problem: 401 Unauthorized on Debug Endpoint
Possible Causes:
- Token is missing from Authorization header
- Token is malformed
- Token signature is invalid
Solutions:
# Check if header is formatted correctly
curl -v -H "Authorization: Bearer YOUR_TOKEN" \
https://dev.api.authsec.dev/exsvc/debug/auth
# Verify token format (should have 3 parts separated by dots)
echo "YOUR_TOKEN" | awk -F. '{print NF-1}'
# Should output: 2
Problem: Token Shows Expired
Possible Causes:
- Token has genuinely expired
- Clock skew between systems
- Token was revoked
Solutions:
# Check current time vs expiration
import time
def check_exp_time(exp_timestamp):
current_time = int(time.time())
time_diff = exp_timestamp - current_time
if time_diff < 0:
print(f"Token expired {abs(time_diff)} seconds ago")
else:
print(f"Token valid for {time_diff} more seconds")
# Check for clock skew
if abs(time_diff) < 300: # Within 5 minutes
print("⚠️ Possible clock skew issue")
Resolution:
- Request a new token from Auth Manager
- Implement token refresh logic in your application
- Verify system clocks are synchronized (NTP)
Problem: Missing Expected Permissions
Possible Causes:
- User role doesn't include required permissions
- Token generated before permissions were granted
- RBAC policy hasn't been applied
Solutions:
- Check user's roles in User Management
- Verify role permissions in Authorization
- Generate a fresh token after permission changes
- Contact administrator to grant required permissions
Code Examples
Python
import requests
from typing import Dict, Any
class AuthDebugger:
def __init__(self, base_url: str):
self.base_url = base_url
self.debug_endpoint = f"{base_url}/debug/auth"
def inspect_token(self, token: str) -> Dict[str, Any]:
"""
Get complete token debug information
"""
response = requests.get(
self.debug_endpoint,
headers={"Authorization": f"Bearer {token}"}
)
response.raise_for_status()
return response.json()
def print_token_summary(self, token: str):
"""
Print human-readable token summary
"""
try:
data = self.inspect_token(token)
claims = data['claims']
token_info = data['token_info']
print("=" * 50)
print("TOKEN DEBUG SUMMARY")
print("=" * 50)
print(f"Authenticated: {'✅ Yes' if data['authenticated'] else '❌ No'}")
print(f"User: {claims.get('email', 'N/A')}")
print(f"User ID: {claims.get('sub', 'N/A')}")
print(f"Tenant: {claims.get('tenant_id', 'N/A')}")
print(f"\nRoles: {', '.join(claims.get('roles', []))}")
print(f"\nPermissions:")
for perm in claims.get('permissions', []):
print(f" - {perm}")
print(f"\nScopes: {', '.join(claims.get('scopes', []))}")
print(f"\nToken Validity:")
print(f" Issued: {token_info['issued_at']}")
print(f" Expires: {token_info['expires_at']}")
print(f" Expires in: {token_info['expires_in']} seconds")
print(f" Status: {'❌ EXPIRED' if token_info['is_expired'] else '✅ Valid'}")
print("=" * 50)
except requests.exceptions.HTTPError as e:
print(f"❌ Error: {e}")
print(f"Status Code: {e.response.status_code}")
# Usage
debugger = AuthDebugger("https://dev.api.authsec.dev/exsvc")
debugger.print_token_summary("your_jwt_token")
TypeScript
interface AuthDebugResponse {
authenticated: boolean;
claims: {
sub: string;
email: string;
tenant_id: string;
roles: string[];
permissions: string[];
scopes: string[];
iat: number;
exp: number;
iss: string;
aud: string;
};
token_info: {
type: string;
expires_in: number;
issued_at: string;
expires_at: string;
is_expired: boolean;
};
}
class AuthDebugger {
constructor(private baseUrl: string) {}
async inspectToken(token: string): Promise<AuthDebugResponse> {
const response = await fetch(
`${this.baseUrl}/debug/auth`,
{
headers: {
"Authorization": `Bearer ${token}`,
},
}
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
async printTokenSummary(token: string): Promise<void> {
try {
const data = await this.inspectToken(token);
const { claims, token_info } = data;
console.log("=".repeat(50));
console.log("TOKEN DEBUG SUMMARY");
console.log("=".repeat(50));
console.log(`Authenticated: ${data.authenticated ? '✅ Yes' : '❌ No'}`);
console.log(`User: ${claims.email}`);
console.log(`User ID: ${claims.sub}`);
console.log(`Tenant: ${claims.tenant_id}`);
console.log(`\nRoles: ${claims.roles.join(', ')}`);
console.log(`\nPermissions:`);
claims.permissions.forEach(perm => console.log(` - ${perm}`));
console.log(`\nScopes: ${claims.scopes.join(', ')}`);
console.log(`\nToken Validity:`);
console.log(` Issued: ${token_info.issued_at}`);
console.log(` Expires: ${token_info.expires_at}`);
console.log(` Expires in: ${token_info.expires_in} seconds`);
console.log(` Status: ${token_info.is_expired ? '❌ EXPIRED' : '✅ Valid'}`);
console.log("=".repeat(50));
} catch (error) {
console.error(`❌ Error: ${error.message}`);
}
}
}
// Usage
const debugger = new AuthDebugger("https://dev.api.authsec.dev/exsvc");
await debugger.printTokenSummary("your_jwt_token");
Best Practices
1. Development vs Production
# Only use debug endpoint in development/staging
import os
DEBUG_MODE = os.getenv('ENVIRONMENT') != 'production'
if DEBUG_MODE:
# Safe to call debug endpoint
debug_info = get_debug_auth(token)
print(f"Debug info: {debug_info}")
else:
# Production: minimal logging
print("Token validated")
2. Sensitive Data Handling
# Don't log full claims in production
def safe_log_claims(claims: dict):
"""
Log claims safely without exposing sensitive data
"""
safe_claims = {
'sub': claims['sub'][:8] + '...', # Partial ID
'tenant_id': claims['tenant_id'][:8] + '...',
'roles': claims['roles'],
'has_permissions': len(claims.get('permissions', [])) > 0,
'expires_in': claims.get('exp', 0) - int(time.time())
}
print(f"Token info: {safe_claims}")
3. Automated Testing
def test_token_permissions():
"""
Automated test for token permissions
"""
token = get_test_token()
debug_response = inspect_token(token)
assert debug_response['authenticated'] == True
assert 'services:read' in debug_response['claims']['permissions']
assert not debug_response['token_info']['is_expired']
print("✅ Token permission tests passed")
Next Steps
- Service Management - Manage external services
- Credential Management - Handle service credentials
- Auth Manager - Generate tokens
- Authorization - Configure RBAC