Appearance
Switch Tenant Flow
This page documents the login flow when a user has no default tenant configured, requiring tenant selection before accessing the application.
Overview
In GrydAuth's multi-tenant architecture, users can belong to multiple tenants. When no tenant is marked as default (IsDefault = false for all UserTenant records), the user must explicitly select which tenant to use after initial authentication.
When This Flow is Triggered
| Scenario | Description |
|---|---|
| No default tenant | All UserTenant.IsDefault = false |
| Multiple tenants, none default | User belongs to several tenants with no preference |
| First tenant assignment | User was just added to a new tenant |
| Admin removed default flag | Default was cleared by administrator |
Sequence Diagram
100% 💡 Use Ctrl + Scroll para zoom | Arraste para navegar
Step-by-Step Explanation
Phase 1: Initial Login (Pre-Authentication)
1.1 Credential Validation
Standard credential validation occurs:
- User lookup by email
- Password verification
- AppId validation
1.2 Default Tenant Check
The system attempts to find a default tenant:
sql
SELECT * FROM UserTenants
WHERE UserId = @userId AND IsDefault = true AND IsActive = trueIf no result is returned, tenant selection is required.
1.3 Retrieve Available Tenants
All active tenants for the user are fetched:
sql
SELECT ut.*, t.Name, t.Description
FROM UserTenants ut
JOIN Tenants t ON ut.TenantId = t.Id
WHERE ut.UserId = @userId AND ut.IsActive = true AND t.IsActive = true1.4 Pre-Auth Token Generation
A limited-purpose token is created:
json
{
"sub": "user-id",
"purpose": "tenant_selection",
"available_tenants": ["tenant-1-id", "tenant-2-id"],
"exp": 1706885400 // 5 minutes
}1.5 Response
json
{
"requiresTenantSelection": true,
"preAuthToken": "eyJhbGciOiJIUzI1NiIs...",
"availableTenants": [
{
"id": "tenant-1-id",
"name": "Acme Corporation",
"description": "Main company account"
},
{
"id": "tenant-2-id",
"name": "Acme Subsidiary",
"description": "Regional office"
}
]
}Phase 2: Tenant Selection
2.1 User Selects Tenant
The client application displays a tenant selector:
typescript
interface TenantOption {
id: string;
name: string;
description?: string;
}
// UI shows dropdown or card selection2.2 Switch Tenant Request
json
{
"preAuthToken": "eyJhbGciOiJIUzI1NiIs...",
"tenantId": "tenant-1-id",
"setAsDefault": true
}2.3 Validation
The system validates:
- Pre-auth token is valid and not expired
- Selected tenant is in the
available_tenantslist - Tenant is still active
- UserTenant association is still active
2.4 Set Default (Optional)
If setAsDefault = true:
sql
-- Clear existing defaults
UPDATE UserTenants SET IsDefault = false WHERE UserId = @userId;
-- Set new default
UPDATE UserTenants SET IsDefault = true
WHERE UserId = @userId AND TenantId = @tenantId;User Experience
Setting a default tenant improves UX - the user won't need to select a tenant on future logins.
2.5 Full Token Generation
Now with a selected tenant, the system generates complete JWT tokens with roles and permissions for that specific tenant context.
Phase 3: Authenticated Session
After successful tenant selection, the user receives full authentication tokens and can access protected resources.
Switching Tenants During Session
Already authenticated users can switch tenants without re-entering credentials:
100% 💡 Use Ctrl + Scroll para zoom | Arraste para navegar
Error Scenarios
| Error | HTTP Status | Cause |
|---|---|---|
| Session expired | 401 | Pre-auth token expired (5 min) |
| Access denied | 403 | Tenant not in available list |
| Tenant inactive | 403 | Tenant was deactivated |
| User removed | 403 | UserTenant association removed |
Security Considerations
Pre-Auth Token
- Short expiration (5 minutes)
- Contains list of allowed tenants (prevents tampering)
- Cannot be used for API access
- Single-use (invalidated after tenant selection)
Cross-Tenant Access
When User.AllowCrossTenantAccess = true:
- User can access resources across tenants
- JWT contains additional claims for cross-tenant validation
- Audit logs track cross-tenant access
Code Example
Client-Side Implementation
typescript
interface LoginResponse {
accessToken?: string;
refreshToken?: string;
requiresTenantSelection?: boolean;
preAuthToken?: string;
availableTenants?: Tenant[];
}
async function handleLogin(email: string, password: string): Promise<void> {
const response = await authApi.login({ email, password, appId });
if (response.requiresTenantSelection) {
// Store pre-auth token and show tenant selector
sessionStorage.setItem('preAuthToken', response.preAuthToken!);
tenantStore.setAvailable(response.availableTenants!);
router.push('/select-tenant');
return;
}
// Direct login - default tenant used
authStore.setTokens(response.accessToken!, response.refreshToken!);
router.push('/dashboard');
}
async function selectTenant(tenantId: string, setAsDefault: boolean): Promise<void> {
const preAuthToken = sessionStorage.getItem('preAuthToken');
const response = await authApi.switchTenant({
preAuthToken,
tenantId,
setAsDefault
});
sessionStorage.removeItem('preAuthToken');
authStore.setTokens(response.accessToken, response.refreshToken);
authStore.setCurrentTenant(response.tenant);
router.push('/dashboard');
}
// Switch tenant during active session
async function switchTenant(newTenantId: string): Promise<void> {
const response = await authApi.switchTenant({ tenantId: newTenantId });
authStore.setTokens(response.accessToken, response.refreshToken);
authStore.setCurrentTenant(response.tenant);
// Refresh current view with new tenant context
window.location.reload();
}Tenant Selector Component (Vue)
vue
<template>
<div class="tenant-selector">
<h2>Select Your Organization</h2>
<div class="tenant-list">
<div
v-for="tenant in availableTenants"
:key="tenant.id"
class="tenant-card"
@click="selectTenant(tenant.id)"
>
<h3>{{ tenant.name }}</h3>
<p>{{ tenant.description }}</p>
</div>
</div>
<label>
<input type="checkbox" v-model="setAsDefault" />
Remember my choice
</label>
</div>
</template>Related Flows
- Login Flow - Standard login with default tenant
- First Login - When password change is required
- Multi-Tenancy Architecture - Deep dive into multi-tenancy