GenEmails Multi-Tenant Login Process Document

View the related infographic

GenEmails Multitenant Login Process

This document explains the architecture shown in `gem-multitenant-login-process-hd.png`. The diagram describes how GenEmails activates a tenant, authenticates users through Genesys Cloud, enforces tenant-specific licensing and access rules, and scopes Admin Console actions to the correct tenant.

Architecture Goals

The multitenant architecture has four primary goals:

  • Bind each customer tenant to a durable Genesys organization identity.
  • Use activation keys only for setup, not as the long-term tenant identity.
  • Enforce login access through license state, Genesys role entitlement, tenant membership, and seat limits.
  • Keep Admin Console configuration tenant-scoped unless the user is a master admin performing an explicitly cross-tenant action.

High-Level Flow

The diagram is organized into three swimlanes:

  • **Tenant Activation**: creates and configures a tenant before normal users log in.
  • **User Login**: authenticates a Genesys user, resolves the tenant, validates access, and starts a GenEmails session.
  • **Admin Console**: lets master admins and tenant admins manage tenant configuration, access rules, licenses, logs, and application settings.

The right side of the diagram shows the primary data stores used by these flows.

Tenant Activation

Tenant activation is the onboarding process that turns a customer into an active GenEmails tenant.

1. Activation Key

An activation key is issued to start tenant setup. It proves that the customer is allowed to activate GenEmails, but it should not become the permanent tenant identity.

Recommended behavior:

  • Store activation keys hashed.
  • Keep only a short key prefix for lookup and support.
  • Mark activation keys as setup-only.
  • Expire or consume activation keys after successful tenant creation.

2. Validate Key

The activation service validates that the provided key exists, has not expired, has not already been consumed, and is allowed to create or bind the requested tenant.

Possible outcomes:

  • Valid key: continue tenant activation.
  • Invalid, expired, revoked, or already used key: deny activation and write an audit event.

3. Bind Genesys Org ID

The activation process binds the new tenant to the Genesys organization ID. This is the durable identity that GenEmails should use for tenant resolution.

The Genesys org ID matters because users authenticate through Genesys Cloud. During login, GenEmails can call `GET /api/v2/users/me`, extract the user's organization ID, and match it to the tenant record.

4. Create Tenant

After the activation key and Genesys organization are validated, GenEmails creates the tenant record.

The tenant record should represent the customer organization, not an individual user.

Typical tenant fields:

  • `id`
  • `name`
  • `genesys_org_id`
  • `status`
  • `created_at`
  • `activated_at`
  • `created_by_user_id`

5. Configure Tenant Settings

After tenant creation, GenEmails stores tenant-level configuration. These settings control how the tenant connects to Genesys, how licenses are enforced, and what Admin Console options apply to that tenant.

Configuration can include:

  • License mode and seat limits.
  • Required Genesys role for user access.
  • Connection settings.
  • Allowed domains.
  • Email routing settings.
  • Application settings.

User Login

The user login flow is the core runtime path. It determines whether a Genesys user may open GenEmails for a specific tenant.

1. Sign In With Genesys

The user starts by signing in with Genesys Cloud. GenEmails should use OAuth rather than handling the user's Genesys password directly.

The result is an authenticated Genesys session or token that GenEmails can use to call Genesys APIs allowed by the configured scopes.

2. `GET /api/v2/users/me`

After authentication, GenEmails calls:

GET /api/v2/users/me

This call identifies the current Genesys user and organization.

Important values:

  • `organization.id`
  • Genesys user ID
  • Email address
  • Display name

The `organization.id` is the key value used to resolve the tenant.

3. Resolve Tenant by Org ID

GenEmails matches the Genesys `organization.id` to `tenants.genesys_org_id`.

Decision: **Org match?**

  • **Yes**: continue to license validation.
  • **No**: deny access and write an audit event.

This prevents a user from one Genesys organization from entering another tenant's GenEmails environment.

4. Check Active License

After resolving the tenant, GenEmails loads the tenant's active license.

Decision: **License active?**

  • **Yes**: continue to role validation.
  • **No**: deny access and write an audit event.

License validation should check:

  • License status.
  • Start and expiration dates.
  • Seat limits.
  • License type, such as named or concurrent.
  • Whether the license applies to the resolved tenant.

5. Verify Required Role

GenEmails checks whether the user has the tenant-required Genesys role.

Decision: **Role allowed?**

  • **Yes**: continue to user and tenant membership update.
  • **No**: deny access and write an audit event.

The required role is tenant-specific and should come from `tenant_access_rules`. A role ID is safer than a role name when Genesys provides one, because names can change.

Example:

Tenant A requires role GEM_Key_A
Tenant B requires role GEM_Key_B

This allows each tenant to control entitlement without sharing a global role rule.

6. Upsert User + Tenant User

Once the user passes organization, license, and role checks, GenEmails updates two related records:

  • `users`
  • `tenant_users`

These are intentionally separate.

`users` answers:

Who is this person?

`tenant_users` answers:

Is this person allowed in this tenant, and what tenant-specific state applies?

This separation lets GenEmails track one identity while still enforcing tenant-specific status, roles, license assignment, and audit history.

7. Create / Refresh Session

GenEmails creates or refreshes the user's active session in `user_sessions`.

The session record supports:

  • Active user tracking.
  • Concurrent license enforcement.
  • Last-seen timestamps.
  • Session expiration.
  • Login and logout audit history.

For concurrent licensing, active sessions should be counted from session records rather than from a manually maintained counter.

8. Enforce Seat Limit

GenEmails checks whether the tenant has available seats.

Decision: **Seats available?**

  • **Yes**: open GenEmails.
  • **No**: deny access and write an audit event.

Seat enforcement depends on license type:

  • **Named license**: count active tenant users assigned to the tenant.
  • **Concurrent license**: count active sessions in `user_sessions`.

9. Open GenEmails

If every decision passes, the user can open GenEmails.

At this point, all runtime queries should continue to enforce tenant scope. The authenticated user should only see queues, email data, logs, settings, and administrative options permitted for the resolved tenant and their role.

Admin Console

The Admin Console is the management surface for GenEmails. In a multitenant architecture, Admin Console behavior must be tenant-aware.

Master Admin

The master admin is the only explicitly cross-tenant administrative role.

Master admin responsibilities can include:

  • Viewing all tenants.
  • Creating or disabling tenants.
  • Issuing activation keys.
  • Managing global license records.
  • Troubleshooting cross-tenant platform issues.

Master admin access should be rare, audited, and clearly separated from tenant admin access.

Tenant Admin

A tenant admin manages only one tenant at a time.

Tenant admin responsibilities can include:

  • Managing tenant settings.
  • Reviewing logs for their tenant.
  • Configuring access rules.
  • Updating connection settings.
  • Reviewing license usage for their tenant.

A tenant admin should never read or modify another tenant's data.

Tenant Selector

The tenant selector appears for users who are allowed to administer more than one tenant, such as master admins.

For normal tenant admins, the selected tenant should be fixed to their assigned tenant.

Every Admin Console action should be evaluated against the selected `tenant_id`.

License Manager

The License Manager controls tenant license records.

It should support:

  • License status.
  • License type.
  • Seat limits.
  • Effective dates.
  • Expiration dates.
  • License event history.

License changes should write to `license_events`.

Access Rules

Access Rules define what a Genesys user needs before GenEmails allows login for a tenant.

These rules are stored in `tenant_access_rules`.

Typical rules:

  • Required Genesys role name.
  • Required Genesys role ID.
  • Whether role enforcement is enabled.
  • Optional domain restrictions.
  • Optional tenant admin rules.

Connection Settings

Connection settings define tenant-specific integration values.

These belong in `tenant_connection_settings` and should be scoped by `tenant_id`.

Examples:

  • Genesys region.
  • OAuth client configuration reference.
  • API connection mode.
  • Email integration settings.
  • Routing-related settings.

Secrets should be stored securely, not as plain text in general configuration tables.

Domain Inspector

Domain Inspector is an Admin Console feature for reviewing tenant-specific domain or routing behavior.

Because domain behavior can differ by tenant, any Domain Inspector data should be filtered by `tenant_id`.

Logs

Logs should be tenant-scoped unless viewed by a master admin.

Useful log categories:

  • Login attempts.
  • Activation attempts.
  • License failures.
  • Role failures.
  • Seat-limit failures.
  • Admin Console changes.
  • Genesys API errors.

Application Settings

Application Settings are configurable options that affect GenEmails behavior for a tenant.

Examples:

  • Feature flags.
  • Queue or routing preferences.
  • UI behavior.
  • Notification options.
  • Email handling settings.

Each setting should carry `tenant_id` unless it is truly global.

`tenant_id enforced`

The diagram calls out `tenant_id enforced` because it is the key Admin Console rule.

Every non-global Admin Console feature should include tenant enforcement in:

  • Database queries.
  • API authorization checks.
  • UI filtering.
  • Audit logging.
  • Create, update, and delete operations.

Data Stores

The right side of the diagram lists the core tables used by the architecture.

`tenants`

Stores one row per customer tenant.

Purpose:

  • Durable tenant identity.
  • Genesys organization binding.
  • Tenant status.
  • Activation metadata.

Typical fields:

  • `id`
  • `name`
  • `genesys_org_id`
  • `status`
  • `created_at`
  • `activated_at`

`licenses`

Stores license records for tenants.

Purpose:

  • Determine whether a tenant can use GenEmails.
  • Define license type and seat limits.
  • Control start and expiration dates.

Typical fields:

  • `id`
  • `tenant_id`
  • `license_key_hash`
  • `license_key_prefix`
  • `status`
  • `license_type`
  • `seat_limit`
  • `starts_at`
  • `expires_at`

`tenant_access_rules`

Stores tenant-specific login and entitlement rules.

Purpose:

  • Define the Genesys role required to access GenEmails.
  • Allow each tenant to enforce different role rules.
  • Keep access behavior configurable without code changes.

Typical fields:

  • `id`
  • `tenant_id`
  • `required_role_name`
  • `required_role_id`
  • `enforce_role_check`
  • `allowed_email_domain`
  • `created_at`
  • `updated_at`

This table is why the login flow can ask, "Role allowed?" after resolving the tenant.

`users`

Stores the global user identity known to GenEmails.

Purpose:

  • Represent the person.
  • Store stable identity values from Genesys.
  • Avoid duplicating the same person record for every tenant-specific state change.

Typical fields:

  • `id`
  • `genesys_user_id`
  • `email`
  • `display_name`
  • `last_login_at`
  • `created_at`

`tenant_users`

Stores the user's membership and status within a tenant.

Purpose:

  • Represent the relationship between a user and a tenant.
  • Track tenant-specific enablement, role, license assignment, and first/last seen timestamps.
  • Support named license counting.

Typical fields:

  • `id`
  • `tenant_id`
  • `user_id`
  • `status`
  • `app_role`
  • `license_assigned`
  • `first_seen_at`
  • `last_seen_at`

This table is separate from `users` because the same person identity and the person's tenant membership are different concepts.

`user_sessions`

Stores active and historical login sessions.

Purpose:

  • Track active users.
  • Enforce concurrent license limits.
  • Support session expiration and logout.
  • Provide login audit evidence.

Typical fields:

  • `id`
  • `tenant_id`
  • `user_id`
  • `started_at`
  • `last_seen_at`
  • `ended_at`
  • `ip_address`
  • `user_agent`

`license_events`

Stores license and access-related audit events.

Purpose:

  • Record license activation, expiration, and updates.
  • Record denied logins due to license, role, org, or seat failures.
  • Support troubleshooting and compliance.

Typical fields:

  • `id`
  • `tenant_id`
  • `user_id`
  • `event_type`
  • `event_message`
  • `created_at`
  • `metadata_json`

`admin_accounts`

Stores Admin Console account records or admin mappings.

Purpose:

  • Identify master admins.
  • Identify tenant admins.
  • Separate platform administration from regular GenEmails usage.

Typical fields:

  • `id`
  • `user_id`
  • `tenant_id`
  • `admin_type`
  • `status`
  • `created_at`

For master admins, `tenant_id` may be null or handled through a separate scope model. For tenant admins, `tenant_id` should be required.

`tenant_connection_settings`

Stores tenant-specific integration configuration.

Purpose:

  • Keep Genesys connection behavior isolated per tenant.
  • Support per-tenant Admin Console configuration.
  • Avoid sharing one tenant's connection settings with another tenant.

Typical fields:

  • `id`
  • `tenant_id`
  • `genesys_region`
  • `oauth_client_reference`
  • `settings_json`
  • `created_at`
  • `updated_at`

Sensitive values should be encrypted or stored in a secret manager, with only references stored in this table.

Why `users` and `tenant_users` Are Separate

The split between `users` and `tenant_users` is a standard multitenant pattern.

`users` is the identity table.

Example:

Maria Garcia
maria@example.com
Genesys user ID: abc-123

`tenant_users` is the tenant membership table.

Example:

Maria belongs to Tenant A as an active agent.
Maria belongs to Tenant B as a disabled user.
Maria has a named license in Tenant A.
Maria has no license in Tenant B.

This design supports:

  • Tenant-specific access status.
  • Tenant-specific roles.
  • Tenant-specific license assignment.
  • Tenant-specific audit history.
  • Cleaner user deactivation without deleting identity history.
  • Future support for users who may administer or support more than one tenant.

Why `tenant_access_rules` Exists

`tenant_access_rules` exists because access requirements can differ by tenant.

Without this table, the application would likely hard-code a single role such as `GEM_Key` for every tenant. That is less flexible and harder to manage.

With `tenant_access_rules`, GenEmails can support:

  • Tenant A requiring `GEM_Key`.
  • Tenant B requiring `GEM_Agent`.
  • Tenant C disabling role enforcement temporarily during onboarding.
  • Tenant-specific admin roles.
  • Future changes without deploying new code.

The login flow uses this table after resolving the tenant and before creating the user session.

Deny Access and Audit Event

Failed checks should not silently fail. They should deny access and write an audit event.

Important failure cases:

  • No tenant matches the Genesys organization ID.
  • License is inactive, expired, missing, or revoked.
  • User does not have the required Genesys role.
  • Seat limit is exceeded.
  • Admin user attempts to access another tenant's data.

Audit events should include enough context for troubleshooting without exposing secrets.

Useful audit context:

  • `tenant_id`
  • `user_id`
  • Genesys organization ID
  • failure reason
  • request timestamp
  • IP address
  • user agent

Security Notes

Activation Keys Are Setup-Only

Activation keys are used to start or authorize tenant setup. After activation, the tenant should be resolved by Genesys organization ID, not by repeatedly presenting the activation key.

Genesys Org ID Is the Durable Tenant Identity

The Genesys organization ID is the best long-term tenant binding because it comes from the identity provider during login.

Admin Console Actions Are Tenant-Scoped

All Admin Console actions should enforce tenant scope unless the action is explicitly global and performed by a master admin.

Master Admin Is Cross-Tenant Only

Cross-tenant access should be limited to master admin functions. Tenant admins should not have cross-tenant read or write access.

Implementation Checklist

Use this checklist when implementing or reviewing the architecture:

  • Activation keys are stored hashed.
  • Activation keys are consumed or expired after use.
  • Tenant records store `genesys_org_id`.
  • Login calls `GET /api/v2/users/me`.
  • Tenant resolution uses `organization.id`.
  • License checks happen before session creation.
  • Required role checks come from `tenant_access_rules`.
  • Users are stored in `users`.
  • Tenant membership is stored in `tenant_users`.
  • Sessions are stored in `user_sessions`.
  • Seat limits are derived from data, not manual counters.
  • Admin Console APIs enforce `tenant_id`.
  • Tenant admins cannot access other tenants.
  • Master admin actions are audited.
  • License and access failures write `license_events`.
  • Sensitive tokens and secrets are encrypted or stored outside normal configuration tables.