Skip to main content

Gateway Auth0 Integration - Complete Setup

This document describes the complete Auth0 authentication integration between the NoETL Gateway (Rust) and NoETL server playbooks.

Important: The Gateway is a pure API gateway that does not connect to any database. All authentication logic and data access goes through NoETL server playbooks via REST API calls.

Architecture Overview​

┌──────────────────────────────────────────────────────────────────┐
│ Browser (UI Client) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ login.html → Auth0 → Get Token → POST /api/auth/login │ │
│ │ index.html → Check Session → Authenticated GraphQL │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────┬────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Gateway (Rust - Port 8090) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Session Cache (NATS K/V): │ │
│ │ 1. Check NATS K/V for cached session (fast path) │ │
│ │ 2. Cache miss → Call NoETL playbook (slow path) │ │
│ │ │ │
│ │ Public Routes: │ │
│ │ POST /api/auth/login → NoETL auth0_login │ │
│ │ POST /api/auth/validate → NoETL auth0_validate │ │
│ │ POST /api/auth/check-access → NoETL check_access │ │
│ │ │ │
│ │ Protected Routes (middleware validates session): │ │
│ │ POST /graphql → Authenticated GraphQL Mutations │ │
│ │ │ │
│ │ Static Files: │ │
│ │ GET / → index.html │ │
│ │ GET /static/* → Static assets │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────┬────────────────────────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌───────────────────────┐ ┌──────────────────────────────────────┐
│ NATS K/V (sessions) │ │ NoETL Server (Python - Port 8082) │
│ ┌─────────────────┐ │ │ ┌────────────────────────────────┐ │
│ │ Fast session │ │ │ │ Auth0 Playbooks: │ │
│ │ cache lookups │ │ │ │ auth0_login │ │
│ │ (sub-ms) │ │ │ │ auth0_validate_session │ │
│ └─────────────────┘ │ │ │ check_playbook_access │ │
└───────────────────────┘ │ │ │ │
│ │ Playbooks update NATS K/V │ │
│ │ after Postgres operations │ │
│ └────────────────────────────────┘ │
└─────────────────────┬────────────────┘
│
▼
┌──────────────────────────────────────┐
│ PostgreSQL Database │
│ ┌────────────────────────────────┐ │
│ │ auth schema (source of truth):│ │
│ │ - users (Auth0 profiles) │ │
│ │ - sessions (tokens, expiry) │ │
│ │ - roles, permissions │ │
│ │ - audit_log │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘

Session Caching with NATS K/V​

The Gateway uses NATS JetStream K/V as a fast session cache to avoid calling NoETL playbooks for every authenticated request.

Cache Flow​

Login Request:

  1. Gateway receives Auth0 token from UI
  2. Gateway calls auth0_login playbook via NoETL
  3. Playbook validates token, creates user/session in Postgres
  4. Gateway receives callback with session details
  5. Gateway caches session in NATS K/V (bucket: sessions, TTL: 5 min)

Subsequent Requests (Middleware & Validate):

  1. Gateway checks NATS K/V for session (sub-millisecond lookup)
  2. If found and valid → use cached session data (fast path, no playbook call)
  3. If not found (cache miss) → call auth0_validate_session playbook
  4. Playbook validates from Postgres (source of truth)
  5. Gateway receives callback and caches result in NATS K/V

Session Invalidation:

  • Invalid sessions are removed from cache
  • Cache entries auto-expire via NATS K/V TTL (default: 5 minutes)
  • Postgres remains source of truth

NATS K/V Session Data​

{
"session_token": "abc123...",
"user_id": 42,
"email": "[email protected]",
"display_name": "User Name",
"expires_at": "2026-01-15T10:00:00Z",
"is_active": true
}

Configuration​

Environment Variables:

# NATS connection (with credentials for JetStream K/V access)
export NATS_URL=nats://noetl:[email protected]:4222

# Session cache settings
export NATS_SESSION_BUCKET=sessions # K/V bucket name (default: sessions)
export NATS_SESSION_CACHE_TTL_SECS=300 # Cache TTL in seconds (default: 300 = 5 min)

# For Kind cluster (NodePort)
export NATS_URL=nats://noetl:noetl@localhost:30422

NATS Server Configuration (accounts-based):

accounts {
NOETL {
jetstream: enabled
users: [
{ user: noetl, password: noetl }
]
}
}

Graceful Degradation​

If NATS K/V is unavailable (connection fails, permissions issue), the gateway:

  • Logs a warning: Session cache disabled (NATS K/V unavailable)
  • Continues to work using playbooks for all validations
  • No downtime or errors for users

Benefits​

  • Performance: Sub-millisecond session lookups from NATS K/V
  • Scalability: Reduced load on NoETL server and PostgreSQL
  • Reliability: Postgres remains source of truth, graceful degradation
  • Simplicity: Gateway handles caching automatically after login/validation

Components​

1. Gateway Rust Code​

Files Created:

  • crates/gateway/src/auth/mod.rs - Auth endpoints (login, validate, check_access) - all delegate to NoETL playbooks
  • crates/gateway/src/auth/middleware.rs - Session validation middleware - calls NoETL validation playbook
  • crates/gateway/src/auth/types.rs - User context types
  • crates/gateway/src/main.rs - Updated with auth routes and middleware (no database connection)

Key Features:

  • Pure API gateway - no direct database access
  • REST endpoints call NoETL playbooks via NoetlClient for all auth operations
  • Session middleware validates tokens by calling NoETL validation playbook
  • Extracts session token from Authorization: Bearer <token>, X-Session-Token header, or session_token cookie
  • Injects UserContext into request extensions for downstream use
  • All authentication state managed by NoETL server and PostgreSQL backend

2. GMoved to:** tests/fixtures/gateway_ui/​

  • login.html - Login page with Auth0 and direct token options
  • auth.js - Auth utilities (checkAuth, validateSession, authenticatedGraphQL)
  • index.html - Main page with user menu and session check
  • app.js - Flight search demo with authenticated GraphQL requests
  • styles.css - Shared UI styles

Note: UI is served separately from gateway. Use any static file server:

cd tests/fixtures/gateway_ui
python3 -m http.server 8080

Key Features:

  • Login page supports Auth0 token login or direct session token (testing)
  • Session validation on page load, redirects to login if invalid
  • All GraphQL requests include Authorization: Bearer <token> header
  • Logout button clears session and redirects to login
  • Permission check before executing playbooks

3. NoETL Auth0 Playbooks​

Already Created (in tests/fixtures/playbooks/api_integration/auth0/):

  • provision_auth_schema.yaml - Creates database schema
  • auth0_login.yaml - Authenticates with Auth0 and creates session
  • auth0_validate_session.yaml - Validates session token
  • check_playbook_access.yaml - Checks user permission for playbook

Setup Instructions​

Prerequisites​

  1. PostgreSQL running (localhost:54321 or K8s)
  2. NoETL server running (port 8082/8083)
  3. Auth0 tenant configured (or use test tokens)

Step 1: Provision Auth Schema​

The auth schema is automatically provisioned during bootstrap when deploying from scratch:

# Full bootstrap (includes auth schema provisioning)
noetl run boot

To provision manually or check status:

# Provision auth schema (if not using bootstrap)
noetl run automation/setup/provision_auth.yaml

# Check auth schema status
noetl run automation/setup/provision_auth.yaml --set action=status

# Reset auth schema (WARNING: deletes all user data)
noetl run automation/setup/provision_auth.yaml --set action=reset

What this creates:

TableDescription
auth.usersUser accounts from Auth0 (email, display_name, profile)
auth.rolesRole definitions (admin, developer, analyst, viewer)
auth.permissionsGranular permissions (playbook:execute, catalog:view, etc.)
auth.user_rolesUser-to-role mapping with expiration support
auth.role_permissionsRole-to-permission mapping
auth.playbook_permissionsPlaybook-level access control with glob patterns
auth.sessionsActive user sessions with Auth0 tokens
auth.audit_logAuthentication and authorization event trail

Default Roles:

RoleDescriptionPermissions
adminFull system accessAll permissions, all playbooks
developerCreate and manage playbooksExecute, view, create, modify playbooks (except system/*)
analystExecute and view resultsExecute and view data/* playbooks
viewerRead-only accessView playbooks and execution history

Step 1b: Grant Admin Role to First User​

After the first user logs in via Auth0, grant them admin access:

# Using the provision_auth playbook (recommended)
noetl run automation/setup/provision_auth.yaml \
--set action=grant_admin \

# Or using kubectl directly
kubectl exec -n postgres deploy/postgres -- psql -U demo -d demo_noetl -c \
"INSERT INTO auth.user_roles (user_id, role_id)
SELECT u.user_id, r.role_id
FROM auth.users u, auth.roles r
WHERE u.email = '[email protected]' AND r.role_name = 'admin'
ON CONFLICT DO NOTHING;"

Note: The user must have logged in via Auth0 first to create their account in auth.users.

User Management from Dashboard​

Once logged in as admin, you can manage users from the dashboard:

  1. Navigate to http://localhost:8080/dashboard.html
  2. Click Users in the sidebar
  3. View all users and their roles
  4. Click Edit Roles to assign/remove roles

The dashboard uses the user_management playbook which supports:

  • list_users - Get all users with roles
  • list_roles - Get available roles
  • update_user_roles - Modify user role assignments
  • get_user - Get single user details

Step 2: Update auth_user Credential​

# Change password in database
psql -h localhost -p 54321 -U demo -d demo_noetl -c \
"ALTER ROLE auth_user WITH PASSWORD 'your_secure_password';"

# Update credential file
vim tests/fixtures/credentials/pg_auth_user.json
# Set password to match above

# Register credential
noetl register credential --file tests/fixtures/credentials/pg_auth_user.json

Step 3: Register Auth Playbooks​

noetl register playbook --file tests/fixtures/playbooks/api_integration/auth0/auth0_login.yaml
noetl register playbook --file tests/fixtures/playbooks/api_integration/auth0/auth0_validate_session.yaml
noetl register playbook --file tests/fixtures/playbooks/api_integration/auth0/check_playbook_access.yaml

Step 4: Build and Run Gateway​

(no database credentials needed!) export ROUTER_PORT=8090 export NOETL_BASE_URL=http://localhost:8082

Run gateway

cargo run --release


**Gateway will start on:** `http://localhost:8090`

**Note**: Gateway does NOT require database connection. All data access goes through NoETL server API.
export POSTGRES_DB=demo_noetl

# Run gateway
cargo run --release

Gateway will start on: http://localhost:8090

Step 5: Create Test User (Optional)​

psql -h localhost -p 54321 -U demo -d demo_noetl <<EOF
-- Create test user
INSERT INTO auth.users (auth0_id, email, display_name, is_active)
VALUES ('auth0|test123', '[email protected]', 'Test User', true)
RETURNING user_id;

-- Grant admin role (use user_id from above)
INSERT INTO auth.user_roles (user_id, role_id)
SELECT 1, role_id FROM auth.roles WHERE role_name = 'admin';

-- Create test session (expires in 8 hours)
INSERT INTO auth.sessions (user_id, session_token, expires_at)
VALUES (
1,
'test-session-token-12345',
NOW() + INTERVAL '8 hours'
)
RETURNING session_id, session_token;
EOF

Save the session_token for testing!

Testing the Integration​

Test 1: Direct Login with Session Token​

  1. Open browser: http://localhost:8090/static/login.html
  2. Scroll to "Direct Login" section
  3. Enter session token: test-session-token-12345
  4. Click "Sign In with Token"
  5. Should redirect to http://localhost:8090/ with authenticated session

Test 2: Auth0 Login Flow​

  1. Get Auth0 access token from Auth0 dashboard or authentication
  2. Open browser: http://localhost:8090/static/login.html
  3. Enter Auth0 token and domain (e.g., your-tenant.auth0.com)
  4. Click "Sign In with Auth0"
  5. Gateway calls NoETL auth0_login playbook
  6. If successful, redirects to main app with session

Test 3: Protected GraphQL Request​

  1. After logging in, open browser: http://localhost:8090/
  2. Should see user name and logout button in header
  3. Type flight search query: "I want a flight from SFO to JFK tomorrow"
  4. Gateway validates session via middleware
  5. Checks playbook access permission
  6. Executes GraphQL mutation with authenticated request
  7. Returns flight search results

Test 4: Session Expiration​

  1. Wait for session to expire (8 hours by default)
  2. Refresh page
  3. Should automatically redirect to login page
  4. Session validation fails, localStorage cleared

Test 5: Permission Check​

# Remove user's execute permission for amadeus playbook
psql -h localhost -p 54321 -U demo -d demo_noetl <<EOF
DELETE FROM auth.playbook_permissions
WHERE role_id IN (
SELECT role_id FROM auth.user_roles WHERE user_id = 1
)
AND playbook_path_pattern = 'api_integration/amadeus%';
EOF

Now try to execute playbook - should get "Permission denied" error.

API Reference​

POST /api/auth/login​

Request:

{
"auth0_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"auth0_domain": "your-tenant.auth0.com",
"auth0_refresh_token": "optional-refresh-token",
"session_duration_hours": 8,
"client_ip": "192.168.1.100",
"client_user_agent": "Mozilla/5.0..."
}

Response (Success):

{
"status": "authenticated",
"session_token": "abc123def456...",
"user": {
"user_id": 1,
"email": "[email protected]",
"display_name": "John Doe"
},
"expires_at": "2026-01-08T08:00:00Z",
"message": "Authentication successful"
}

Response (Error):

{
"error": "Invalid credentials"
}

POST /api/auth/validate​

Request:

{
"session_token": "abc123def456..."
}

Response:

{
"valid": true,
"user": {
"user_id": 1,
"email": "[email protected]",
"display_name": "John Doe"
},
"expires_at": "2026-01-08T08:00:00Z",
"message": "Session is valid"
}

POST /api/auth/check-access​

Request:

{
"session_token": "abc123def456...",
"playbook_path": "api_integration/amadeus_ai_api",
"permission_type": "execute"
}

Response:

{
"allowed": true,
"user": {
"user_id": 1,
"email": "[email protected]",
"display_name": "John Doe"
},
"playbook_path": "api_integration/amadeus_ai_api",
"permission_type": "execute",
"message": "Access granted"
}

POST /graphql (Protected)​

Headers:

Authorization: Bearer abc123def456...
Content-Type: application/json

Request:

{
"query": "mutation ExecutePlaybook($name: String!, $vars: JSON) { executePlaybook(name: $name, variables: $vars) { id name status textOutput } }",
"variables": {
"name": "api_integration/amadeus_ai_api",
"vars": {
"query": "Flight from SFO to JFK tomorrow"
}
}
}

Response:

{
"data": {
"executePlaybook": {
"id": "123456",
"name": "amadeus_ai_api",
"status": "completed",
"textOutput": "Found 5 flights from SFO to JFK..."
}
}
}

Security Considerations​

  1. HTTPS Required: In production, use HTTPS for all endpoints
  2. Secure Cookies: Set HttpOnly, Secure, SameSite cookie attributes
  3. CORS: Configure allowed origins (don't use Any in production)
  4. Token Storage: Consider using cookies instead of localStorage
  5. Session Expiration: Configure appropriate session duration
  6. Rate Limiting: Add rate limiting to auth endpoints
  7. Audit Logging: All auth events logged to auth.audit_log
  8. Password Security: Use strong passwords for database roles

Troubleshooting​

Issue: "No session token provided"​

  • Check browser localStorage for session_token
  • Verify Authorization header is sent with GraphQL requests
  • Check browser console for auth.js errors

Issue: "Session validation failed"​

  • Verify NoETL server is running and accessible
  • Check auth playbooks are registered
  • Verify pg_auth_user credential is configured correctly
  • Check session hasn't expired in database

Issue: "Permission denied"​

  • Verify user has correct role assignment in auth.user_roles
  • Check playbook path pattern matches in auth.playbook_permissions
  • Review permission type (execute, view, edit, delete)

Issue: "NoETL execute playbook: send"​

  • Verify NOETL_BASE_URL environment variable is correct
  • Check NoETL server is running and accessible from Gateway
  • Review Gateway logs for connection errors

Next Steps​

  1. Implement NATS K/V Cache in Gateway: ✅ Implemented - Gateway checks NATS K/V before calling playbooks
  2. Session TTL: ✅ Implemented - NATS K/V bucket configured with automatic TTL (default: 5 min)
  3. Cache Invalidation: ✅ Implemented - Invalid sessions removed from cache automatically
  4. Integrate Auth0 SDK: Use Auth0 JavaScript SDK for proper OAuth flow
  5. Add Refresh Tokens: Implement token refresh before expiration
  6. Role Management UI: Build admin interface for role/permission management
  7. Multi-Tenant Support: Extend schema for organization-based access
  8. SSO Integration: Add SAML/OAuth provider support
  9. Audit Dashboard: Visualize authentication events and access patterns
  10. Rate Limiting: Add protection against brute force attacks

Files Summary​

Gateway Rust:

  • src/auth/mod.rs - Auth endpoints with cache-first validation
  • src/auth/middleware.rs - Session validation middleware with cache support
  • src/auth/types.rs - User context types
  • src/session_cache.rs - NATS JetStream K/V session cache module
  • src/config/gateway_config.rs - Configuration with session cache settings
  • src/main.rs - Gateway startup with session cache initialization

Gateway UI:

  • static/login.html (446 lines)
  • static/auth.js (186 lines)
  • static/index.html (updated)
  • static/app.js (updated)
  • static/styles.css (updated)

Total: ~1200 lines of new code + 4 playbooks already created

Support​

For issues or questions:

  • Review NoETL logs: kubectl logs -n noetl deployment/noetl-server
  • Review Gateway logs: Check terminal output
  • Check database: psql -h localhost -p 54321 -U demo -d demo_noetl
  • Consult playbook documentation: tests/fixtures/playbooks/api_integration/auth0/README.md