Zum Hauptinhalt springen

Lifecycle Webhooks

Lifecycle Events werden von mittwald genutzt, um die externe Anwendung über sie betreffende Ereignisse zu informieren.

Templating von Webhook URLs

Um die URL für die Webhooks zu konfigurieren, können Platzhalter verwendet werden. Diese werden durch die Werte der Attribute des Webhook-Payloads ersetzt.

Folgende Platzhalter können verwendet werden:

  • apiVersion: Die API-Version, die für den Webhook verwendet wird. Derzeit immer v1. Siehe auch API Versioning.
  • contributorId: ID des Contributors
  • extensionId: ID der Extension
  • extensionInstanceId: ID der Extension Instance
  • contextId: ID des Extension Contexts, bspw. die ProjectID
  • context: Der Extension Context Kind, zu dem die Extension Instance hinzugefügt wurde. Derzeit entweder customer oder project.

Webhook Payloads

ExtensionAddedToContext

  • apiVersionstring
    required
  • kind"ExtensionAddedToContext"
    required
  • idstring (uuid)
    required
  • contextobject
    required
    • idstring (uuid)
      required
    • kindstring (one of: customer, project)
      required
  • consentedScopesarray of string
    required
    • Array[
    • *string
    • ]
  • stateobject
    required
    • enabledboolean
  • metaobject
    required
    • extensionIdstring (uuid)
      required
    • contributorIdstring (uuid)
      required
  • secretstring
    required
  • requestobject
    required
    • idstring (uuid)
      required
    • createdAtstring (date-time)
      required
    • targetobject
      required
      • methodstring
        required
      • urlstring (uri)
        required
  • ExtensionInstanceUpdated

    • apiVersionstring
      required
    • kind"ExtensionInstanceUpdated"
      required
    • idstring (uuid)
      required
    • contextobject
      required
      • idstring (uuid)
        required
      • kindstring (one of: customer, project)
        required
    • consentedScopesarray of string
      required
      • Array[
      • *string
      • ]
  • stateobject
    required
    • enabledboolean
  • metaobject
    required
    • extensionIdstring (uuid)
      required
    • contributorIdstring (uuid)
      required
  • requestobject
    required
    • idstring (uuid)
      required
    • createdAtstring (date-time)
      required
    • targetobject
      required
      • methodstring
        required
      • urlstring (uri)
        required
  • ExtensionInstanceSecretRotated

    • apiVersionstring
      required
    • kind"ExtensionInstanceSecretRotated"
      required
    • idstring (uuid)
      required
    • contextobject
      required
      • idstring (uuid)
        required
      • kindstring (one of: customer, project)
        required
    • metaobject
      required
      • extensionIdstring (uuid)
        required
      • contributorIdstring (uuid)
        required
    • secretstring
      required
    • requestobject
      required
      • idstring (uuid)
        required
      • createdAtstring (date-time)
        required
      • targetobject
        required
        • methodstring
          required
        • urlstring (uri)
          required

    ExtensionInstanceRemovedFromContext

    • apiVersionstring
      required
    • kind"ExtensionInstanceUpdated"
      required
    • idstring (uuid)
      required
    • contextobject
      required
      • idstring (uuid)
        required
      • kindstring (one of: customer, project)
        required
    • consentedScopesarray of string
      required
      • Array[
      • *string
      • ]
  • stateobject
    required
    • enabledboolean
  • metaobject
    required
    • extensionIdstring (uuid)
      required
    • contributorIdstring (uuid)
      required
  • requestobject
    required
    • idstring (uuid)
      required
    • createdAtstring (date-time)
      required
    • targetobject
      required
      • methodstring
        required
      • urlstring (uri)
        required
  • Erläuterung der einzelnen Attribute

    apiVersion

    Die API-Version, die für den Webhook verwendet wird. Derzeit immer v1. Ermöglicht zukünftige Breaking Changes an dem Payload. Siehe auch API Versioning.

    kind

    Art des Lifecycle-Webhooks. Mögliche Werte sind:

    • ExtensionAddedToContext
    • ExtensionInstanceUpdated
    • ExtensionInstanceSecretRotated
    • ExtensionInstanceRemovedFromContext

    Je nach Anforderungen kann dadurch die URL für alle Webhooks identisch sein und die Art des Webhooks anhand des kind-Attributs unterschieden werden oder alternativ für jeden Lifecycle ein eigener Webhook-Endpoint konfiguriert und der kind ignoriert werden. Für weitere Erläuterungen zu der semantischen Bedeutung der einzelnen Lifecycle-Webhooks siehe Lifecycle Webhooks Konzept.

    id

    Der global eindeutige Identifier der Extension Instance. Dies ist nicht zu verwechseln mit der Extension ID, die die Extension als Ganzes identifiziert. Auch darf diese ID nicht mit der request.id verwechselt werden, die den Request identifiziert und dafür genutzt werden kann, um Replay-Attacken zu verhindern. Siehe dazu auch Request Meta Informationen.

    context

    Informationen über den Extension Context, zu dem die Extension Instance gehört. Derzeit sind zwei Arten von Extension Contexts möglich:

    • customer
    • project

    Zusätzlich zu dem context.kind wird auch die context.id übermittelt. Dies ist die ID des Extension Contexts, also bspw. des Projektes.

    consentedScopes

    Die Berechtigungen, denen der Nutzer der Extension beim Hinzufügen zu einem Extension Context zugestimmt hat. Wenn einer Extension nachträglich zusätzliche Scopes hinzugefügt wurden, muss ein Nutzer diesen erst noch zustimmen, bevor die Extension diese für die Extension Instance verwenden kann.

    Anhand der consentedScopes kann die Extension prüfen, ob die benötigten Berechtigungen vorhanden sind. Deswegen wird empfohlen, die consentedScopes zu persistieren.

    state

    Der aktuelle Zustand der Extension Instance. Über die Lifecycle-Webhooks wird derzeit lediglich über den Zustand von enabled informiert. Darüber kann die Extension in Erfahrung bringen, ob die Extension Instance aktiv oder inaktiv ist und somit, ob sie ihre Funktionalität bereitstellen kann. Für eine deaktivierte Extension Instance funktionieren bspw. die Authentifizierungs- und Autorisierungsmechanismen nicht. Somit würden auch Domain Actions für eine deaktivierte Extension Instance nicht akzeptiert werden.

    meta

    Meta-Informationen über die Extension (Instance). Diese beinhalten die IDs der Extension und des Contributors.

    Diese Informationen sollten bspw. verwendet werden, um sicherzustellen, dass der Webhook nicht nur ursprünglich von mittwald stammt, sondern auch für die eigene Extension bestimmt ist. Dies beugt Forward-Replay-Attacken vor. Dies ist nicht zu verwechseln mit den Request Meta Informationen, die Aufschluss über den Webhook an sich geben.

    secret

    Das Secret, das für die Authentifizierung der Extension Instance verwendet wird. Dieses kann verwendet werden, um nutzerunabhängige Authentifizierung zu implementieren. Für weitere Informationen siehe Authentifizierung von Extension Instances.

    request

    Meta-Informationen über den Lifecycle-Webhook-Request.

    Angriffsvektoren, wie Replay- oder Forward Replay Attacks, können ausgeschlossen werden, indem die request-Property validiert wird.

    Um denselben Request nur einmalig zu akzeptieren, kann die request.id persistiert und abgeglichen werden. Das mStudio schickt niemals Requests mit derselben request.id mehrfach.

    Um zu überprüfen, ob der Request den richtigen Empfänger erreicht, kann das Feld request.target.url überprüft werden. Dadurch kann verhindert werden, dass ein Angreifer den signierten Request an eine andere Extension weiterleitet.

    Um veraltete Requests erkennen zu können, bietet das Feld request.createdAt einen Anhaltspunkt. Es sollte jedoch beachtet werden, dass das mStudio die Webhooks potenziell asynchron versendet und somit die Zeitdifferenz zwischen dem Erstellen des Requests und dem Empfangen des Webhooks variieren kann.

    Validierung von Lifecycle Webhooks

    Um sicherzustellen, dass der Webhook von mittwald ausging, bieten wir die Möglichkeit, die Identität des Senders und den Inhalt des Request Bodys zu verifizieren. Dazu signieren wir den versendeten Request Body und schicken die Signatur inklusive der Meta-Informationen darüber in Request-Headern mit.

    X-Marketplace-Signature-Serial: 7f640dcf-c5fb-4e79-bc4b-99a30e50fcc5
    X-Marketplace-Signature-Algorithm: Ed25519
    X-Marketplace-Signature: HKdS7xD...qQMN94HINte7Dlof+9V/PQ6gW0C5uBOz+9F6YNOLE6vdb2ybVwRH23GAg==

    X-Marketplace-Signature-Serial

    Eindeutige ID des zur Überprüfung der Signatur zu nutzenden Public Keys. Wir garantieren für eine Public Key ID einen stabilen Public Key. Dadurch kann der Public Key gecached werden. Ist eine ID unbekannt, kann der Key mit der Operation GET/v2/webhook-public-keys/{serial}/ über die mittwald-API bezogen werden.

    X-Marketplace-Signature-Algorithm

    Algorithmus, der zum Erzeugen der Signatur verwendet wurde, derzeit immer Ed25519.

    X-Marketplace-Signature

    DSA-Signatur in Base64 codiert.

    Um die Signatur zu prüfen, kann eine übliche kryptographische Library in der gewählten Programmiersprache verwendet werden. Dazu wird mithilfe der verify-Methode der gesamte, unverarbeitete Request Body geprüft. Damit ist sichergestellt, dass der übermittelte Request Body unmodifiziert von mittwald übertragen wurde.

    bodyBytes, err := io.ReadAll(body)
    if err != nil {
    return err
    }

    if !ed25519.Verify(publicKey, bodyBytes, signature) {
    panic("invalid signature")
    }

    Referenzimplementierungen für die Validierung von Lifecycle Webhooks

    TODO

    Dry Running Webhooks

    Um die Entwicklung einer Extension zu erleichtern, kann ein Entwickler einer Extension einen Webhook im Dry Run Modus aufrufen lassen. Bei einer Dry Run webhook execution wird ein Webhook-Kind definiert und anschließend der entprechende Webhook der Extension aufgerufen. Der Webhook-Endpoint wird hierbei mit Demo-Werten aufgerufen.
    Nach dem Dry Run wird das Ergebnis der webhook execution in der APIResponse zurück gegeben. Inklusive response-body, response-headers und allgemeinen Fehlern.
    Um die Dry Run webhook calls zu identifizieren wird in den Query-Parametern der Wert dry-run=true mitgeliefert. Ist der dry-run Query-Parameter auf true gesetzt, sollte das Extension-Backend diesen Call auch als solchen behandeln und daher den Aufruf als reinen Test interpretieren. Zudem wird vom mStudio auch der Query-Parameter executing-user-id gesetzt.

    Die extensionInstanceId hat bei einem Dry Run keinerlei Relevanz für echte Entitäten im mStudio.

    Nur Benutzer, die zu einem Contributor gehören dürfen Webhooks als Dry Run ausführen.

    Demo-Werte

    Die Demo-Werte können als Testdaten genutzt werden, um die interne Logik der Extension zu testen, indem man das folgende JSON-Objekt im Body mit sendet. Sind diese Werte nicht gesetzt, werden sie aus der Extension abgeleitet oder zufällig generiert.

    • contextIdstring
    • scopesarray of string
      • Array[
      • *string
      • ]
  • instandeDisabledboolean
  • createdAtstring (date-time)
  • secretstring