Security & Permissions
Security & Permissions
Section titled “Security & Permissions”This guide covers Querri’s security architecture, including Fine-Grained Authorization (FGA), authentication via WorkOS, JWT token management, and data security.
Security Architecture
Section titled “Security Architecture”Querri implements a multi-layered security model:
User Request ↓Traefik Reverse Proxy (JWT Validation) ↓OPA Policy Engine (Authorization) ↓Application Layer (Business Logic) ↓FGA (Fine-Grained Permissions) ↓Data Layer (MongoDB)Authentication
Section titled “Authentication”WorkOS Single Sign-On (SSO)
Section titled “WorkOS Single Sign-On (SSO)”Querri uses WorkOS for enterprise-grade authentication.
Authentication Flow
Section titled “Authentication Flow”- User visits application
- Redirected to WorkOS authentication endpoint
- User authenticates via SSO provider (Google, Microsoft, Okta, etc.)
- WorkOS returns authorization code
- Hub service exchanges code for JWT token
- JWT token stored in secure cookie
- Subsequent requests include JWT in header
WorkOS Configuration
Section titled “WorkOS Configuration”Required environment variables:
# WorkOS API credentialsWORKOS_API_KEY=sk_live_xxxxxxxxxxxxxWORKOS_CLIENT_ID=client_xxxxxxxxxxxxx
# JWKS endpoint for token verificationWORKOS_JWKS_ENDPOINT=https://auth.yourcompany.com/sso/jwks/client_xxxxxxxxxxxxx
# OAuth callback URLWORKOS_REDIRECT_URI=https://app.yourcompany.com/hub/auth/callback
# Cookie encryptionWORKOS_COOKIE_PASSWORD=generate_random_32_character_stringSupported SSO Providers
Section titled “Supported SSO Providers”WorkOS supports major identity providers:
- Google Workspace - OAuth 2.0
- Microsoft Azure AD - SAML 2.0 / OAuth 2.0
- Okta - SAML 2.0
- OneLogin - SAML 2.0
- Auth0 - OAuth 2.0
- Custom SAML - Any SAML 2.0 provider
Configuring SSO Provider
Section titled “Configuring SSO Provider”-
Create WorkOS Organization:
- Log in to WorkOS Dashboard
- Create organization for your company
- Note organization ID
-
Configure SSO Connection:
- Select SSO provider type
- Enter provider configuration (IdP URL, certificates, etc.)
- Configure attribute mapping
-
Update Environment:
Terminal window WORKOS_PUBLIC_ORG=org_xxxxxxxxxxxxx -
Test SSO:
Terminal window # Test authentication flowcurl https://app.yourcompany.com/hub/auth/login
JWT Token Management
Section titled “JWT Token Management”Token Structure
Section titled “Token Structure”Querri uses JWT (JSON Web Tokens) for authentication:
{ "header": { "alg": "RS256", "typ": "JWT" }, "payload": { "sub": "user@company.com", "email": "user@company.com", "name": "John Doe", "org": "org_xxxxxxxxxxxxx", "is_admin": false, "iat": 1640000000, "exp": 1640086400 }, "signature": "..."}Token Generation
Section titled “Token Generation”Tokens are generated by the hub service:
# Generate JWT tokenimport jwtfrom datetime import datetime, timedelta
payload = { 'sub': user_email, 'email': user_email, 'name': user_name, 'org': organization_id, 'is_admin': is_admin, 'iat': datetime.utcnow(), 'exp': datetime.utcnow() + timedelta(hours=8)}
token = jwt.encode( payload, private_key, algorithm='RS256')Token Validation
Section titled “Token Validation”Traefik validates tokens using JWT plugin:
traefik/dynamic.yml:
http: middlewares: jwt-auth: plugin: jwt: jwksUrl: ${WORKOS_JWKS_ENDPOINT} required: true forwardHeaders: Authorization: "Bearer {token}"Token Refresh
Section titled “Token Refresh”Tokens expire after 8 hours. Refresh process:
- Client detects expired token (401 response)
- Redirect to
/hub/auth/refresh - Hub service validates refresh token
- New JWT token issued
- Client resumes with new token
Token Storage
Section titled “Token Storage”- Client-side: Secure HTTP-only cookie
- Server-side: Redis for session management
# View active sessions in Redisdocker compose exec redis redis-cli> KEYS session:*> GET session:user@company.comAuthorization
Section titled “Authorization”Fine-Grained Authorization (FGA)
Section titled “Fine-Grained Authorization (FGA)”Querri uses OpenFGA for resource-level permissions.
Permission Model
Section titled “Permission Model”FGA implements a tuple-based authorization model:
user:<email> <relation> <object_type>:<object_id>Examples:
user:john@company.com owner project:abc123user:jane@company.com editor project:abc123user:bob@company.com viewer project:abc123Permission Levels
Section titled “Permission Levels”- Full control over resource
- Can delete resource
- Can modify permissions
- Can transfer ownership
Editor
Section titled “Editor”- Can view and modify resource
- Can create child resources (e.g., steps in project)
- Cannot delete resource
- Cannot change permissions
Viewer
Section titled “Viewer”- Read-only access
- Can view resource and data
- Can export data
- Cannot modify or delete
Checking Permissions
Section titled “Checking Permissions”FGA queries determine access:
# Check if user can edit projectfrom openfga_sdk.client import OpenFgaClient
client = OpenFgaClient(configuration)
allowed = await client.check( user="user:john@company.com", relation="editor", object="project:abc123")Permission Inheritance
Section titled “Permission Inheritance”Resources inherit permissions from parent:
Organization ↓ (all members)Project ↓ (inherited)Steps ↓ (inherited)ResultsExample:
- User is organization member → Access to org resources
- User is project owner → Owner of all project steps
- Project shared with user → User can access all steps
OPA Policy Engine
Section titled “OPA Policy Engine”Open Policy Agent enforces authorization policies.
Policy Files
Section titled “Policy Files”opa/policies/authz.rego - General authorization:
package querri.authz
default allow = false
# Allow if user is adminallow { input.user.is_admin == true}
# Allow if user owns resourceallow { input.user.email == input.resource.owner}
# Allow if user has permission via FGAallow { fga_check(input.user.email, input.permission, input.resource.id)}opa/policies/adminz.rego - Admin authorization:
package querri.admin
default allow_admin = false
# Allow if user email is @querri.comallow_admin { endswith(input.user.email, "@querri.com")}
# Allow if user has is_admin flagallow_admin { input.user.is_admin == true}Testing Policies
Section titled “Testing Policies”Test OPA policies locally:
# Test authorization policydocker compose exec opa opa eval \ -d /policies/authz.rego \ -i '{"user": {"email": "john@company.com", "is_admin": false}, "resource": {"owner": "john@company.com"}}' \ 'data.querri.authz.allow'API Authentication
Section titled “API Authentication”All API endpoints require authentication:
# API request with JWTcurl -X GET "https://app.yourcompany.com/api/projects" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."API Key Authentication (Optional)
Section titled “API Key Authentication (Optional)”For programmatic access, users can generate API keys:
# Generate API keycurl -X POST "https://app.yourcompany.com/api/keys" \ -H "Authorization: Bearer JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Production API Key", "expires_in_days": 90}'API key storage:
{ _id: "key_xxxxxxxxxxxxx", user_email: "user@company.com", name: "Production API Key", key_hash: "sha256_hash_of_key", created_at: "2024-01-15T10:00:00Z", expires_at: "2024-04-15T10:00:00Z", last_used: "2024-01-20T15:30:00Z"}Share Link Security
Section titled “Share Link Security”Public Share Links
Section titled “Public Share Links”Projects and dashboards can be shared via public links.
Share Link Generation
Section titled “Share Link Generation”// Generate share linkdb.share_links.insertOne({ _id: "share_xxxxxxxxxxxxx", resource_type: "project", resource_id: "project_uuid", created_by: "user@company.com", created_at: new Date(), expires_at: new Date(Date.now() + 30*24*60*60*1000), // 30 days access_level: "viewer", password_hash: "optional_bcrypt_hash", access_count: 0, max_access_count: -1 // -1 = unlimited})Share Link URL
Section titled “Share Link URL”https://app.yourcompany.com/share/share_xxxxxxxxxxxxxShare Link Security Features
Section titled “Share Link Security Features”- Expiration - Links expire after set period
- Password protection - Optional password requirement
- Access limits - Maximum number of views
- Revocation - Can be disabled anytime
- Audit logging - Track who accessed shared resource
Configuring Share Links
Section titled “Configuring Share Links”Set organization-wide share link policies:
db.organizations.updateOne( {_id: "org_xxxxxxxxxxxxx"}, { $set: { "settings.sharing": { allow_public_sharing: true, require_password: false, default_expiration_days: 30, max_expiration_days: 90, allow_download: true } } })Revoking Share Links
Section titled “Revoking Share Links”// Revoke specific share linkdb.share_links.updateOne( {_id: "share_xxxxxxxxxxxxx"}, { $set: { status: "revoked", revoked_at: new Date(), revoked_by: "admin@company.com" } })
// Revoke all share links for resourcedb.share_links.updateMany( {resource_id: "project_uuid"}, { $set: { status: "revoked", revoked_at: new Date() } })Data Encryption
Section titled “Data Encryption”Data at Rest
Section titled “Data at Rest”Database Encryption
Section titled “Database Encryption”MongoDB supports encryption at rest:
docker-compose.yml:
mongo: command: - mongod - --enableEncryption - --encryptionKeyFile=/data/encryption.key volumes: - ./encryption.key:/data/encryption.key:roGenerate encryption key:
# Generate 32-byte encryption keyopenssl rand -base64 32 > encryption.keychmod 400 encryption.keyFile Storage Encryption
Section titled “File Storage Encryption”For S3 storage, enable server-side encryption:
# S3 encryption configurationAWS_S3_ENCRYPTION=AES256AWS_S3_KMS_KEY_ID=arn:aws:kms:region:account:key/key-idData in Transit
Section titled “Data in Transit”HTTPS/TLS
Section titled “HTTPS/TLS”All communication encrypted via TLS:
- Client ↔ Traefik: HTTPS with TLS 1.2+
- Traefik ↔ Services: Internal HTTP (within Docker network)
- External APIs: HTTPS required
TLS Configuration
Section titled “TLS Configuration”traefik/traefik.yml:
tls: options: default: minVersion: VersionTLS12 cipherSuites: - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384Audit Logging
Section titled “Audit Logging”Authentication Audit
Section titled “Authentication Audit”Log all authentication events:
// Login eventdb.audit_log.insertOne({ event_type: "authentication", action: "login", user_email: "user@company.com", timestamp: new Date(), ip_address: "203.0.113.42", user_agent: "Mozilla/5.0...", success: true})Authorization Audit
Section titled “Authorization Audit”Log permission checks:
// Permission checkdb.audit_log.insertOne({ event_type: "authorization", action: "check_permission", user_email: "user@company.com", resource_type: "project", resource_id: "project_uuid", permission: "editor", granted: true, timestamp: new Date()})Data Access Audit
Section titled “Data Access Audit”Log sensitive data access:
// Data accessdb.audit_log.insertOne({ event_type: "data_access", action: "view_project", user_email: "user@company.com", resource_id: "project_uuid", ip_address: "203.0.113.42", timestamp: new Date()})Viewing Audit Logs
Section titled “Viewing Audit Logs”# View recent authentication eventsdocker compose exec mongo mongosh -u querri -p> use querri> db.audit_log.find({event_type: "authentication"}).sort({timestamp: -1}).limit(10)
# View failed login attempts> db.audit_log.find({ event_type: "authentication", action: "login", success: false }).sort({timestamp: -1})
# View user's activity> db.audit_log.find({user_email: "user@company.com"}).sort({timestamp: -1})Security Best Practices
Section titled “Security Best Practices”Password Policies
Section titled “Password Policies”For WorkOS-managed authentication:
- Minimum length: 12 characters
- Complexity: Upper, lower, number, special character
- Expiration: 90 days (optional)
- History: Prevent reuse of last 5 passwords
- Lockout: 5 failed attempts = 30 minute lockout
Configure in WorkOS Dashboard > Organization > Security Settings
Two-Factor Authentication (2FA)
Section titled “Two-Factor Authentication (2FA)”Enable 2FA for admin users:
- Navigate to WorkOS Dashboard
- Select organization
- Enable 2FA requirement
- Users configure 2FA on next login (TOTP app or SMS)
Session Management
Section titled “Session Management”Session Timeout
Section titled “Session Timeout”Configure session timeout:
# Session expires after 8 hours of inactivitySESSION_TIMEOUT_HOURS=8db.organizations.updateOne( {_id: "org_xxxxxxxxxxxxx"}, { $set: { "settings.security.session_timeout_minutes": 480 } })Force Logout
Section titled “Force Logout”Force logout all sessions for a user:
# Clear user sessions from Redisdocker compose exec redis redis-cli> DEL session:user@company.comConcurrent Session Limits
Section titled “Concurrent Session Limits”Limit concurrent sessions per user:
db.organizations.updateOne( {_id: "org_xxxxxxxxxxxxx"}, { $set: { "settings.security.max_concurrent_sessions": 3 } })IP Whitelisting
Section titled “IP Whitelisting”Restrict access to specific IP ranges:
db.organizations.updateOne( {_id: "org_xxxxxxxxxxxxx"}, { $set: { "settings.security.allowed_ip_ranges": [ "203.0.113.0/24", // Office network "198.51.100.0/24" // VPN network ] } })Implement in Traefik:
traefik/dynamic.yml:
http: middlewares: ip-whitelist: ipWhiteList: sourceRange: - "203.0.113.0/24" - "198.51.100.0/24"Regular Security Tasks
Section titled “Regular Security Tasks”Weekly
Section titled “Weekly”- Review failed login attempts
- Check for unusual access patterns
- Monitor API usage spikes
Monthly
Section titled “Monthly”- Review user permissions
- Audit admin user list
- Review share links
- Check for inactive users
Quarterly
Section titled “Quarterly”- Rotate JWT private keys
- Update API credentials
- Review and update security policies
- Security audit of custom integrations
Annually
Section titled “Annually”- Full security audit
- Penetration testing
- Update SSL certificates (if not auto-renewed)
- Review disaster recovery procedures
Incident Response
Section titled “Incident Response”Suspected Account Compromise
Section titled “Suspected Account Compromise”-
Immediately disable account:
db.users.updateOne({user_email: "compromised@company.com"},{$set: {is_suspended: true}}) -
Revoke all sessions:
Terminal window docker compose exec redis redis-cli DEL session:compromised@company.com -
Revoke API keys:
db.api_keys.updateMany({user_email: "compromised@company.com"},{$set: {revoked: true}}) -
Audit account activity:
db.audit_log.find({user_email: "compromised@company.com",timestamp: {$gte: new Date(Date.now() - 7*24*60*60*1000)}}).sort({timestamp: -1}) -
Reset credentials via WorkOS
-
Document incident for security review
Data Breach Response
Section titled “Data Breach Response”- Identify scope of breach
- Contain breach - Disable affected services
- Notify stakeholders per compliance requirements (GDPR, etc.)
- Forensic analysis - Review audit logs
- Remediation - Patch vulnerabilities
- Post-incident review - Update security policies
Compliance
Section titled “Compliance”GDPR Compliance
Section titled “GDPR Compliance”- Data portability: Export user data on request
- Right to deletion: Permanently delete user data
- Consent management: Track data processing consent
- Audit logging: Maintain access logs
SOC 2 Considerations
Section titled “SOC 2 Considerations”- Access controls: Role-based permissions
- Encryption: Data at rest and in transit
- Audit logging: Comprehensive activity logs
- Incident response: Documented procedures
Troubleshooting Security Issues
Section titled “Troubleshooting Security Issues”Authentication Failures
Section titled “Authentication Failures”Issue: Users cannot log in
Check:
- WorkOS service status
- JWT validation configuration
- Certificate expiration
- Session timeout settings
Permission Denied Errors
Section titled “Permission Denied Errors”Issue: Users cannot access resources they should have access to
Check:
- User organization membership
- FGA permission tuples
- OPA policy evaluation
- Resource ownership
Share Link Not Working
Section titled “Share Link Not Working”Issue: Public share link returns error
Check:
- Link expiration date
- Link revocation status
- Password requirement (if set)
- Access count limits
Next Steps
Section titled “Next Steps”- User Management - Manage user access and roles
- Organization Settings - Configure security policies
- Monitoring & Usage - Monitor security events
- Environment Configuration - Security-related environment variables