Eviworx
Docs

User Management Architecture

🛡️ Permission-/Authz-Modell: Das Rechtemodell (Actor-Modell, Rechteprüfung, Caching, Sichtbarkeit, Verknüpfungen) ist auf einer eigenen Seite beschrieben: Permissions & RBAC →. Diese Seite behandelt das operative User-/Agent-/Gruppen-/Absence-Management.

Das User-Management umfasst Rollen und Rechte (RBAC mit 28+ Modulen, frei definierbare Rollen inkl. Datenschutzbeauftragter), Agent-Groups mit automatischer Zuweisung und Kapazitätsgrenzen, Abwesenheiten mit Vertretung, Einladungen per E-Mail, Zwei-Faktor-Anmeldung (TOTP), Follower auf Tickets sowie die Anbindung an Entra ID.

🏗️
Funktionen
✓ RBAC (28+ Module, 350+ Einzelrechte)
✓ Rechte-Cache pro Rolle (5 min)
✓ Dynamische Rollen (4 System-Rollen + DSB)
✓ Einladung per E-Mail (Passwort per Link)
✓ Zwei-Faktor-Anmeldung (TOTP)
✓ DSGVO-Datenexport und Anonymisierung
✓ Agent-Kapazität (maxWorkload)
✓ 4 Zuweisungs-Strategien (z. B. ROUND_ROBIN)
✓ Vertretung bei abwesenden Agents
✓ EntraID-Sync (Gruppe → Rolle)

System-Übersicht

USER-MANAGEMENT ARCHITEKTUR
===============================================================================

User Model
  * id, email, password, name, avatar
  * roleId (FK to Role)
  * roleChangedAt, roleChangedBy (Audit)
  * theme, language (DE, EN, ES, FR, IT)
  * twoFactorEnabled, twoFactorSecret (MFA/TOTP)
  |
  +-- 1:1 -> ManagedUser (END_USER profile)
  |   * firstName, lastName, phone, department, location
  |   * isActive, isArchived (Soft-delete)
  |   * lockSource: ADMIN, ENTRA_SYNC, SYSTEM (set while locked)
  |   * entraIDUserId (SSO mapping)
  |   * source: PORTAL, EMAIL, ASSET_CHECKOUT (origin snapshot)
  |   * emailOnlyContact, autoCreatedFromEmail
  |   * invitationStatus: PENDING / ACCEPTED
  |   * invitationSentAt, invitationSentBy
  |
  +-- 1:1 -> Agent (AGENT profile)
  |   * workload (Current assigned items)
  |   * maxWorkload (Capacity limit per agent)
  |   * lastActivity
  |   * N:M -> AgentGroup (via AgentGroupMember)
  |
  +-- 1:N -> UserAbsence (Absences)
  |   * type, startDate, endDate, substituteId
  |   * status (PENDING/APPROVED/REJECTED)
  |
  +-- N:M -> TicketParticipant (Follower System)
      * role: CC, FOLLOWER, MENTIONED
      * source: MANUAL, EMAIL_CC, MENTION, MERGE, FOLLOW,
                CUSTOMER_CHANGE, SLA_ESCALATION

                            |
                            v

Role Model (Dynamic)
  * id, name, displayName, description
  * isSystem (system roles cannot be deactivated, deleted or moved), isActive
  * priority (EntraID conflict resolution)
  * permissions (JSONB) - 28+ feature modules
  * entraIDRoleId (Azure AD Group mapping)
  |
  +-- System Roles (protected):
  |     END_USER (priority: 99000)
  |     AGENT (priority: 2000)
  |     ADMIN (priority: 1000)
  |     APPROVER (priority: 3000)
  |
  +-- Pre-installed Custom Role:
  |     DATA_PROTECTION_OFFICER / DSB (priority: 2500)
  |       - Supports the GDPR Art. 33 notification duty
  |       - Can confirm data breaches in incidents
  |       - Read-only access to all incidents
  |
  +-- Custom Roles:
        Fully dynamic, created via admin UI
        Full permission matrix configuration

                            |
                            v

Permission Loading (per request, by role)

  * Read requests: role matrix from the cache (5 min, per role)
  * Modifying requests (POST/PUT/PATCH/DELETE) and critical
    actions: always fresh from the database
  * A role change via the API clears the role's cache at once

                            |
                            v

AgentGroup (Assignment System)
  * id, name, color, applicableEntityTypes[], assignmentStrategy
  * lastAssignedAgentId (for ROUND_ROBIN)
  * N:M -> Agent (via AgentGroupMember)
    +-- isActive (per-group status)
    +-- isTeamLead (read-only absence view, notifications; manages own group: skills/pause/workload)
    +-- joinedAt
  * Note: Users are assigned to AgentGroups via Agent profile directly

Absence-aware Assignment
  * Absent or inactive agents are skipped by every strategy
  * Assigning to an absent agent redirects to the substitute
  * ignoreSubstitution bypasses the redirect (audited)

Capacity
  * Agents at their maxWorkload are skipped by automatic assignment
  * ASSIGNMENT_CAPACITY_REACHED notification to the group's team leads
  * Dashboard widget for workload tracking

Rollen-System (Dynamisch)

Neben den 4 geschützten System-Rollen lassen sich beliebig viele eigene Rollen anlegen. Zusätzlich vorinstalliert ist die Rolle DATA_PROTECTION_OFFICER (DSB) für Datenschutz-Aufgaben. Rollen stehen überall zur Auswahl, wo Rechte vergeben werden, z.B. im Formular-Management.

Vorinstallierte Rollen (5)

Role ID Priority isSystem Beschreibung
END_USERrole-system-enduser99000trueBasis-User mit Ticket-Erstellung und View-Own
AGENTrole-system-agent2000trueSupport-Agent mit vollem Ticket- und Problem-Management
ADMINrole-system-admin1000trueVoller Zugriff auf alle Module und Einstellungen
APPROVERrole-system-approver3000trueGenehmigungsberechtigungen für Changes und Incidents
DATA_PROTECTION_OFFICERrole-custom-dsb2500falseDatenschutzbeauftragter (DSB): unterstützt die Meldepflicht nach DSGVO Art. 33, bestätigt Datenschutzverletzungen in Incidents, liest alle Incidents

User-Typen & Datenmodelle

User Model (Core)

interface User {
  id: string;                   // CUID
  email: string;                // Unique
  password?: string;            // Optional (invitation flow, SSO)
  name: string;                 // Full name
  avatar?: string;              // Compressed image
  theme?: string;               // light, dark, system
  language?: string;            // de, en, es, fr, it (5 languages)
  timezone?: string;            // system, local or e.g. Europe/Berlin
  dateTimeFormat?: string;      // system or e.g. dd.MM.yyyy HH:mm

  // RBAC
  roleId: string;               // FK to Role
  roleChangedAt?: Date;
  roleChangedBy?: string;       // User ID who changed

  // MFA / Two-Factor
  twoFactorEnabled: boolean;    // TOTP enabled
  twoFactorSecret?: string;     // Encrypted via TWO_FACTOR_ENCRYPTION_KEY

  // Relations
  role?: Role;
  managedProfile?: ManagedUser; // 1:1
  agent?: Agent;                // 1:1
  absences?: UserAbsence[];     // 1:N
  participants?: TicketParticipant[]; // N:M (follower/CC)

  createdAt: Date;
  updatedAt: Date;
}

Sprache, Zeitzone und Datumsform sind persönliche Einstellungen des Benutzers. Sie gelten in der Oberfläche und ebenso für Texte, die der Server erzeugt — E-Mail, Push, Teams und Webex. Ohne eigene Einstellung greift die Systemvorgabe; die vollständige Reihenfolge steht bei den Benachrichtigungen.

ManagedUser (END_USER Profile)

interface ManagedUser {
  userId: string;               // Unique FK to User (1:1)

  // Contact Info
  firstName?: string;
  lastName?: string;
  phone?: string;
  department?: string;
  location?: string;

  // Status
  isActive: boolean;            // false = locked (no sign-in)
  isArchived: boolean;          // archived = always locked
  lockSource?: string;          // ADMIN, ENTRA_SYNC, SYSTEM — set exactly while locked
  lastLogin?: Date;

  // Invitation Flow
  invitationStatus?: string;    // PENDING, ACCEPTED
  invitationSentAt?: Date;
  invitationSentBy?: string;    // Admin user ID

  // EntraID/SSO
  entraIDUserId?: string;       // Unique
  entraIDSyncedAt?: Date;
  isSyncedFromEntraID: boolean;
  entraIDConflict: boolean;     // true = user is in no mapped role group

  // Source Tracking
  source?: string;              // PORTAL, EMAIL, ASSET_CHECKOUT — snapshot of how the account was created
  emailOnlyContact: boolean;    // TRUE = no portal login
  autoCreatedFromEmail: boolean; // TRUE = auto-created from email
}

Agent Profile

interface Agent {
  userId: string;               // Unique FK to User (1:1)
  avatar?: string;              // Agent-specific avatar
  isActive: boolean;            // Global agent status

  // Assignment & Capacity
  workload: number;             // Current assigned items count
  maxWorkload?: number;         // Capacity limit (1-999, nullable = unlimited)
  lastActivity?: Date;

  // Relations
  memberships: AgentGroupMember[];  // N:M to groups
  specialties: AgentOnSpecialty[];  // For SKILL_BASED
}

Einladungs-System (Invitation Flow)

Neue User können per E-Mail eingeladen werden. Der Admin erstellt den User, eine Einladungs-E-Mail wird gesendet, und der User setzt sein Passwort über den Link.

EINLADUNGS-FLOW:

1. Admin creates user (POST /api/users, no password)
   -> User created with no password
   -> invitationStatus = null

2. Invitation is sent (POST /api/users with sendInvitation: true,
   or later POST /api/users/:id/resend-invitation)
   -> A one-time token is generated
      (valid 24 h, configurable via INVITATION_TOKEN_TTL_HOURS)
   -> Invitation email sent with password-setup link
   -> invitationStatus = 'PENDING'
   -> invitationSentAt = now()
   -> invitationSentBy = adminUserId

3. User clicks link -> validates token
   -> GET /api/auth/validate-invitation
   -> Returns user info if token valid and account active

4. User sets password
   -> POST /api/auth/setup-password
   -> Password is hashed and stored
   -> invitationStatus = 'ACCEPTED'
   -> Token invalidated
   -> User can now log in

User-Lebenszyklus & DSGVO

Ein User wird in zwei Stufen stillgelegt: erst ARCHIVIERT (isArchived; ab dann ist weder Anmeldung noch API-Zugriff möglich, bis die Verwaltung das Konto mit users.archive ent-archiviert — ein Login ändert den Status nie), dann optional ANONYMISIERT. Die Anonymisierung (Art. 17) überschreibt alle personenbezogenen Daten unwiderruflich, lässt die Zeile aber als Skelett für die Fremdschlüssel-Integrität und die Vorgangs-Historie stehen. Archivierung ist damit die bewusste Vorstufe: Ausgeschiedene User werden nach 180 Tagen automatisch anonymisiert (sofern kein aktiver Vorgang blockiert und keine Lösch-Sperre gesetzt ist). Die Frist läuft ab dem letzten Kontakt — dem späteren Zeitpunkt aus Archivierung und letzter E-Mail zu diesem Konto; ein archiviertes Konto, mit dem weiter korrespondiert wird, wird also nicht anonymisiert.

PermissionAktion
users.dataExportDatenexport einer Person (Art. 15/20), ohne Kontingent; der Self-Service-Export läuft ohne Permission über /privacy/export/me, dort höchstens einer je 24 Stunden
users.eraseVorabprüfung, Anonymisierung (nur bei archivierten Konten) und Lösch-Sperre (jederzeit, mit Begründung); kritische Aktion, DB-frisch revalidiert

Vollständiger Ablauf, Hindernisse der Vorabprüfung, Fehlercodes, Fristen, Lösch-Rückstand und Aufbewahrung: Privacy & DSGVO.

MFA / Two-Factor Authentication

User können TOTP-basierte Zwei-Faktor-Anmeldung in ihren Sicherheitseinstellungen aktivieren. Das TOTP-Secret wird mit TWO_FACTOR_ENCRYPTION_KEY verschlüsselt gespeichert.

MFA SETUP FLOW:

1. User requests setup (POST /api/auth/2fa/setup)
   -> The server generates a TOTP secret
   -> Secret encrypted with TWO_FACTOR_ENCRYPTION_KEY (AES-256-GCM)
   -> Returns QR code + backup codes

2. User verifies with authenticator app
   -> POST /api/auth/2fa/verify-setup
   -> Validates TOTP code
   -> twoFactorEnabled = true

3. On login with MFA enabled:
   -> Normal login returns twoFactorPendingToken (partial session)
   -> POST /api/auth/2fa/complete-login
   -> Validates TOTP code -> full session

ADMIN-VERWALTUNG:

  POST /api/admin/users/:id/reset-2fa        (Permission users.reset2FA)
  -> Admin can reset MFA for a user (e.g., lost device); self-reset blocked
  -> Audit-logged

Follower-System (Ticket Participants)

User können Tickets folgen und erhalten dann Notifications bei Änderungen. Das System unterstützt drei Rollen: CC (aus E-Mail), FOLLOWER (aktiv gefolgt), MENTIONED (erwähnt). FOLLOWER hat die höchste Priorität und wird nie heruntergestuft.

interface TicketParticipant {
  ticketId: string;
  userId: string;
  role: 'CC' | 'FOLLOWER' | 'MENTIONED';
  source: 'MANUAL' | 'EMAIL_CC' | 'MENTION' | 'MERGE' | 'FOLLOW' | 'CUSTOMER_CHANGE' | 'SLA_ESCALATION';
  addedAt: Date;
}

// Hierarchy: FOLLOWER > CC > MENTIONED
// If user is CC and follows → upgraded to FOLLOWER
// If user is FOLLOWER and added as CC → stays FOLLOWER (no downgrade)

Rollen-Berechtigungen (RBAC)

Jede Rolle trägt eine Permission-Matrix: pro Modul (tickets, problems, changes, incidents, assets, workflows, settings, users, audit …) eine Menge boolescher Aktionen. Eine Rollen-Änderung gilt sofort für alle Träger der Rolle; ändernde Anfragen und kritische Aktionen (z.B. *.delete, changes.approve, settings.manageIntegrations) prüfen die Rechte immer frisch. Ändert sich die Rolle eines Benutzers, schreibt das System einen Audit-Eintrag ROLE_CHANGE mit alter und neuer Rolle; er liegt in der SHA-256-Hash-Kette des Audit-Logs, nachträgliche Änderungen sind damit erkennbar.

Der vollständige Permission-Katalog (alle Module/Actions), die System-Rollen, die drei Prüfebenen sowie Laden und Caching der Rechte sind zentral dokumentiert unter Permissions & RBAC.

Agent Group Assignment System

Manager vs TeamLead

In AgentGroups gibt es zwei Führungsrollen mit unterschiedlichen Berechtigungen:

Rolle Absences Gruppen-Verwaltung Mitglieder
ManagerVolle Verwaltung (erstellen, genehmigen, ablehnen)Voller ZugriffHinzufügen/Entfernen
TeamLeadNur Lesen (kann Absences sehen, nicht verwalten)Kein Gruppen-CRUDEigene Gruppe: Skills, Pause/Aktivieren, Workload-Limit (kein Hinzufügen/Entfernen/TeamLead-Vergabe)

TeamLead-Befähigung (für Mitglieder der EIGENEN aktiven Gruppe): Specialties/Proficiency pflegen, Mitgliedschaft pausieren/aktivieren (isActive der Mitgliedschaft) und das Workload-Limit (maxWorkload) setzen. Erlaubt ist das mit agents.manageGroups oder als TeamLead der Gruppe. Gruppen-CRUD, Mitglieder hinzufügen/entfernen und die TeamLead-Vergabe bleiben ausschließlich bei agents.manageGroups.

Agent-Kapazität (maxWorkload)

Jeder Agent kann ein optionales Kapazitätslimit (maxWorkload, 1–999) haben; ohne eigenes Limit gilt die globale Einstellung. Agents, deren Workload das Limit erreicht hat, werden bei der automatischen Zuweisung übergangen. Findet die Zuweisung deshalb keinen Agent, erhalten die TeamLeads der Gruppe eine ASSIGNMENT_CAPACITY_REACHED-Benachrichtigung. Ein Dashboard-Widget zeigt den aktuellen Workload-Status.

// Agent capacity configuration
interface Agent {
  workload: number;        // Current assigned items
  maxWorkload?: number;    // Limit (1-999, null = global setting)
}

Assignment-Strategien

Vor jeder Strategie werden abwesende und inaktive Agents, pausierte Mitgliedschaften und Agents am Kapazitätslimit aussortiert. Bleibt niemand übrig, bleibt das Item der Gruppe zugeordnet, aber unzugewiesen.

StrategieAuswahl
FIRST_AVAILABLEder erste verfügbare Agent der Gruppe
ROUND_ROBINreihum: der nächste verfügbare Agent nach dem zuletzt zugewiesenen
LEAST_LOADEDder Agent mit dem geringsten aktuellen Workload
SKILL_BASEDder Agent mit der besten Specialty-Übereinstimmung zur Kategorie (Proficiency, dann Workload); ohne Kategorie oder Treffer wie LEAST_LOADED

Abwesenheiten

Zeitzonen-bewusste Abwesenheiten

Eine genehmigte Abwesenheit gilt entweder ganztägig (allDay) oder stundenweise (startTime/endTime). Stundenweise Abwesenheiten werden in der eingestellten Zeitzone ausgewertet (Standard Europe/Berlin).

BEISPIELE:

Szenario 1: All-Day Absence
Absence: { startDate: "2026-02-10", endDate: "2026-02-14", allDay: true }
Check at: 2026-02-12 14:30
Result: TRUE (absent all day)

Szenario 2: Partial-Day Absence
Absence: {
  startDate: "2026-01-30",
  endDate: "2026-01-30",
  allDay: false,
  startTime: "08:00",
  endTime: "12:00"
}
Check at: 2026-01-30 10:30 (Europe/Berlin)
Result: TRUE (currently within 08:00-12:00)

Check at: 2026-01-30 14:30 (Europe/Berlin)
Result: FALSE (after 12:00)

Szenario 3: Timezone Conversion
at = 2026-01-30 09:00:00 UTC
Timezone: Europe/Berlin (UTC+1)
Local time: 10:00 (CET)
startTime: "08:00", endTime: "12:00"
Result: TRUE (10:00 within 08:00-12:00)

Vertreter bei manueller Zuweisung

Wird ein Item einem abwesenden Agent zugewiesen, geht es an dessen Vertreter; Vertretungsketten werden über höchstens drei Stationen verfolgt, Zyklen erkannt. Mit ignoreSubstitution lässt sich die Umleitung umgehen; das wird auditiert (SUBSTITUTE_BYPASS). Wer das Item am Ende bekommt, ist geprüft verfügbar: Hat der abwesende Agent keinen Vertreter, oder endet die Kette auf einer Person, die selbst abwesend ist, bleibt das Item beim abwesenden Original — im zweiten Fall auditiert als SUBSTITUTE_UNAVAILABLE. So liegt der Vorgang sichtbar dort, statt scheinbar in Arbeit zu sein.

EntraID/Azure AD Integration

Group-to-Role-Mapping

// Link role with EntraID group
PATCH /api/roles/:id
{
  "entraIDRoleId": "azure-group-uuid"
}

// EntraID Sync Process (per base-group member):
// 1. Fetch the user's EntraID groups
// 2. Match groups to ACTIVE roles via entraIDRoleId
//    (unique: one group maps to at most one role)
// 3. Exactly one match -> that role
// 4. Several matches -> lowest priority number wins (no conflict):
//    - ADMIN: 1000
//    - AGENT: 2000
//    - DATA_PROTECTION_OFFICER: 2500
//    - APPROVER: 3000
//    - Custom roles: ~50000
//    - END_USER: 99000
// 5. No match -> entraIDConflict = true:
//    existing users keep their role as long as it is usable
//    (an active role with a valid permission matrix),
//    otherwise they get END_USER; new users get END_USER

Vorrang bei mehreren Gruppen

Role Priority Ergebnis bei mehreren Treffern
ADMIN1000Gewinnt immer (höchste Priorität)
AGENT2000Zweite Priorität
DATA_PROTECTION_OFFICER2500Datenschutz (DSB)
APPROVER3000Genehmigungs-Rolle
Custom Roles~50000Mittlere Priorität
END_USER99000Niedrigste Priorität

Konflikt-Konten (entraIDConflict = true): Nur bei ihnen ist die Rollenauswahl in der User-Verwaltung bedienbar, sonst verwaltet Entra die Rolle — ein Rollenwechsel per API an einem synchronisierten Konto ohne Konflikt wird mit 403 ROLE_MANAGED_BY_ENTRA_ID abgewiesen. Eine manuell gesetzte Rolle bleibt beim Sync erhalten, solange das Konto in keiner zugeordneten Gruppe steht und die Rolle nutzbar ist (aktiv und mit gültiger Rechte-Matrix); andernfalls setzt der Sync END_USER, damit das Konto arbeitsfähig bleibt. Kommt das Konto in eine zugeordnete Gruppe, setzt der nächste Sync die Rolle aus der Zuordnung.

Sync und Kontostatus

  • Der Sync läuft als eingebauter täglicher Job („Entra ID User Sync", 01:00 UTC) und lässt sich zusätzlich jederzeit von Hand anstoßen (POST /api/entra-id/sync, Recht settings.editIntegrations); ohne aktive Integration tut er nichts. Ein Anstoß während eines laufenden Laufs startet keinen zweiten: Die Antwort ist 409 ENTRA_ID_SYNC_ALREADY_RUNNING und nennt den laufenden Lauf samt Fortschritt.
  • Nur der Sync legt Konten aus der Basis-Gruppe an. Der Microsoft-Login legt keine Konten an und fragt keine Gruppen ab.
  • Wer die Basis-Gruppe verlässt, wird beim nächsten Sync gesperrt. Jede Sperre trägt ihre Herkunft: Verwaltung, Entra-Sync oder System (lockSource: ADMIN, ENTRA_SYNC, SYSTEM). Die Herkunft steht am Konto und ist in der Benutzer-Verwaltung sichtbar — sie erklärt, warum ein vom Sync gesperrtes Konto nach einer Reaktivierung beim nächsten Lauf wieder gesperrt wird, solange es nicht in der Basis-Gruppe steht.
  • Kehrt ein Konto in die Basis-Gruppe zurück, hebt der Sync nur seine eigene Sperre auf. Sperren der Verwaltung oder des Systems sowie archivierte Konten bleiben bestehen; der Sync vermerkt das einmal je Sperre im Audit (ENTRAID / USER_LOCK_KEPT). So kann ein Sync keine bewusste Sperre der Verwaltung aufheben.
  • Schutz vor Massensperren: Ist die Basis-Gruppe leer, sperrt der Sync niemanden. Würde ein Lauf mehr als 20 % der aktiven synchronisierten Konten UND mehr als 50 Konten sperren, bricht er vor der ersten Sperre ab.
  • In Entra deaktivierte Konten sperrt der Sync ebenfalls, mit der Herkunft Entra-Sync. Lässt sich der Kontostatus aus Entra nicht lesen, bleibt er unverändert und der Lauf hält das in seinem Ergebnis fest — ein fehlendes Leserecht sperrt also niemanden versehentlich.
  • Übernimmt der Sync ein Konto nicht vollständig, nennt er den Grund als Code: NO_ROLE_GROUP (das Konto steht in keiner zugeordneten Rollen-Gruppe — ein bestehendes Konto behält seine nutzbare Rolle, sonst END_USER), PROTECTED_ACCOUNT (ein geschütztes Konto, das der Sync nie verknüpft) oder NO_MAIL (das Verzeichnis liefert keine E-Mail-Adresse — ein solches Konto wird nicht angelegt). Jede Konflikt-Zeile nennt die Verzeichnis-ID, den Anzeigenamen und, sofern vorhanden, die E-Mail-Adresse; so lässt sich das Konto im Verzeichnis auch ohne Postfach finden.
  • Ändert der Sync die Rolle eines Kontos, wird der Inhaber darüber benachrichtigt — auf demselben Weg wie bei einem Rollenwechsel durch die Verwaltung, und ohne Doppel-Benachrichtigung, wenn beide Wege denselben Wechsel betreffen. Der Zeitpunkt des letzten Rollenwechsels steht am Konto (roleChangedAt).
  • Steht ein per E-Mail angelegter Kontakt in der Basis-Gruppe, macht der Sync einen vollwertigen Benutzer daraus: Die Kennzeichen emailOnlyContact und autoCreatedFromEmail fallen, das Konto erscheint unter „Benutzer" statt unter „E-Mail-Kontakte" und nimmt an internen Benachrichtigungen teil. Die Herkunft (source) bleibt unverändert — sie beschreibt, wie das Konto entstanden ist.
  • Synchronisierte Konten melden sich nur über Microsoft an: Beim Verknüpfen entfernt der Sync ein lokales Passwort samt lokaler Zwei-Faktor-Anmeldung und beendet offene Sitzungen. Ein Passwort-Login antwortet danach 401 ACCOUNT_REQUIRES_PASSWORD_SETUP, ein Passwortwechsel 400 ACCOUNT_NO_PASSWORD.
  • Jede Statusänderung wirkt sofort — egal ob sie aus der Verwaltung oder aus dem Sync kommt: Mit der Sperre enden alle Zugänge des Kontos (laufende Sitzungen, Refresh-Token, Push-Abos, offene Echtzeit-Verbindungen), und der Rechte-Cache wird geleert.

Wie gesperrte und archivierte Konten bei der Anmeldung abgewiesen werden: Authentication.

Deployment & Configuration

Environment Variables

# MFA / Two-Factor
TWO_FACTOR_ENCRYPTION_KEY=...         # AES key for TOTP secret encryption (required for MFA)

# Invitations
INVITATION_TOKEN_TTL_HOURS=24         # Validity of invitation links (hours)

Initial Setup

# 1. System roles + DSB role automatically created on first start (seed)
# System: role-system-enduser, role-system-agent, role-system-admin, role-system-approver
# Custom default: role-custom-dsb (DATA_PROTECTION_OFFICER)

# 2. Create first admin user
POST /api/users
{
  "email": "admin@company.com",
  "name": "System Admin",
  "roleId": "role-system-admin",
  "password": "secure-initial-password"
}

# 3. Or invite users via email (omit the password -> invitation flow)
POST /api/users
{ "email": "user@company.com", "name": "New User", "roleId": "role-system-enduser" }
# -> Invitation email sent, user sets password via link
POST /api/users/:id/resend-invitation
# -> Sends the invitation again

# 4. Create agent groups
POST /api/agents/groups
{
  "name": "IT Support Level 1",
  "applicableEntityTypes": ["TICKET"],
  "assignmentStrategy": "ROUND_ROBIN"
}

# 5. Add agents to groups (userId is the canonical id)
POST /api/agents/groups/:groupId/members/:userId

# ... and promote a member to team lead
PATCH /api/agents/groups/:groupId/members/:userId
{
  "isTeamLead": true
}

# 6. Configure agent capacity (optional)
PATCH /api/agents/:userId
{
  "maxWorkload": 25
}

Best Practices

  1. Role-Design: Starte mit den 5 vorinstallierten Rollen (inkl. DSB), erstelle weitere Custom-Roles nur bei Bedarf. Rollen sind dynamisch und können jederzeit angepasst werden.
  2. Permission-Granularität: Nutze viewOwn für END_USER, viewAll für AGENT, editAll für ADMIN
  3. Agent Groups Setup: Mindestens 2 Groups (Level 1 + Level 2), GENERAL-Queue für Fallback. User werden direkt via Agent-Profil zugewiesen.
  4. Manager vs TeamLead: Manager für volle Gruppen-Verwaltung; TeamLead: Read-Only Absences + Notifications, plus Skills/Pause/Workload der eigenen Gruppenmitglieder
  5. Agent-Kapazität: Setze maxWorkload pro Agent für faire Verteilung, überwache via Dashboard-Widget
  6. Assignment-Strategie: LEAST_LOADED für faire Verteilung (respektiert maxWorkload), ROUND_ROBIN für eine vorhersehbare Reihenfolge
  7. Einladungs-E-Mails: Nutze den Invitation-Flow für neue User statt manuellem Passwort-Setup
  8. MFA aktivieren: Empfohlen für alle Admin- und Agent-Accounts, TWO_FACTOR_ENCRYPTION_KEY muss gesetzt sein
  9. Absence-Planning: Plane Absences 1-2 Wochen im Voraus, setze immer Substitute für >3 Tage
  10. Follower nutzen: Stakeholder können Tickets folgen statt als CC hinzugefügt zu werden - FOLLOWER wird nie heruntergestuft
  11. Sprach-Einstellungen: User können aus 5 Sprachen wählen (DE, EN, ES, FR, IT), Aktivitäten erscheinen in der gewählten Sprache
  12. E-Mail-Only Users: emailOnlyContact = true für externe Customers ohne Portal-Zugriff
  13. Permission-Cache: Der Rechte-Cache wird bei Rollen-Änderungen automatisch geleert; kritische Aktionen nach einer Rollen-Änderung testen

Verwandte Dokumentation