Auth0 Authentication
Auth0 provides flexible identity platform services for BoilStream. This guide covers integrating with Auth0 for JWT authentication, custom claims, and universal login.
Overview
Auth0 integration provides:
- Universal Login: Customizable login flows and social connections
- Multi-provider Support: Integrate with Google, Microsoft, GitHub, and more
- Custom Claims: Flexible namespaced claims for groups and roles
- Rules and Actions: Custom authentication logic and user enrichment
- High Availability: Auth0-managed service with enterprise SLA
Prerequisites
- Auth0 account (free tier available)
- Auth0 tenant configured
- API resource registered in Auth0
- Users and roles configured in Auth0
Auth0 Setup
1. Create API Resource
Register BoilStream as an API in Auth0:
# Create API resource
auth0 apis create \
--name "BoilStream Data Platform" \
--identifier "https://api.boilstream.company.com" \
--scopes "read:data,write:data,admin:system"
{
"name": "BoilStream Data Platform",
"identifier": "https://api.boilstream.company.com",
"scopes": [
{
"value": "read:data",
"description": "Read access to data streams"
},
{
"value": "write:data",
"description": "Write access to data streams"
},
{
"value": "admin:system",
"description": "Administrative access to system"
}
],
"signing_alg": "RS256",
"allow_offline_access": false
}
2. Configure Application
Create or configure an application in Auth0:
# Create application
auth0 apps create \
--name "BoilStream Client" \
--type "spa" \
--callbacks "https://boilstream.company.com/callback" \
--allowed-origins "https://boilstream.company.com"
{
"name": "BoilStream Client",
"app_type": "spa",
"callbacks": [
"https://boilstream.company.com/callback"
],
"allowed_origins": [
"https://boilstream.company.com"
],
"jwt_configuration": {
"alg": "RS256"
}
}
3. Setup Custom Claims (Optional)
For group and role mapping, configure custom claims using Auth0 Actions:
// Auth0 Action: Add Custom Claims
exports.onExecutePostLogin = async (event, api) => {
const namespace = 'https://boilstream.company.com/';
// Add user groups (from app_metadata or external source)
if (event.user.app_metadata && event.user.app_metadata.groups) {
api.accessToken.setCustomClaim(`${namespace}groups`, event.user.app_metadata.groups);
}
// Add user roles
if (event.authorization && event.authorization.roles) {
api.accessToken.setCustomClaim(`${namespace}roles`, event.authorization.roles);
}
};
4. Configure User Roles
Set up roles in Auth0 Dashboard or via Management API:
# Create roles via Management API
curl -X POST https://YOUR_DOMAIN.auth0.com/api/v2/roles \
-H "Authorization: Bearer YOUR_MGMT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "DataAdmin",
"description": "Full access to data operations"
}'
curl -X POST https://YOUR_DOMAIN.auth0.com/api/v2/roles \
-H "Authorization: Bearer YOUR_MGMT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "DataReader",
"description": "Read-only access to data"
}'
BoilStream Configuration
Environment Variables
Configure BoilStream to use Auth0:
# Required Auth0 settings
export AUTH_PROVIDERS="auth0"
export AUTH0_TENANT="your-tenant" # e.g., "company" for company.auth0.com
export AUTH0_AUDIENCE="https://api.boilstream.company.com"
# Optional: Custom namespaces for claims
export AUTH0_GROUPS_NAMESPACE="https://boilstream.company.com/groups"
export AUTH0_ROLES_NAMESPACE="https://boilstream.company.com/roles"
# Authorization rules (optional)
export ADMIN_GROUPS="DataAdmin,SystemAdmin"
export READ_ONLY_GROUPS="DataReader,Analyst"
export WRITE_GROUPS="DataWriter,DataEngineer"
Docker Compose Example
version: '3.8'
services:
boilstream:
image: boilstream/ingestion-agent
environment:
# Auth0 Configuration
AUTH_PROVIDERS: "auth0"
AUTH0_TENANT: "company"
AUTH0_AUDIENCE: "https://api.boilstream.company.com"
AUTH0_GROUPS_NAMESPACE: "https://boilstream.company.com/groups"
AUTH0_ROLES_NAMESPACE: "https://boilstream.company.com/roles"
# Authorization
ADMIN_GROUPS: "DataAdmin,SystemAdmin"
READ_ONLY_GROUPS: "DataReader"
WRITE_GROUPS: "DataWriter,DataEngineer"
# Other configuration...
S3_BUCKET: "my-data-bucket"
ports:
- "50051:50051"
Client Integration
JavaScript/Node.js
import { createAuth0Client } from '@auth0/auth0-spa-js';
// Initialize Auth0 client
const auth0 = await createAuth0Client({
domain: 'company.auth0.com',
clientId: 'YOUR_CLIENT_ID',
authorizationParams: {
redirect_uri: window.location.origin,
audience: 'https://api.boilstream.company.com',
scope: 'read:data write:data'
}
});
// Get access token
const token = await auth0.getTokenSilently();
// Use with BoilStream
const headers = {
'Authorization': `Bearer ${token}`
};
Python
import requests
from authlib.integrations.requests_client import OAuth2Session
# Auth0 configuration
AUTH0_DOMAIN = 'company.auth0.com'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
AUDIENCE = 'https://api.boilstream.company.com'
# Get access token
client = OAuth2Session(CLIENT_ID, CLIENT_SECRET)
token = client.fetch_token(
f'https://{AUTH0_DOMAIN}/oauth/token',
audience=AUDIENCE,
grant_type='client_credentials'
)
# Use with BoilStream
headers = {
'Authorization': f"Bearer {token['access_token']}"
}
DuckDB with Authentication
-- Set up authentication headers
SET VARIABLE auth_header = 'Bearer YOUR_JWT_TOKEN_HERE';
-- Connect with authentication
ATTACH 'boilstream' (
TYPE AIRPORT,
location 'grpc+tls://localhost:50051/',
headers (Authorization = getvariable('auth_header'))
);
-- Use authenticated connection
CREATE TABLE boilstream.s3.events (
timestamp TIMESTAMPTZ,
user_id VARCHAR,
event_type VARCHAR,
properties JSON
);
Claims Mapping
Standard Claims
Auth0 tokens include standard JWT claims:
sub
: User ID (e.g.,auth0|6123456789abcdef
)iss
: Issuer (e.g.,https://company.auth0.com/
)aud
: Audience (API identifier)exp
: Expiration timestampiat
: Issued at timestampscope
: OAuth 2.0 scopes (space-separated)
Custom Claims
Configure custom namespaced claims for authorization:
{
"sub": "auth0|6123456789abcdef",
"iss": "https://company.auth0.com/",
"aud": "https://api.boilstream.company.com",
"scope": "read:data write:data",
"https://boilstream.company.com/groups": [
"data-engineers",
"platform-users"
],
"https://boilstream.company.com/roles": [
"DataAdmin",
"PlatformUser"
]
}
Security Considerations
Token Validation
BoilStream validates Auth0 tokens by:
- Signature Verification: RSA signature validation using Auth0 JWKS
- Issuer Validation: Ensures token comes from configured Auth0 tenant
- Audience Validation: Verifies token intended for BoilStream API
- Expiration Check: Ensures token hasn't expired
Best Practices
- Use HTTPS: Always use TLS for token transmission
- Short-lived Tokens: Configure reasonable token expiration times
- Principle of Least Privilege: Grant minimal required scopes and roles
- Regular Rotation: Rotate client secrets regularly
- Monitor Access: Use Auth0 logs to monitor authentication events
JWKS Caching
BoilStream caches Auth0 JWKS keys for performance:
- Cache Duration: 1 hour (3600 seconds)
- Max Keys: 100 keys cached
- Automatic Refresh: Keys refreshed on cache miss
Testing
Test JWT Generation
For testing, you can use Auth0's test tokens or create your own:
# Get test token using Auth0 CLI
auth0 test token \
--audience "https://api.boilstream.company.com" \
--scopes "read:data write:data"
# Or use curl with client credentials
curl -X POST https://company.auth0.com/oauth/token \
-H "Content-Type: application/json" \
-d '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "https://api.boilstream.company.com",
"grant_type": "client_credentials"
}'
Integration Testing
# Test authentication endpoint
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
https://localhost:50051/flight/health
# Test with invalid token (should return 401)
curl -H "Authorization: Bearer invalid_token" \
https://localhost:50051/flight/health
Troubleshooting
Common Issues
"Invalid audience"
- Verify
AUTH0_AUDIENCE
matches your API identifier - Check token
aud
claim contains correct audience
- Verify
"Invalid issuer"
- Verify
AUTH0_TENANT
matches your Auth0 domain - Check token
iss
claim format:https://YOUR_TENANT.auth0.com/
- Verify
"Key not found"
- Auth0 JWKS endpoint unreachable
- Key rotation occurred, wait for cache refresh
"Groups not extracted"
- Verify custom claims Action is deployed
- Check namespace configuration matches environment variables
Debug Commands
# Verify Auth0 configuration
echo "Tenant: $AUTH0_TENANT"
echo "Audience: $AUTH0_AUDIENCE"
echo "Groups Namespace: $AUTH0_GROUPS_NAMESPACE"
# Test JWKS endpoint
curl https://$AUTH0_TENANT.auth0.com/.well-known/jwks.json
# Decode JWT token (base64)
echo "YOUR_JWT_TOKEN" | cut -d'.' -f2 | base64 -d | jq
Log Analysis
Look for these log messages:
INFO boilstream::auth::manager: Added Auth0 authentication provider
DEBUG boilstream::auth::auth0: Fetching JWKS from Auth0: https://company.auth0.com/.well-known/jwks.json
DEBUG boilstream::auth::auth0: Successfully validated Auth0 token for user: auth0|6123456789abcdef
Migration from Other Providers
From Cognito
Auth0 can complement or replace Cognito:
# Multi-provider setup
export AUTH_PROVIDERS="cognito,auth0"
# Auth0-only setup
export AUTH_PROVIDERS="auth0"
export AUTH0_TENANT="company"
export AUTH0_AUDIENCE="https://api.boilstream.company.com"
From Azure AD
Similar migration process:
# Migrate from Azure AD
export AUTH_PROVIDERS="auth0" # Replace "azure-ad"
export AUTH0_TENANT="company"
export AUTH0_AUDIENCE="https://api.boilstream.company.com"
Advanced Configuration
Multiple Audiences
Auth0 supports multiple audiences in JWT tokens:
# BoilStream validates against this audience
export AUTH0_AUDIENCE="https://api.boilstream.company.com"
# Auth0 can issue tokens for multiple APIs
# Token will contain array: ["https://api.boilstream.company.com", "https://other-api.com"]
Custom Domains
Use Auth0 custom domains for branded authentication:
# Custom domain setup
export AUTH0_TENANT="auth.company.com" # Instead of "company.auth0.com"
This requires Auth0 custom domain configuration and SSL certificate setup.