Skip to content
← Alle Beiträge
· Delta1 Labs LizenzierungBerechtigungenLeitfaden

Eine Lizenz, viele Uhren: Ablauf pro Berechtigung mit Keyright

Eine Lizenz hat üblicherweise ein einziges Ablaufdatum, echte Verträge aber selten: Ein Basisplan verlängert sich jährlich, während ein Test-Add-on 30 Tage läuft und eine Aktionsfunktion mit dem Quartal endet. Ein einziges Datum in die gesamte Lizenz einzubacken zwingt Sie entweder dazu, zu viel zu gewähren oder ständig neu auszustellen. So geben Sie jeder Berechtigung in einer einzigen signierten Keyright-Lizenz ihren eigenen Ablauf — die Form der Nutzdaten, das SDK, das eine Uhr pro Funktion offline liest, die Durchsetzung an der Fähigkeitsgrenze und die Kulanzregeln, die verhindern, dass ein abgelaufenes Add-on die ganze App lahmlegt.

Die meisten Lizenzierungs-Stacks geben einer Lizenz genau eine Uhr: ein expiresAt für den gesamten Schlüssel. Das modelliert den einfachsten Vertrag — ein Jahresabonnement, das sich entweder verlängert oder ausläuft — und sonst nichts. Echte kommerzielle Vereinbarungen sind unordentlicher. Ein Kunde hat einen jährlichen Basisplan, testet 30 Tage lang ein Premium-Add-on und hat zusätzlich eine Aktionsfunktion, die Sie ihm bis zum Quartalsende mitgegeben haben. Drei Fähigkeiten, drei verschiedene Enddaten, ein Kunde.

Mit einer Lizenz mit nur einer Uhr haben Sie zwei schlechte Optionen. Entweder stellen Sie mehrere Lizenzen aus — eine pro Enddatum — und Ihre App muss nun ein Bündel von Schlüsseln jonglieren und abgleichen, oder Sie wählen das späteste Datum für die gesamte Lizenz und gewähren den Test und die Aktion bis dahin stillschweigend zu lange. Keins von beidem ist ehrlich. Die Lösung besteht darin, Ablauf nicht mehr als Eigenschaft der Lizenz zu betrachten, sondern als Eigenschaft jeder einzelnen Berechtigung.

Eine Berechtigung kann ihre eigene Uhr tragen

Wenn Sie Berechtigungen und Feature-Flags, gesteuert von der Lizenz gelesen haben, wissen Sie, dass eine Berechtigung eine benannte Fähigkeit ist — ein boolesches Flag wie export oder ein numerisches Limit wie max-projects —, die aus einer Stufenvorlage in die signierte Lizenz eingebettet wird. Der Ablauf pro Berechtigung fügt ein Feld hinzu: Jede Berechtigung kann angeben, wann sie nicht mehr gilt.

Die Lizenz-Nutzdaten bleiben ein einziges signiertes Objekt; der Berechtigungsblock wird lediglich reicher:

{
  "sub": "acct_8f21c",
  "tier": "pro",
  "iat": "2026-10-12T00:00:00Z",
  "exp": "2027-10-12T00:00:00Z",
  "entitlements": {
    "export":        { "value": true, "exp": "2027-10-12T00:00:00Z" },
    "sso":           { "value": true },
    "premium-trial": { "value": true, "exp": "2026-11-11T00:00:00Z" },
    "promo-reports": { "value": true, "exp": "2026-12-31T23:59:59Z" },
    "max-projects":  { "value": 25,   "exp": "2027-10-12T00:00:00Z" }
  }
}

Drei Uhren sind nun sichtbar. Das oberste exp ist die Lebensdauer der Lizenz selbst — die harte äußere Grenze. premium-trial endet am 11. November, promo-reports an Silvester, und sso hat überhaupt kein exp, was bedeutet, dass es genau so lange lebt wie die Lizenz. Jede Berechtigung wird gegen ihr eigenes Datum bewertet, begrenzt durch das der Lizenz.

Lebensdauer der Lizenz (ein signiertes Objekt)Okt 2026Okt 2027sso — kein exp, überspannt die gesamte Lizenzpremium-trialendet in 30 Tagen → IsEnabled falsepromo-reportsendet am 31. Dez → IsEnabled false, Lizenz weiterhin gültigjetzt+30 TQuartalsende

Eine Uhr pro Funktion in Ihrer App lesen

Die SDK-Oberfläche ändert sich kaum, und genau das ist der Punkt — IsEnabled und GetLimit geben bereits die effektive Antwort zurück, und „ist die eigene Uhr dieser Berechtigung abgelaufen?” wird einfach in diese Antwort eingefaltet. Eine abgelaufene Berechtigung meldet sich als deaktiviert:

var client = KeyrightClient.Initialize(options);
await client.ActivateAsync(licenseKey);   // once; the lease is cached and verified

// premium-trial is true in the payload, but if its exp has passed,
// IsEnabled returns false — no special-casing at the call site.
if (client.IsEnabled("premium-trial"))
    ShowPremiumPanel();

// A numeric limit whose clock has run out falls back to your conservative value.
int maxProjects = client.GetLimit("max-projects", fallback: 1);

Da die gesamte Entscheidung lokal ist — das exp der Berechtigung wird mit der aktuellen Zeit verglichen, innerhalb von Nutzdaten, deren Signatur Sie bereits gegen den eingebetteten öffentlichen Schlüssel verifiziert haben —, gibt es keinen Netz-Rundlauf und nichts Neues, das fehlschlagen könnte. Die Bewertung, die IsEnabled durchführt, sieht ungefähr so aus:

internal bool IsEnabled(string key)
{
    if (!_entitlements.TryGetValue(key, out var e)) return false;   // unknown → closed
    if (e.Value is not true) return false;                          // flag is off
    if (e.Expiry is DateTimeOffset exp && _clock.UtcNow >= exp)
        return false;                                               // this feature's clock ran out
    return true;
}

Alles, worauf sich Ihr bestehender Code bereits verlassen hat, gilt weiterhin: Unbekannte Flags fallen in den gesperrten Zustand, die Signatur wird offline verifiziert, und das Manipulieren eines Zeitstempels macht die gesamte Lizenz ungültig. Die einzige Ergänzung ist der eine Vergleich _clock.UtcNow >= exp, beschränkt auf eine einzelne Berechtigung.

Wenn Sie das Datum selbst brauchen — um „Ihr Premium-Test endet in 6 Tagen” anzuzeigen —, fragen Sie direkt danach, statt in die Nutzdaten hineinzugreifen:

DateTimeOffset? ends = client.ExpiresAt("premium-trial");
if (ends is { } d && d - DateTimeOffset.UtcNow < TimeSpan.FromDays(7))
    ShowTrialEndingBanner(d);

ExpiresAt gibt null für eine Berechtigung ohne Uhr zurück (sie lebt so lange wie die Lizenz) und andernfalls das effektive Datum — niemals ein Datum nach dem eigenen exp der Lizenz, denn die Lizenz bleibt die äußere Grenze.

An der Fähigkeitsgrenze durchsetzen

Die Regel für Berechtigungen im Allgemeinen gilt unverändert: Einen Button auszublenden ist UX, keine Durchsetzung. Setzen Sie die Prüfung dorthin, wo die Fähigkeit tatsächlich lebt, und lassen Sie den Fall des abgelaufenen Tests aus demselben IsEnabled-Aufruf hervorgehen:

public Report BuildPromoReport(ReportOptions options)
{
    if (!_license.IsEnabled("promo-reports"))
        throw new FeatureNotLicensedException("promo-reports");

    return _renderer.Render(options);
}

Am 30. Dezember läuft das durch. Am 1. Januar wirft derselbe Code eine Ausnahme — nicht, weil Sie etwas ausgeliefert hätten, und nicht, weil die Lizenz ungültig geworden wäre, sondern weil die Uhr dieser einen Berechtigung abgelaufen ist. Der Kunde behält export, sso und alles andere auf der Lizenz bis zum eigenen exp der Lizenz.

Kulanz, Zeitabweichung und die vertrauenswürdige Uhr

Der Ablauf pro Funktion erbt dieselben zwei Gefahren, die jede zeitbasierte Prüfung hat, und Sie behandeln sie auf dieselbe Weise.

Zeitabweichung und Zurückstellen der Uhr. Eine Offline-Prüfung ist nur so ehrlich wie die Geräteuhr. Ein Benutzer, der seine Uhr einen Monat zurückstellt, erweckt einen abgelaufenen Test wieder zum Leben — es sei denn, Sie erkennen es. Die Uhrmanipulationserkennung von Keyright (behandelt in Manipulation der Lizenzuhr und Zurückstellen der Zeit) speichert eine monotone Hochwassermarke der letzten Zeit, die die App gesehen hat, und weigert sich, eine Wanduhr zu akzeptieren, die sich hinter diese zurückbewegt hat. Der Ablauf pro Berechtigung sitzt direkt darauf: Das _clock.UtcNow in der obigen Bewertung ist die validierte Zeit, nicht das rohe DateTimeOffset.UtcNow.

Kulanz, pro Fähigkeit entschieden. Wenn die Lizenz in ihr Kulanzfenster eintritt, lassen Sie die App üblicherweise ein paar Tage schreibgeschützt weiterlaufen (siehe Ausfall des Lizenzservers und Kulanzfenster). Eine einzelne ablaufende Berechtigung ist sanfter: Der Standard ist ein hartes Abschalten bei ihrem exp, denn der Rest der App ist nicht betroffen und der Kunde verliert nur das Add-on. Wenn Sie für eine bestimmte Funktion eine weiche Landung wollen — etwa ein paar Tage, in denen ein Test auf schreibgeschützt zurückfällt, bevor er verschwindet —, kodieren Sie das als Richtlinie dort, wo Sie das Flag lesen, nicht als zweites Ablaufdatum:

public bool CanEditWithPremium()
{
    if (_license.IsEnabled("premium-trial")) return true;

    // Optional soft landing: 3 read-only days after the trial clock lapses.
    var ended = _license.ExpiredAt("premium-trial");   // when it lapsed, or null
    return ended is { } t && DateTimeOffset.UtcNow - t < TimeSpan.FromDays(3)
        ? false   // view allowed elsewhere; editing is off
        : false;
}

Die Kulanz in Ihrem Code statt in den Nutzdaten zu halten bedeutet, dass die Lizenz eine ehrliche Aussage macht — diese Funktion endet an diesem Datum — und Ihr Produkt entscheidet, wie sanft es sie loslässt.

Warum das besser ist als das Jonglieren mit Schlüsseln

Der Ablauf pro Berechtigung beseitigt eine ganze Kategorie operativer Schmerzen:

  • Immer ein Schlüssel pro Kunde. Ein Test-Add-on, eine Aktion und ein Basisplan mit drei verschiedenen Enddaten werden weiterhin als eine einzige Lizenz ausgeliefert. Ihr Aktivierungsablauf, Ihre Speicherung und Ihr Support-Team haben es mit einem Schlüssel zu tun.
  • Aktionen und Tests laufen von selbst ab. Sie stellen die Lizenz einmal aus, mit dem Enddatum der Aktion auf genau dieser einen Berechtigung. Niemand muss daran denken, beim Quartalswechsel neu auszustellen oder zu widerrufen — die Uhr steckt bereits in den signierten Nutzdaten.
  • Upselling ohne Absturzkante. Wenn der Premium-Test ausläuft, fällt die App auf das Basiserlebnis zurück, für das der Kunde bezahlt hat, nicht auf einen Sperrbildschirm. Das ist der Moment für eine Upgrade-Aufforderung, und ExpiresAt liefert Ihnen das genaue Datum, um sie darum herum zu bauen.
  • Es bleibt offline und manipulationssicher. Jede Uhr reist in derselben signierten Vorlage mit und wird lokal verifiziert. Kein neuer Endpunkt, kein neuer Rundlauf, und jede Änderung an irgendeinem Datum bricht die Signatur.

Eine Lizenz war nie wirklich ein Vertrag mit einem Enddatum — das war nur das, was sich am einfachsten modellieren ließ. Geben Sie jeder Berechtigung ihre eigene Uhr, und die Lizenz entspricht endlich dem Geschäft, das Sie tatsächlich verkauft haben. Siehe den Keyright-Leitfaden zu Berechtigungen für das Fähigkeitsmodell, auf dem das aufbaut, und Manipulation der Lizenzuhr für die validierte Uhr, von der es abhängt.

Nebula.NET testen

Härten Sie Ihren .NET-Code in wenigen Minuten — starten Sie mit der kostenlosen Edition.