1.5 Tenant Auth Configuration
Section 1 — Studio Accounts
1.5 Tenant Auth Configuration
What Tenant Auth Config Is
By default, all player authentication providers (Steam, Epic, wallet, email) operate with platform-level credentials. Tenant auth configuration allows each tenant to supply their own provider credentials — their own Steam API key, Epic deployment, Sequence project, etc.
When a tenant has a config registered for a provider, the player login flow uses that tenant's credentials instead of the platform defaults. If no config is registered or it is disabled, the login attempt is rejected for that provider on that tenant.
Each config is scoped to exactly one (tenant_id, provider) pair. A tenant can configure
multiple providers independently.
Supported Providers
| Provider | Tenant config supported | Required config keys |
|---|---|---|
Steam | Yes | apiKey, appId, webApiIdentity |
Epic | Yes | clientId, clientSecret, productId, deploymentId |
Sequence | Yes | projectId |
EvmWallet | Yes | None — wallet validation is key-less |
Email | Yes | None — uses platform email infrastructure |
Email One-Time Code | Yes | None — uses platform email infrastructure |
Mock | No | Test/dev provider; cannot be tenant-configured |
Providers with required keys fail validation if any key is missing at upsert time. Providers
with no required keys (EvmWallet, Email, Email One-Time Code) only need to exist and be
enabled. Player auth DTOs use the enum-style value EmailOneTimeCode; tenant auth
configuration uses the provider string Email One-Time Code.
Storage and Encryption
Config data is stored in bus_tenant_auth_configs.config_data as an encrypted JSONB column.
Encryption is AES-256-GCM using an envelope format:
enc:v2:{keyId}:{nonce_b64}:{cipher_b64}:{tag_b64}- 12-byte nonce, 16-byte authentication tag, 256-bit keys
- Key ring loaded from
Encryption:Keysin app configuration - Active key identified by
Encryption:CurrentKeyId - Key rotation is supported — old keys remain in the ring to decrypt existing data
Sensitive field values are masked in all API responses — any configured value is shown as
[configured]. Decrypted values are only used internally by the player login flow.
How It Integrates with Player Login
When a player attempts to log in, IAuthProviderConfigGuard runs before the provider validator:
- Looks up the tenant's config for the requested provider.
- If no config exists or
is_enabled = false→ login is rejected (422 Unprocessable Entity). - If config exists and is enabled → decrypted config data is passed to the provider validator.
A tenant must explicitly configure each provider they want to support. There is no implicit fallback to platform credentials.
Encrypted configs are cached in shared Redis-backed distributed cache for 5 minutes
(auth_config:{tenantId}:{provider}), and each cache hit is decrypted on read. Cache is
invalidated on upsert and delete.
Available Operations
All operations require a SaaS JWT with owner or admin role on the tenant.
| Operation | Notes |
|---|---|
| List all configs for a tenant | Returns all providers with masked values; also lists which providers are available to configure |
| Get config for a specific provider | Returns masked config; 404 if not configured |
| Create or update a provider config | Validates required keys; encrypts and stores; audit-logged |
| Delete a provider config | Removes the config; invalidates cache; audit-logged |
There is no separate enable/disable endpoint — enabling/disabling is done via isEnabled in
the upsert request.
Audit Logging
| Operation | Logged |
|---|---|
| Config created | Yes — provider name, isEnabled state |
| Config updated | Yes — before/after diff of non-sensitive fields; sensitive values not diffed |
| Config enabled / disabled | Yes |
| Config deleted | Yes |
Raw secret values are never written to the audit log.
Limitations
- No fallback to platform credentials. If a tenant has no config for a provider, logins via that provider are blocked for that tenant.
- No partial key updates. The upsert replaces the entire
config_datadictionary. To update a single key, the caller must re-submit all required keys. - Secrets cannot be read back. If a secret is lost, the only option is to re-submit the full config.
- Mock provider is not configurable.
Code References
- Controller:
GamersLabRestAPI/Saas/Business/Controller/TenantAuthConfigController.cs - Service interface:
GamersLabRestAPI/Saas/Business/Services/ITenantAuthConfigService.cs - Service:
GamersLabRestAPI/Saas/Business/Services/TenantAuthConfigService.cs - Repository interface:
GamersLabRestAPI/Saas/Business/Repositories/ITenantAuthConfigRepository.cs - Repository:
GamersLabRestAPI/Saas/Business/Repositories/NpgsqlTenantAuthConfigRepository.cs - Model:
GamersLabRestAPI/Saas/Business/Models/TenantAuthConfigModel.cs - DTOs:
GamersLabRestAPI/Saas/Business/DTOs/TenantAuthConfigDTOs.cs - Provider registry:
GamersLabRestAPI/Core/Constants/AuthProviderRegistry.cs - Encryption service:
GamersLabRestAPI/Saas/Business/Services/EncryptionService.cs - PlayerAuth guard:
GamersLabRestAPI/PlayerAuth/Services/IAuthProviderConfigGuard.cs