Multi-Tenancy & Isolation
Shyntr is built for SaaS from day one. A single Shyntr instance can serve thousands of different customers with distinct configurations, cryptographic keys, and protocol requirements—all while maintaining strict isolation between tenants.
What is a Tenant?
A tenant in Shyntr represents an isolated authentication boundary. Each tenant has its own:
- OIDC/SAML clients (applications)
- Identity Provider connections
- Token signing keys
- Configuration settings
- Session data
┌─────────────────────────────────────────────────────────────────┐
│ Single Shyntr Instance │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ Tenant: Acme │ │ Tenant: Globex │ │ Tenant: Wayne │ │
│ │ │ │ │ │ │ │
│ │ • 3 OIDC Apps │ │ • 5 OIDC Apps │ │ • 2 OIDC Apps │ │
│ │ • 1 SAML App │ │ • 2 SAML Apps │ │ • 3 SAML Apps │ │
│ │ • Google IdP │ │ • Okta IdP │ │ • ADFS IdP │ │
│ │ • Azure AD IdP │ │ • Azure AD IdP │ │ • Custom IdP │ │
│ │ │ │ │ │ │ │
│ └──────────────────┘ └──────────────────┘ └────────────────┘ │
│ │
│ Complete isolation between tenant boundaries │
└─────────────────────────────────────────────────────────────────┘
Tenant Isolation Guarantees
Data Isolation
Every piece of data in Shyntr is scoped to a tenant:
| Resource | Isolation Level |
|---|---|
| OIDC Clients | Strictly tenant-bound |
| SAML Clients | Strictly tenant-bound |
| IdP Connections | Strictly tenant-bound |
| Sessions | Strictly tenant-bound |
| Tokens | Cryptographically isolated |
| Audit Logs | Tenant-filtered |
Shyntr performs strict validation on every request to prevent cross-tenant data leakage. A token issued for Tenant A cannot be used to access resources in Tenant B—the request will be rejected at the validation layer.
Cryptographic Isolation
Each tenant can have its own signing keys:
┌─────────────────────────────────────────────────────┐
│ Key Management │
├─────────────────────────────────────────────────────┤
│ │
│ Global RSA Key (fallback) │
│ └─ Used if tenant doesn't have custom keys │
│ │
│ Tenant-Specific Keys │
│ ├─ Tenant A: RSA-2048 (own key pair) │
│ ├─ Tenant B: RSA-4096 (own key pair) │
│ └─ Tenant C: EC P-256 (own key pair) │
│ │
│ All keys encrypted at rest with APP_SECRET │
│ │
└─────────────────────────────────────────────────────┘
Creating Tenants
Via CLI
./shyntr create-tenant \
--id acme \
--name "Acme Corporation" \
--display-name "Acme Corp" \
--desc "Production tenant for Acme Corporation"
Via Admin API
curl -X POST "http://localhost:7497/admin/tenants" \
-H "Content-Type: application/json" \
-d '{
"id": "acme",
"name": "Acme Corporation",
"display_name": "Acme Corp",
"description": "Production tenant for Acme Corporation"
}'
Managing Tenant Resources
All resources are created within a tenant context:
OIDC Client in a Tenant
./shyntr create-client \
--tenant-id acme \
--name "Acme Dashboard" \
--redirect-uris "https://dashboard.acme.com/callback"
SAML Client in a Tenant
./shyntr create-saml-client \
--tenant-id acme \
--name "Acme Salesforce" \
--entity-id "https://acme.my.salesforce.com" \
--acs-url "https://acme.my.salesforce.com/sso/saml"
Identity Provider Connection
./shyntr create-oidc-connection \
--tenant-id acme \
--name "Acme Google Workspace" \
--issuer "https://accounts.google.com" \
--client-id "xxx.apps.googleusercontent.com" \
--client-secret "GOCSPX-xxx"
Tenant Discovery
Shyntr supports multiple strategies for identifying the tenant context:
1. Path-Based Routing
The tenant ID is extracted from the URL path:
https://auth.yourapp.com/{tenant_id}/.well-known/openid-configuration
https://auth.yourapp.com/acme/.well-known/openid-configuration
https://auth.yourapp.com/globex/.well-known/openid-configuration
2. Subdomain-Based Routing
The tenant ID is derived from the subdomain:
https://acme.auth.yourapp.com/.well-known/openid-configuration
https://globex.auth.yourapp.com/.well-known/openid-configuration
3. Header-Based Routing
The tenant ID is passed via a custom header:
curl "https://auth.yourapp.com/.well-known/openid-configuration" \
-H "X-Tenant-ID: acme"
The Default Tenant
When Shyntr initializes, it creates a default tenant. This tenant:
- Cannot be deleted
- Serves as the fallback when no tenant is specified
- Is ideal for single-tenant deployments
DEFAULT_TENANT_ID=default # Configurable
If you don't need multi-tenancy, simply use the default tenant. Shyntr works seamlessly in single-tenant mode without any additional configuration.
Tenant Lifecycle
Listing Tenants
./shyntr list-tenants
Viewing Tenant Details
./shyntr get-tenant acme
Updating a Tenant
./shyntr update-tenant acme \
--display-name "Acme Industries"
Deleting a Tenant
./shyntr delete-tenant acme
Deleting a tenant will also delete all associated:
- OIDC clients
- SAML clients
- Identity Provider connections
- Sessions and tokens
This action is irreversible.
Best Practices
1. Tenant Naming
Use consistent, URL-safe identifiers:
✅ Good: acme, globex-corp, wayne-industries
❌ Bad: Acme Corp, Globex Corporation, Wayne Industries Inc.
2. Isolation Testing
Always test that tenant isolation is enforced:
# Get token for Tenant A
TOKEN_A=$(get_token --tenant acme)
# Attempt to use in Tenant B context
curl "https://auth.yourapp.com/globex/userinfo" \
-H "Authorization: Bearer $TOKEN_A"
# Expected: 401 Unauthorized
3. Audit Logging
Enable comprehensive audit logging per tenant for compliance:
# Each tenant's authentication events are logged separately
# Useful for compliance (SOC2, HIPAA, GDPR)
Use Cases
| Scenario | Configuration |
|---|---|
| SaaS Platform | One tenant per customer organization |
| Enterprise SSO | One tenant per department or subsidiary |
| Development | Separate tenants for dev/staging/production |
| White-Label | One tenant per white-label partner |
Next Steps
- Learn about Zero Trust Security features
- Follow the Docker Compose Guide for deployment
- Explore the CLI Reference for tenant management commands