> For the complete documentation index, see [llms.txt](https://docs.cloudassert.com/hybr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cloudassert.com/hybr/features/tenant-management.md).

# Tenant Management

### <mark style="color:$primary;">Login</mark>

The tenant portal has no local username/password store — every sign-in is delegated to an external identity provider (Azure AD, ADFS, Azure AD B2C, IdentityServer, Keycloak, or the custom "MyIdentity" provider) via OWIN/OIDC.&#x20;

{% hint style="info" %}
"Login" is really an identity-provider chooser page that redirects out; credential entry, password reset, and (for most providers) MFA all happen on the provider's own hosted pages
{% endhint %}

### <mark style="color:$primary;">📅</mark><mark style="color:$primary;">**View the Login Page**</mark>

Served by `UserRegistrationController.Register()` (`Views/UserRegistration/RegisterForm.cshtml`), `[AllowAnonymous]`. Shows one button per enabled identity provider ("Login using Azure Active Directory", "Login using ADFS", etc.), each posting to `Accounts/Login?identityProviderType=...`. Branding (logo, background image, welcome heading/HTML, footer text) is pulled live from Portal Settings.

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="2712">✒️</span> Steps to View the Login Page</summary>

* Navigate to the tenant portal URL — you land on the combined Login/Register page (or are redirected straight past it, see below).
* Click the button for your organization's identity provider.
* You're redirected to that provider's own hosted sign-in page — enter credentials and complete MFA there if required.

</details>

{% hint style="info" %}
**Tip:** If your organization has exactly one identity provider enabled and self-registration is turned off, it skips this page entirely and redirects straight into the provider's challenge — most single-tenant deployments never actually see a chooser page.
{% endhint %}

{% hint style="info" %}
A "Personal Account" section/button appears separately if a third-party/personal Azure AD B2C tenant (`ThirdPartyAADB2C`) is enabled — this lets individual users sign in outside their employer's directory.
{% endhint %}

## Supported Identity Providers

### <mark style="color:$primary;">🔌</mark><mark style="color:$primary;">**Provider Types**</mark>

Defined in `IdentityProviderType`: Azure AD (`Aad`), ADFS 2016 (`Adfs2016`), a generic OIDC IdentityServer (`Idsvr`), Azure AD B2C (`AadB2c`), a separate "personal account" B2C tenant (`ThirdPartyAADB2C`), Keycloak, and a bespoke SDK-based provider (`MyIdentity`).

| Provider                      | Enable Flag                 |
| ----------------------------- | --------------------------- |
| Azure AD                      | `IsAADEnabled`              |
| ADFS 2016                     | `IsADFS2016Enabled`         |
| IdentityServer (generic OIDC) | `IsIDSVREnabled`            |
| Keycloak                      | `IsKeyCloakEnabled`         |
| Azure AD B2C                  | `IsAADB2CEnabled`           |
| Third-party / personal B2C    | `IsThirdPartyAADB2CEnabled` |
| MyIdentity (custom SDK)       | `IsMyIdentityEnabled`       |

{% hint style="info" %}
Each `TenantCompany` also carries its own `IdentityProviderType` and issuer/tenant details — different companies within the same Trisul.Org deployment can be bound to different IdP tenants/directories of the same provider type (e.g. two customers each with their own Azure AD tenant).
{% endhint %}

### <mark style="color:$primary;">Multi-Factor Authentication</mark>

🔒**MFA is enforced by the identity provider, not by Trisul.Org** — with one exception: for Azure AD specifically, sign-in is unconditionally rejected unless the token's `amr` claim contains `"mfa"` ("Multi-factor authentication is not currently enforced for the logged-in user…"). This check is hard-coded, not a toggle. No equivalent check exists for ADFS/IdentityServer/B2C/Keycloak/MyIdentity — MFA there depends entirely on how the customer's own identity provider is configured.

SMS/email one-time codes also exist in this codebase, but they verify contact info during self-registration (gated by `IsSmsFeatureEnabled`) — that's account verification, not a login MFA factor. Don't conflate the two.

### <mark style="color:$primary;">Forgot Password</mark>

#### <mark style="color:$primary;">✏️</mark><mark style="color:$primary;">**Password Reset — Azure AD B2C only**</mark>

There is no locally-hosted "forgot password" form. For B2C, clicking "Forgot your password?" on B2C's own hosted sign-in page returns a well-known error code that Trisul.Org detects and auto-redirects into B2C's password-reset policy (`AccountsController.ResetPassword`, using the `AADB2CPasswordResetPolicy` app setting) — again, a fully hosted B2C page, not a Trisul.Org screen.

{% hint style="info" %}
**Important:** For AAD, ADFS, IdentityServer, Keycloak, and MyIdentity, there is no equivalent handling in this codebase. Password reset for those happens entirely outside Trisul.Org — via the customer's own Azure AD self-service reset, ADFS, Keycloak account console, etc.
{% endhint %}

### <mark style="color:$primary;">Multi-Tenancy at Login</mark>

#### <mark style="color:$primary;">🌐</mark><mark style="color:$primary;">**Which company do I land in?**</mark>

After the identity provider confirms who you are, Trisul.Org resolves which `TenantCompany` you belong to by matching your identity's issuer against companies you're associated with. If you belong to more than one company, your last-selected company (`UserSettings.DefaultCompanyId`) is used if still valid.

**`AllowCrossDomainUser`** — permits a user's home-tenant identity to resolve into a different tenant company's data (used with reseller/service-provider delegated access).

**Auto-onboarding** — if a company is configured with `IsAutoOnboardUsers` and your issuer matches, you're silently created as a new tenant user on first successful login, with no explicit invite needed.

**`IsUserGroupSupportEnabled`** — lets group-claims from the identity provider drive auto-onboarding by group membership rather than per-user invites.

#### <mark style="color:$primary;">Switching Companies</mark>

`AccountsController.SwitchCompany` — for users belonging to multiple companies. This is not a simple client-side context swap: it persists your chosen company as the new default and **re-issues a fresh OIDC challenge** scoped to that company's identity provider. If the target company uses a different identity provider or directory than your current session, expect to see another provider's sign-in prompt.

{% hint style="info" %}
When `IsResellerModelEnabled` is on, the accessible-companies set is computed differently for reseller vs. end-customer companies, so a reseller admin's company-switch list is scoped to their downstream customers.
{% endhint %}

### <mark style="color:$primary;">Invite Acceptance</mark>

#### <mark style="color:$primary;">📧</mark><mark style="color:$primary;">**Accepting a Co-Administrator Invite**</mark>

There is no invite-specific "set your password" screen (no local password exists to set). An invited co-admin receives an email with a link to `TenantMyAccount/AcceptInvite`. Landing there **requires the invitee to already be signed in** (they go through the same identity-provider login as anyone else — possibly auto-onboarded per the rules above) and to already hold at least Contributor-level access. Accepting simply marks the invite record `Accepted` and redirects back into `Accounts/Login`.

### <mark style="color:$primary;">🚪</mark><mark style="color:$primary;">**Sign Out**</mark>

`AccountsController.SignOut` clears the server-side user/company cache and session, then performs a **federated sign-out** — signing out of both the local cookie and the upstream identity provider — before landing on a Sign-Out confirmation page.
