JWT Authentication
Complete guide to programmatically creating sessions using JWT tokens in Authonomy
JWT Authentication Guide
Create secure user sessions programmatically using JSON Web Tokens (JWT). This guide covers JWT token creation, permissions system, and tenant configuration for seamless authentication integration.
Overview
Authonomy’s JWT authentication allows you to create authenticated sessions by generating and validating JWT tokens. This is ideal for:
- API Integration: Programmatic user authentication
- Single Sign-On: Custom authentication flows
- Service-to-Service: Backend system integration
- Mobile Apps: Secure token-based authentication
Authentication Flow
sequenceDiagram participant App as Your Application participant JWT as JWT Service participant Auth as Authonomy participant User as User Browser App->>JWT: Generate JWT Token JWT->>App: Signed JWT Token App->>User: Redirect with token User->>Auth: GET /auth/jwt?token=... Auth->>Auth: Validate & Create Session Auth->>User: Redirect to Application
JWT Token Structure
Required Claims
Every JWT token must include these standard and Authonomy-specific claims:
{
// Standard JWT Claims
"iss": "https://your-app.com", // Issuer (must match config)
"sub": "user-123", // Subject (user ID)
"aud": "authonomy", // Audience
"exp": 1640995200, // Expiration timestamp
"iat": 1640991600, // Issued at timestamp
"jti": "token-unique-id", // JWT ID (for one-time use)
// Required Authonomy Claims
"email": "user@example.com", // User email address
"tenant_id": "550e8400-e29b-41d4-a716-446655440000", // Tenant UUID
// Optional Claims
"name": "John Doe", // User full name
"roles": ["admin", "user"], // User roles
"permissions": ["read", "write"], // Specific permissions
"redirect_url": "https://app.com/dashboard", // Post-auth redirect
"metadata": { // Custom user metadata
"department": "Engineering",
"employee_id": "E-1234"
}
}
Permissions System
The permissions
claim supports Authonomy’s resource-based access control:
{
"permissions": [
"users:read", // Read users
"users:write", // Create/update users
"applications:admin", // Admin access to applications
"reports:read:own", // Read own reports only
"settings:write:tenant:123" // Write settings for specific tenant
]
}
Permission Format
Permissions follow the format: resource:action:scope:target
- resource: What you’re accessing (users, applications, reports)
- action: What you can do (read, write, admin, delete)
- scope: Access level (own, team, tenant, global)
- target: Specific resource ID (optional)
Creating JWT Tokens
Algorithm Support
Authonomy supports two signing algorithms:
RS256 (Recommended for Production)
Uses RSA public/private key pairs. More secure for distributed systems.
HS256 (Simple for Development)
Uses shared secret. Simpler setup but requires secret distribution.
Node.js Implementation
RS256 Example
const jwt = require('jsonwebtoken');
const fs = require('fs');
// Load your private key
const privateKey = fs.readFileSync('path/to/private-key.pem', 'utf8');
function createJWTToken(user, tenantId, options = {}) {
const now = Math.floor(Date.now() / 1000);
const payload = {
// Standard claims
iss: 'https://your-app.com',
sub: user.id,
aud: 'authonomy',
exp: now + (options.expiresIn || 3600), // 1 hour default
iat: now,
jti: generateUniqueId(), // Implement your unique ID generation
// Required Authonomy claims
email: user.email,
tenant_id: tenantId,
// Optional claims
name: user.name,
roles: user.roles || [],
permissions: user.permissions || [],
redirect_url: options.redirectUrl,
metadata: user.metadata || {}
};
const token = jwt.sign(payload, privateKey, {
algorithm: 'RS256',
keyid: 'your-key-id' // Must match JWKS
});
return token;
}
// Usage example
const user = {
id: 'user-123',
email: 'john@example.com',
name: 'John Doe',
roles: ['user'],
permissions: ['users:read', 'reports:read:own']
};
const token = createJWTToken(user, 'tenant-uuid', {
expiresIn: 7200, // 2 hours
redirectUrl: 'https://your-app.com/dashboard'
});
console.log('Generated token:', token);
HS256 Example
const jwt = require('jsonwebtoken');
// Your shared secret (store securely!)
const secretKey = process.env.JWT_SECRET_KEY;
function createJWTToken(user, tenantId, options = {}) {
const now = Math.floor(Date.now() / 1000);
const payload = {
iss: 'https://your-app.com',
sub: user.id,
aud: 'authonomy',
exp: now + (options.expiresIn || 3600),
iat: now,
email: user.email,
tenant_id: tenantId,
name: user.name,
roles: user.roles || [],
permissions: user.permissions || []
};
const token = jwt.sign(payload, secretKey, {
algorithm: 'HS256'
});
return token;
}
Python Implementation
RS256 Example
import jwt
import time
import uuid
from cryptography.hazmat.primitives import serialization
def load_private_key(key_path):
with open(key_path, 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None
)
return private_key
def create_jwt_token(user, tenant_id, private_key, **options):
now = int(time.time())
payload = {
# Standard claims
'iss': 'https://your-app.com',
'sub': user['id'],
'aud': 'authonomy',
'exp': now + options.get('expires_in', 3600),
'iat': now,
'jti': str(uuid.uuid4()),
# Required Authonomy claims
'email': user['email'],
'tenant_id': tenant_id,
# Optional claims
'name': user.get('name'),
'roles': user.get('roles', []),
'permissions': user.get('permissions', []),
'redirect_url': options.get('redirect_url'),
'metadata': user.get('metadata', {})
}
token = jwt.encode(
payload,
private_key,
algorithm='RS256',
headers={'kid': 'your-key-id'}
)
return token
# Usage example
private_key = load_private_key('path/to/private-key.pem')
user = {
'id': 'user-123',
'email': 'john@example.com',
'name': 'John Doe',
'roles': ['user'],
'permissions': ['users:read', 'reports:read:own']
}
token = create_jwt_token(
user,
'tenant-uuid',
private_key,
expires_in=7200,
redirect_url='https://your-app.com/dashboard'
)
print(f'Generated token: {token}')
HS256 Example
import jwt
import time
import os
def create_jwt_token(user, tenant_id, **options):
secret_key = os.getenv('JWT_SECRET_KEY')
now = int(time.time())
payload = {
'iss': 'https://your-app.com',
'sub': user['id'],
'aud': 'authonomy',
'exp': now + options.get('expires_in', 3600),
'iat': now,
'email': user['email'],
'tenant_id': tenant_id,
'name': user.get('name'),
'roles': user.get('roles', []),
'permissions': user.get('permissions', [])
}
token = jwt.encode(payload, secret_key, algorithm='HS256')
return token
Tenant Configuration
Before using JWT authentication, you need to configure your tenant’s JWT settings. This includes setting up validation rules, signing algorithms, required claims, and security policies.
Quick Configuration Steps
- Access JWT Settings: Navigate to Settings → Authentication → JWT Configuration in your admin dashboard
- Create Configuration: Set up issuer, algorithm (RS256/HS256), and validation rules
- Configure Security: Define required claims, session settings, and return URL policies
- Test Setup: Use the built-in token tester to verify your configuration
Configuration Requirements
Your JWT configuration must include:
- Issuer: Must exactly match your JWT
iss
claim - Algorithm: RS256 (recommended) or HS256
- Required Claims: At minimum
email
andtenant_id
- Security Policies: Session timeout, one-time use enforcement, allowed domains
Using JWT Tokens
Browser Authentication
Redirect users to Authonomy with the JWT token:
function authenticateUser(token) {
const authUrl = `https://auth.authonomy.io/auth/jwt?token=${encodeURIComponent(token)}`;
// Redirect browser to Authonomy
window.location.href = authUrl;
// Authonomy will validate token and redirect back to your app
}
API Authentication
Use JWT tokens in API requests:
# Query parameter
curl "https://auth.authonomy.io/auth/jwt?token=YOUR_JWT_TOKEN"
# Authorization header
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
"https://auth.authonomy.io/auth/jwt"
# POST request body
curl -X POST "https://auth.authonomy.io/auth/jwt" \
-H "Content-Type: application/json" \
-d '{"token": "YOUR_JWT_TOKEN"}'
AJAX Authentication
For frontend JavaScript applications:
async function authenticateWithJWT(token) {
try {
const response = await fetch('https://auth.authonomy.io/auth/jwt', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ token: token }),
credentials: 'include' // Important: include cookies
});
if (response.ok) {
const result = await response.json();
console.log('Authentication successful:', result);
// User is now authenticated
// Session cookie is automatically set
return result;
} else {
const error = await response.json();
console.error('Authentication failed:', error);
throw new Error(error.error || 'Authentication failed');
}
} catch (error) {
console.error('JWT authentication error:', error);
throw error;
}
}
// Usage
authenticateWithJWT(jwtToken)
.then(result => {
// Redirect to dashboard or update UI
window.location.href = '/dashboard';
})
.catch(error => {
// Handle authentication error
alert('Login failed: ' + error.message);
});
JWKS Setup (RS256)
For RS256 algorithm, you need to provide a JWKS endpoint:
Generate Key Pair
# Generate private key
openssl genrsa -out jwt-private.pem 2048
# Extract public key
openssl rsa -in jwt-private.pem -pubout -out jwt-public.pem
# Get key components for JWKS
openssl rsa -in jwt-private.pem -noout -text
JWKS Endpoint
Create a JWKS endpoint at /.well-known/jwks.json
:
const express = require('express');
const fs = require('fs');
const crypto = require('crypto');
const app = express();
// Load your public key
const publicKey = fs.readFileSync('jwt-public.pem', 'utf8');
app.get('/.well-known/jwks.json', (req, res) => {
// Extract key components (implement based on your crypto library)
const jwks = {
keys: [
{
kid: 'your-key-id',
kty: 'RSA',
use: 'sig',
alg: 'RS256',
n: 'BASE64URL_ENCODED_MODULUS',
e: 'AQAB' // Usually this value for RSA
}
]
};
res.json(jwks);
});
Error Handling
Handle common JWT authentication errors:
async function handleJWTAuth(token) {
try {
const result = await authenticateWithJWT(token);
return result;
} catch (error) {
switch (error.message) {
case 'Invalid token':
// Token is malformed or signature invalid
console.error('JWT token is invalid');
break;
case 'Token expired':
// Token has expired
console.error('JWT token has expired');
break;
case 'Token has already been used':
// One-time token was already used
console.error('Token replay detected');
break;
case 'Configuration not found':
// Issuer not configured in tenant
console.error('JWT configuration missing');
break;
case 'Required claims validation failed':
// Missing required claims
console.error('Token missing required claims');
break;
default:
console.error('JWT authentication error:', error.message);
}
// Redirect to login page or show error
window.location.href = '/login?error=' + encodeURIComponent(error.message);
}
}
Security Best Practices
Token Security
- Short Expiration: Keep token lifetimes short (15-60 minutes)
- HTTPS Only: Always use HTTPS for token transmission
- Secure Storage: Store private keys securely
- Key Rotation: Regularly rotate signing keys
Validation Settings
- Audience Validation: Always set and validate audience claim
- Issuer Validation: Strictly validate issuer claim
- One-Time Use: Enable for high-security scenarios
- Clock Skew: Account for clock differences between systems
Permissions Management
- Principle of Least Privilege: Grant minimal required permissions
- Regular Audits: Review and audit permission assignments
- Scoped Permissions: Use specific resource scoping
- Permission Validation: Validate permissions in your application
Testing JWT Configuration
Test your JWT setup before deployment:
Test Token Validation
curl -X POST "https://api.authonomy.io/v1/tenants/{tenant-id}/jwt-configs/{config-id}/test" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"token": "YOUR_TEST_JWT_TOKEN"
}'
Validate JWKS Endpoint
# Test JWKS accessibility
curl "https://your-app.com/.well-known/jwks.json"
# Should return valid JWKS format:
# {
# "keys": [
# {
# "kid": "key-id",
# "kty": "RSA",
# "use": "sig",
# "alg": "RS256",
# "n": "...",
# "e": "AQAB"
# }
# ]
# }
Troubleshooting
Common Issues
Invalid Issuer
Error: “Configuration not found for issuer” Solution: Ensure JWT iss
claim exactly matches configuration
Missing Claims
Error: “Required claims validation failed” Solution: Include all required claims in JWT payload
JWKS Issues
Error: “Failed to get JWKS” Solution: Verify JWKS URL is accessible and returns valid JSON
Clock Skew
Error: “Token is not valid yet” or “Token expired” Solution: Ensure server clocks are synchronized (use NTP)
Debug Mode
Enable debug logging to troubleshoot issues:
// Enable JWT debug logging
process.env.DEBUG = 'jwt:*';
// Or use Authonomy debug endpoint
const debugResponse = await fetch('/auth/jwt/debug?token=' + token);
console.log(await debugResponse.json());
Related Information
Configuration Guides
- JWT Configuration - Complete guide to configuring JWT settings in the admin dashboard
- Federation Wizard - Setting up identity federation relationships
API Documentation
- API Reference Overview - Complete JWT API reference and endpoints
- Authentication API - Programmatic JWT configuration
Advanced Topics
- Session Management - Managing authenticated user sessions
- Permission System - Advanced permission and role configuration
- Security Best Practices - Security guidelines for JWT implementation
Next Steps
- Configure your tenant using the JWT Configuration guide
- Implement token creation using the code examples above
- Test your integration with the built-in token tester
- Set up monitoring and error handling for production use
JWT authentication provides secure, flexible user authentication for modern applications. Start with tenant configuration, then implement token creation and validation in your application.