Self-Service-Onboarding (Architektur)¶
Das Self-Service-Onboarding erlaubt Interessenten, sich ohne Vertriebskontakt selbst eine DiKAS-Cloud-Instanz einzurichten – wahlweise als kostenlosen Test (Trial) oder direkt als zahlende Live-Instanz. Diese Seite beschreibt die technische Architektur, die Konfiguration und die Sicherheitsmechanismen. Die Endanwender-Sicht ist unter Online-Registrierung beschrieben.
Standardmäßig deaktiviert (Dark-Rollout)
Der öffentliche Funnel ist über das Flag SelfServiceSignupEnabled abgesichert und im
Auslieferungszustand aus. Solange das Flag false ist, liefern alle öffentlichen
Endpoints 404. Vor einer Aktivierung sind die Punkte unter
Vor der Aktivierung zu erfüllen.
Überblick¶
Der Ablauf besteht aus einem öffentlichen Frontend-Wizard und einer Reihe anonymer Backend-Endpoints.
Die eigentliche Provisionierung läuft über denselben ILicenseProvisioningService, den auch der
Portal- und der Trial-Pfad nutzen.
| Bereich | Ort |
|---|---|
| Frontend-Wizard | Route /signup (PublicOnboardingWizardComponent, App dikas-web) |
| Magic-Link-Bestätigung | Route /signup/verify |
| Backend-Endpoints | /api/v1/onboarding/public/* und /api/v1/trial/start |
| Feature-Pack | Dikas.Features.Licensing |
Der Wizard führt durch sieben Schritte: Paket → TSE (Cloud vs. Swissbit) → Account (E-Mail, passwortlos per Magic-Link) → Firmendaten → Online-Name (Subdomain) → Recht & Zahlung → Fertig (Instanz-URL und Zugangsdaten).
Feature-Flag SelfServiceSignupEnabled¶
Das Flag liegt als Eigenschaft auf dem Singleton-Dokument OperationalConfig
(feste ID operationalconfig) und ist nur über die Datenbank bzw. die Admin-Oberfläche
änderbar – es gibt keine Umgebungsvariable dafür.
Jeder öffentliche Endpoint ruft zuerst CheckSelfServiceEnabledAsync() auf und antwortet mit
404 Not Found, solange das Flag false ist. Der Funnel bleibt damit unsichtbar, bis er
bewusst freigeschaltet wird.
Öffentliche Endpoints¶
Alle Endpoints sind [AllowAnonymous] und durch das Flag sowie die Anti-Missbrauchs-Maßnahmen
abgesichert.
| Methode & Pfad | Zweck |
|---|---|
POST /api/v1/onboarding/public/orders |
Anonyme Draft-Bestellung anlegen (liefert geheimes Order-Token) |
PUT /api/v1/onboarding/public/orders/{id} |
Draft aktualisieren (Token-gebunden) |
POST /api/v1/onboarding/public/orders/{id}/reserve-name |
Online-Namen kurzzeitig reservieren |
POST /api/v1/onboarding/public/register |
Passwortlosen Kunden anlegen, Magic-Link versenden |
POST /api/v1/onboarding/public/orders/{id}/bind |
Order an den per Magic-Link verifizierten Account binden ([Authorize]) |
POST /api/v1/onboarding/public/payment/set |
Stripe-SetupIntent/PaymentIntent erzeugen (liefert ClientSecret) |
POST /api/v1/onboarding/public/payment/webhook |
Stripe-Webhook (Zahlungsbestätigung) |
POST /api/v1/onboarding/public/orders/{id}/submit |
Instanz provisionieren |
GET /api/v1/onboarding/public/payment-config |
Stripe-Publishable-Key fürs Frontend |
POST /api/v1/trial/start |
Direkter Self-Service-Trial (ohne Order/Portal-Account) |
Sicherheit / Anti-Missbrauch¶
Da jede Provisionierung echte Kosten (COGS) erzeugt, sind mehrere voneinander unabhängige Schutzschichten aktiv:
- Honeypot – ein verstecktes Formularfeld; ist es ausgefüllt, wird mit
202 Acceptedstill abgelehnt (kein Provisioning). - Fehler-Limiter pro IP – exponentielles Delay, Sperre nach 10 Fehlern (1 h) bzw. 20 Fehlern (24 h).
- Provisionierungs-Kontingent pro IP und 24 h (
MaxPublicProvisionsPerIpPer24h, Default 3). Es greift sowohl am öffentlichen Submit als auch am Trial-Start und wird bei Erfolg nicht zurückgesetzt. Wird das Kontingent überschritten, antwortet der Endpoint mit429(code: SIGNUP_RATE_LIMITED). - E-Mail-Versand-Kontingent pro IP und Stunde (
MaxPublicEmailSendsPerIpPerHour, Default 5) gegen Magic-Link-Bombing. - Magic-Link statt klassischer E-Mail-Bestätigung; der Klick verifiziert die Adresse und liefert ein Portal-JWT, mit dem die Order gebunden wird.
- Echte Client-IP – hinter dem Ingress wird die Quell-IP über
ForwardedHeadersermittelt. Die vertrauenswürdigen Netze (KnownNetworks) müssen im Deployment auf die Cluster-CIDR gesetzt sein, sonst kollabiert die IP-Quote.
In-Memory-Kontingente
Die Kontingente werden im Arbeitsspeicher gehalten (rollendes Zeitfenster). Bei mehreren Instanzen hinter einem Load-Balancer zählt jede Instanz für sich; in dem Fall ist eine Sticky-Session oder ein gemeinsamer Cache vorzusehen.
Konfiguration¶
Die Optionen werden aus der Sektion Onboarding geladen (OnboardingOptions). Alle Werte haben
sinnvolle Defaults im Code; Überschreiben per Umgebungsvariable mit doppeltem Unterstrich.
| Schlüssel | Default | Bedeutung |
|---|---|---|
Onboarding__MaxPublicProvisionsPerIpPer24h |
3 |
Max. Provisionierungen pro IP / 24 h (Submit + Trial) |
Onboarding__MaxPublicEmailSendsPerIpPerHour |
5 |
Max. Magic-Link-Mails pro IP / Stunde |
Onboarding__RequireEmailVerifyBeforeProvision |
true (Portal) |
E-Mail-Bestätigung vor Provisionierung erzwingen |
Onboarding__RequireOnlinePaymentForNonTrial |
false (Portal) |
Bei Nicht-Trial echte Online-Zahlung erzwingen |
Zahlung¶
Die Zahlung läuft über Stripe auf einem zentralen DiKAS-Konto (nicht pro Mandant).
| Schlüssel | Bedeutung |
|---|---|
Signup__Stripe__SecretKey |
Stripe-Secret-Key (aktiviert den echten Zahlungs-Adapter) |
Signup__Stripe__PublishableKey |
Publishable-Key fürs Frontend |
Signup__Stripe__WebhookSecret |
Signatur-Geheimnis für den Webhook |
Ohne gesetzten SecretKey ist ein Null-Adapter aktiv (Dev/Test). Die OnboardingPaymentMethod-Werte:
SepaLastschrift = 1, StripeSetupIntent = 2 (Trial-first, Zahlart hinterlegen),
StripePaymentIntent = 3 (Pay-first, sofortige Zahlung).
Lebenszyklus & Aufräumen¶
Zwei Hintergrund-Dienste halten den Funnel sauber:
Abgebrochene Bestellungen¶
OnboardingCleanupService (täglich) entfernt abgebrochene/unbezahlte Bestellungen, die länger als
14 Tage (CleanupAbandonedOnboardingsCommand, konfigurierbar) inaktiv sind – einschließlich
verwaister anonymer Public-Drafts (ohne gebundenen Kunden) – und gibt deren
Online-Namen-Reservierung wieder frei. Provisionierte/bezahlte Bestellungen bleiben unberührt.
Abgelaufene Trials (Dry-Run, dark)¶
TrialDeprovisioningService ist ein bewusst zurückhaltend gebauter Dienst:
- Er findet abgelaufene Trials, deren Kulanzfrist (
GraceDays, Default 30) nach dem Trial-Ende verstrichen ist (IsTrial = trueundEndDatejenseits der Frist; konvertierte/bezahlte Lizenzen mitIsTrial = falsewerden nie erfasst). - Im DRY-RUN protokolliert er lediglich, welche Tenant-Datenbanken
(
{DatabasePrefix}_maindb,{DatabasePrefix}_gastrocurrent) ein späterer Live-Lauf löschen würde – er löscht nichts. - Er ist standardmäßig vollständig deaktiviert und läuft nur bei
TrialDeprovision__Enabled=true.
| Schlüssel | Default | Bedeutung |
|---|---|---|
TrialDeprovision__Enabled |
false |
Master-Schalter; false = Dienst läuft gar nicht |
TrialDeprovision__GraceDays |
30 |
Kulanzfrist in Tagen nach Trial-Ende |
Echter DB-Drop ist nicht implementiert
Das tatsächliche Löschen der Mandanten-Datenbanken ist absichtlich nicht verdrahtet. Es erfordert eine ausdrückliche Freigabe sowie ein verifiziertes Datenbank-Namens-Mapping und sollte mit dem Ablauf-Enforcement (Trial-Ende → Sperre → Export) koordiniert werden.
Audit¶
Jede Provisionierung wird über den IAuditLogger protokolliert und ist in der
Operator-Audit-Ansicht sichtbar (GET /api/v1/audit/logs/{date}, nur Rolle Admin):
| Ereignis | Auslöser |
|---|---|
TRIAL_PROVISIONED |
Erfolgreicher Trial-Start (mit Online-Name, Lizenz-ID, Laufzeit) |
TRIAL_CONVERTED_PAID |
Konversion eines Trials zu einer bezahlten Lizenz |
Zeitstempel und Client-IP stammen aus dem Request; bei anonymen Trials steht als Benutzer -.
Zugangsdaten werden nicht ins Audit geschrieben.
Vor der Aktivierung¶
Bevor SelfServiceSignupEnabled jemals auf true gesetzt wird:
-
ForwardedHeaders:KnownNetworksim Prod-Deploy auf die echte Cluster-CIDR gesetzt (sonst IP-Quote wirkungslos). - Stripe-Keys (
Signup__Stripe__*) gesetzt – sonst wäre eine „sofort live"-Zahlung mit echtem Provider kostenlos. - Magic-Link-Versand und
/signup/verifydurchgängig verdrahtet. - Flag in der
OperationalConfigaktiviert.