Authentifizierung & CORS

Zuletzt aktualisiert 27 May 2026

Der Endpunkt-Listener prüft eine eingehende Anfrage, mit grünen Haken für Aussteller, Signatur, Ablauf und Origin

Ein Endpunkt-Listener hat zwei Schutzmechanismen: eine JSON-Web-Token-Prüfung, mit der Sie jeden Aufruf ablehnen, der nicht mit Ihrem gemeinsamen Geheimnis signiert ist, und eine Cross-Origin-Prüfung, mit der ein Webbrowser den Endpunkt von Ihrer eigenen Website aus aufrufen kann (und nur von Ihrer eigenen Website). Beide werden in Einen Endpunkt erstellen kurz vorgestellt. Diese Seite ist die ausführliche Referenz für beide: das genaue Protokoll, dem das aufrufende System folgen muss, die Fehlerantworten, der Fallstrick, wenn Sie beide gleichzeitig aktivieren, und einsatzbereite Codebeispiele in neun Sprachen.

Die Reihenfolge der Abläufe

Wenn eine Anfrage /listener/{first_hash}/{second_hash} erreicht und der Listener eine der beiden Funktionen aktiviert hat, verarbeitet Flexie sie in dieser festen Reihenfolge bevor irgendein Workflow-Schritt läuft:

Anfrage trifft ein SCHRITT 1 JWT-Prüfung wenn add_authentication = on 403 / 401 Workflow läuft nicht SCHRITT 2 CORS-Prüfung wenn allow_cors = on OPTIONS-Preflight liefert 204 mit CORS-Headern 403 Workflow läuft nicht Der Workflow läuft

Die Reihenfolge ist für einen bestimmten Fall entscheidend (die Kombination beider), der am Ende dieser Seite behandelt wird.

JSON-Web-Token-Authentifizierung (JWT)

Ein JSON Web Token ist ein kleines Stück signierter Text, das das aufrufende System in einen Header legt, damit Flexie prüfen kann, dass der Aufruf echt ist. Wenn Sie dies aktivieren, wird jede Anfrage abgelehnt, die kein gültiges, nicht abgelaufenes, korrekt signiertes Token trägt, das unter Ihrem endpoint_key ausgestellt wurde.

Die drei Einstellungen am Listener

Einstellung Was es ist
add_authentication Ein/Aus-Schalter. Wenn aktiviert, läuft die JWT-Prüfung bei jeder Anfrage.
endpoint_key Der erwartete Aussteller, der Wert des iss-Claims im Token. Wird automatisch erzeugt, sobald Sie die Authentifizierung erstmals aktivieren; Sie können ihn ändern. Unterstützt Flexie Scripting, kann also ein zur Laufzeit aufgelöstes {{ ... }} sein.
endpoint_secret Das gemeinsame Geheimnis, mit dem das Token signiert und verifiziert wird. Wird automatisch erzeugt; Sie können es ändern. Unterstützt Flexie Scripting. Zum Rotieren ändern Sie den Wert und aktualisieren jedes aufrufende System.

Sowohl endpoint_key als auch endpoint_secret werden zum Zeitpunkt der Verifizierung mit Flexie Scripting aufgelöst. So können Sie sie aus der Konfiguration ziehen, falls Sie sie nicht direkt im Listener-Formular hinterlegen möchten.

Unterstützte Signaturalgorithmen

Erlaubt Familie Hinweise
HS256 HMAC-SHA-256 Die Standardwahl
HS384 HMAC-SHA-384
HS512 HMAC-SHA-512 Größte Signatur

Nicht unterstützt: RS256 / RS384 / RS512 (RSA), ES256 / ES384 / ES512 (ECDSA), PS256 / PS384 / PS512 oder none. Tokens, die mit einem dieser Verfahren signiert sind, bestehen die Verifizierung nicht.

Nur HMAC, weil die Verifizierung ein gemeinsames Geheimnis verwendet und kein öffentliches/privates Schlüsselpaar, beide Seiten halten also dasselbe endpoint_secret.

Ein Token bauen (was das aufrufende System tut)

Ein Token besteht aus drei Base64-kodierten Teilen, Header, Payload, Signatur, durch Punkte verbunden:

<header>.<payload>.<signature>

Der Payload (der mittlere Teil) ist ein kleines JSON-Objekt, das Claims enthält. Flexie benötigt genau einen bestimmten Claim und unterstützt einige weitere:

Claim Erforderlich? Was Flexie damit macht
iss Erforderlich Wird mit Ihrem endpoint_key verglichen. Muss exakt übereinstimmen.
exp Empfohlen Ablaufzeit (Unix-Sekunden). Tokens nach exp werden abgelehnt.
iat Optional Ausstellungszeitpunkt. Hilfreich für die Auditierung auf Seiten des Aufrufers.
nbf Optional „Not before"-Zeit. Das Token wird vor diesem Moment abgelehnt.
sub Optional Subjekt, z. B. der Nutzer oder das System, das das Token repräsentiert. Flexie ignoriert es bei der Verifizierung, aber Sie können es im Workflow aus dem Token auslesen.
data Optional Alles Zusätzliche, das dem Workflow zur Verfügung stehen soll. Siehe Was im Token steckt, auslesen.

Die Signatur wird über Header und Payload mit Ihrem endpoint_secret berechnet.

In Flexies eigenem Scripting ist das Bauen eines Tokens aus einem Workflow heraus eine einzige Zeile:

{% set token = jwtEncode({ "iss": "your-key", "exp": dateAdd(now(), 5, "minutes") }, "your-secret", "HS256") %}

Siehe jwtEncode in der Funktionsreferenz.

Wie Sie das Token senden

Beide Header funktionieren. Flexie prüft zuerst Authorization, dann greift es auf token zurück:

Authorization: Bearer <token>

…oder:

token: <token>

Das Präfix Bearer ist optional und wird, falls vorhanden, entfernt (Groß-/Kleinschreibung egal).

Toleranz gegenüber Uhrenabweichung

Flexie wendet einen Spielraum von 60 Sekunden auf die Prüfungen von exp und nbf an. Weicht die Uhr Ihres Aufrufers also um bis zu eine Minute von der von Flexie ab, werden Tokens trotzdem akzeptiert. Alles darüber hinaus wird als abgelaufen oder noch nicht gültig abgelehnt.

Was im Token steckt, auslesen

Der data-Claim eines Tokens steht, falls Sie einen einfügen, Ihrem Workflow zur Verfügung. Da er in einem Anfrage-Header (dem Authorization- oder token-Header) übertragen wird, wird er im Headers-Namensraum bereitgestellt, und da Header-Werte als Liste gespeichert werden (ein Eintrag je Vorkommen), greifen Sie mit [0] darauf zu:

{# Token payload was:
   { "iss": "internal-tools",
     "exp": 1727712000,
     "data": { "tenant": "acme", "feature_flag": "beta" } } #}

Tenant: {{ __data.__headers.__jwt_data[0].tenant }}
Beta on?: {{ __data.__headers.__jwt_data[0].feature_flag }}

Das ist nützlich, um Metadaten zu transportieren, die nicht Teil des Bodys sind: eine Tenant-ID, ein Feature-Flag, einen Akteur oder eine Identität, ohne sie in jeden Anfrage-Body einzubacken.

Wie eine fehlgeschlagene Verifizierung für den Aufrufer aussieht

Was passiert ist HTTP-Status Antwort-Body
Weder Authorization- noch token-Header vorhanden 403 Missing authentication token
Token konnte nicht dekodiert werden, falsche Signatur, abgelaufen oder noch nicht gültig 401 Not authorized. Invalid or expired token. <token>
Token dekodiert, aber sein iss stimmt nicht mit endpoint_key überein 401 Not authorized. Invalid key. <token>

Das Token wird im Fehler-Body zurückgespiegelt, nützlich zum Debuggen auf Seiten des Aufrufers, aber das bedeutet: Fehlerantworten enthalten das Token. Behandeln Sie sie in allen Logs wie sensible Daten.

Fehlschlag: der Workflow läuft nicht

Schlägt die JWT-Verifizierung fehl, wird der Workflow nie erreicht. Es gibt keinen Log-Eintrag in den Aktivitätsansichten des Workflows für die fehlgeschlagene Anfrage, dem Aufrufer wird nur die Fehlerantwort zurückgegeben. Wenn Sie eine Prüfspur fehlgeschlagener Versuche benötigen, führen Sie sie auf Seiten des aufrufenden Systems, oder leiten Sie über einen Workflow weiter, der nicht JWT-geschützt ist, und verifizieren Sie das Token selbst.

Häufige JWT-Fehler

  • Die rohe Signatur statt des vollständigen header.payload.signature senden. Ein JWT ist die gesamte dreiteilige Zeichenkette, nicht nur die Signatur.
  • Algorithmus-Diskrepanz. Aufrufer signiert mit HS256, gibt aber RS256 im Token-Header an. Flexie lehnt es ab.
  • Falsches iss. Tippfehler im endpoint_key oder eine veraltete Aussteller-Zeichenkette nach der Rotation.
  • Falsches Geheimnis. Tippfehler im endpoint_secret oder nach der Rotation nicht mehr synchron.
  • Uhrenabweichung über 60 Sekunden. Prüfen Sie mit date -u auf dem aufrufenden Host.
  • exp zu knapp. Geben Sie sich für Wiederholungsversuche genug Puffer.
  • Das Geheimnis in einer Browser- oder Mobile-App einbetten. Das Geheimnis muss serverseitig bleiben. Wird es geleakt, kann jeder gültige Tokens prägen.

Cross-Origin Resource Sharing (CORS)

CORS ist ein reines Browser-Konzept. Server, die Flexie direkt aufrufen (ein Webhook von einem anderen System, ein Cron von Ihren Servern), lösen nie CORS aus. Sie müssen es nur aktivieren, wenn ein Webbrowser den Endpunkt von einem anderen Origin aus aufrufen wird, typischerweise weil das JavaScript Ihrer Website ein Formular direkt an den Endpunkt postet.

Die zwei Einstellungen am Listener

Einstellung Was es ist
allow_cors Ein/Aus-Schalter. Wenn aktiviert, führt Flexie die Cross-Origin-Prüfungen durch und fügt die CORS-Antwort-Header hinzu.
cors_domains Die Erlaubnisliste. Eine kommagetrennte Liste von Origins (z. B. https://app.example.com,https://staging.example.com) oder das wörtliche *, um jeden Origin zuzulassen. Unterstützt Flexie Scripting.

Wann die CORS-Prüfung läuft

Sie läuft nur, wenn alle drei zutreffen:

  1. allow_cors ist aktiviert.
  2. Die Anfrage trägt einen Origin-Header.
  3. Der Origin der Anfrage ist verschieden vom eigenen Host von Flexie (eine Same-Origin-Anfrage überspringt die Prüfung vollständig).

Was geprüft wird

  • Der Origin-Header wird getrimmt und exakt mit jedem Eintrag in cors_domains verglichen (nach dem Trennen an Kommata und Trimmen).
  • Ist cors_domains das wörtliche *, wird jeder Origin zugelassen.
  • Der Abgleich ist exakt, inklusive Schema, Host und Port. https://app.example.com stimmt nicht mit http://app.example.com oder https://www.app.example.com überein.

Antwort-Header bei Zulassung

Flexie fügt diese der Antwort hinzu:

Access-Control-Allow-Origin: <the request's Origin, echoed back>
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
Access-Control-Allow-Headers: *

Selbst wenn cors_domains * ist, spiegelt der Antwort-Header den tatsächlichen Origin der Anfrage zurück statt *. Das ist beabsichtigt und erlaubt Ihnen, Cookies und Anmeldedaten sicher zu senden.

Preflight-Anfragen (OPTIONS)

Ein Browser sendet vor jeder „nicht-einfachen" Cross-Origin-Anfrage eine Preflight-OPTIONS-Anfrage (z. B. einer mit einem benutzerdefinierten Header wie Authorization oder einem Content-Type von application/json). Flexie behandelt den Preflight, indem es 204 No Content mit den CORS-Headern zurückgibt. Der Workflow läuft für den Preflight selbst nicht.

Wann CORS ablehnt

Ist der Origin der Anfrage nicht in der Erlaubnisliste, gibt Flexie 403 mit leerem Body zurück. Der Browser verweigert dann die Anfrage und meldet einen CORS-Fehler in der JavaScript-Konsole der Seite.

JWT und CORS kombinieren, der Preflight-Fallstrick

Das ist die wichtigste Wechselwirkung, die Sie kennen sollten.

JWT läuft vor CORS in der Anfrage-Pipeline. Das bedeutet: eine Preflight-OPTIONS-Anfrage des Browsers, die Browser ohne Authorization-Header senden, wird von der JWT-Prüfung abgelehnt mit 403 Missing authentication token, noch bevor der OPTIONS-Handler im CORS-Block überhaupt läuft.

Wenn Sie also beide aktivieren, Authentifizierung und CORS, in der Erwartung, dass Browser-Code den Endpunkt direkt mit einem Authorization-Header aufruft, schlägt der Preflight fehl und die eigentliche Anfrage wird nie gesendet.

Praktische Konsequenzen und Workarounds

Ihre Situation Empfohlene Konfiguration
Nur Server-zu-Server (anderes System, Cron, Partner-Backend) JWT an, CORS aus. Kein Preflight-Problem; der aufrufende Server sendet Authorization direkt.
Nur Browser, keine Authentifizierung (Formular-Post von öffentlicher Website) JWT aus, CORS an.
Browser plus Ihr eigenes Backend kann davorsitzen JWT an, CORS aus bei Flexie. Der Browser ruft Ihr Backend auf (Same-Origin, kein CORS); Ihr Backend ruft Flexie mit dem JWT auf. Das ist das empfohlene Muster, wenn Sie sowohl Authentifizierung als auch einen browser-initiierten Ablauf wollen.
Browser muss Flexie direkt aufrufen und authentifiziert sein Vermeiden Sie Authorization (das löst den Preflight aus). Senden Sie stattdessen ein kurzlebiges signiertes Token im Anfrage-Body oder Query-String und verifizieren Sie es im Workflow mit jwtDecode und hashHmac. CORS allein schützt den Endpunkt; das Token im Body authentifiziert jeden Aufruf.

Dokumentieren Sie das klar in Ihrer Integrationsübergabe. Es überrascht die Leute jedes Mal.

Sensible Header werden nie offengelegt

Selbst wenn die Authentifizierung aus und CORS an ist, entfernt Flexie einige Header von dem, was Ihr Workflow sehen kann. Cookies und alle in der Anfrage mitgeführten Authentifizierungs-Anmeldedaten werden entfernt, bevor die Anfrage protokolliert oder als __data.__headers.* bereitgestellt wird.

Wenn Sie cookiebasierte Daten im Workflow benötigen, senden Sie sie als benutzerdefinierten Header oder im Body.

Konkrete Beispiele

Server-zu-Server mit JWT (der häufigste Fall)

Das aufrufende System signiert ein Token und postet an Flexie:

# Build the token (pseudo-shell, use your language's JWT library in practice)
TOKEN=$(jwt-encode \
  --alg HS256 \
  --secret "$ENDPOINT_SECRET" \
  --claim "iss=$ENDPOINT_KEY" \
  --claim "exp=$(( $(date +%s) + 60 ))" \
  --claim "data={\"tenant\":\"acme\"}")

curl -X POST https://your-flexie/listener/abc.../def... \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "order_id": 12345, "amount": 99.50 }'

Im Workflow:

Received order {{ __data.order_id }} for tenant {{ __data.__headers.__jwt_data[0].tenant }}

Browser-initiierter Formular-Post nur mit CORS

Ein öffentliches Formular auf https://www.example.com postet an den Endpunkt:

<form id="signup" method="post"
      action="https://your-flexie/listener/abc.../def...">
  <input name="email" />
  <input name="first_name" />
  <button type="submit">Sign up</button>
</form>

Endpunkt-Einstellungen: allow_cors = on, cors_domains = https://www.example.com, kein JWT.

Browser plus Ihr Backend-Proxy (Authentifizierung und Browser kombiniert)

   Browser (yoursite.com)
        │ POST /api/signup  (same-origin)
        ▼
   Your back-end
        │ Adds Authorization: Bearer <fresh JWT>
        │ POST /listener/abc.../def...
        ▼
   Flexie (JWT verified, CORS off)

Das ist die sauberste Kombination aus „der Browser hat es abgeschickt" und „wir wissen, dass wirklich wir es waren, die aufgerufen haben".

Browser ruft direkt mit einem im Body mitgeführten signierten Token auf

Wenn Sie kein Backend davorsetzen können, signieren Sie ein kurzlebiges Token auf dem Server, der die Seite lädt, fügen es in den Formular-Body ein und verifizieren es im Workflow:

<input type="hidden" name="ts" value="2026-05-23T12:34:00Z">
<input type="hidden" name="sig" value="<HMAC of ts + form_id with a shared secret>">

In der ersten Entscheidung des Workflows:

{# Recompute the signature and compare; reject if mismatched or older than 5 min #}
{% set expected = hashHmac("sha256", __data.ts ~ "signup-form", "your-secret") %}
{{ __data.sig == expected and dateDiff(__data.ts, now(), "minutes") < 5 }}

CORS ist aktiviert (der Browser ist also erlaubt), JWT ist aus, und die Echtheit wird durch die Signatur im Body nachgewiesen.

Codebeispiele, ein Token signieren und eine Anfrage senden

Die folgenden Beispiele tun alle dasselbe: ein HS256-JWT bauen mit iss gleich Ihrem endpoint_key und einem Ablauf von 60 Sekunden, dann einen JSON-Body posten an den Endpunkt mit Authorization: Bearer <token>. Wählen Sie das passende für Ihren Stack.

Ersetzen Sie die Platzhalterwerte durch Ihren echten endpoint_key, endpoint_secret und die URL, die Flexie für Ihren Listener erzeugt hat.

Shell (curl)

curl signiert Tokens nicht selbst. Signieren Sie das Token mit einem kleinen Helfer (ein einmaliges Node- oder Python-Skript, ein CLI-Tool, Ihr Secret-Store) und reichen Sie es durch:

TOKEN="<your signed JWT here>"

curl -X POST https://your-flexie/listener/abc.../def... \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "order_id": 12345, "amount": 99.50 }'

Nützlich für Ad-hoc-Tests, sobald Sie ein Token anderswo erzeugt haben.

Node.js (jsonwebtoken + eingebautes fetch)

import jwt from 'jsonwebtoken';

const ENDPOINT_URL    = 'https://your-flexie/listener/abc.../def...';
const ENDPOINT_KEY    = 'your-endpoint-key';
const ENDPOINT_SECRET = 'your-endpoint-secret';

const token = jwt.sign(
  { data: { tenant: 'acme' } },                // your optional metadata
  ENDPOINT_SECRET,
  { algorithm: 'HS256', issuer: ENDPOINT_KEY, expiresIn: '60s' }
);

const res = await fetch(ENDPOINT_URL, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ order_id: 12345, amount: 99.50 }),
});

console.log(res.status, await res.text());

jsonwebtoken setzt iss automatisch in den Payload, wenn Sie issuer übergeben.

Python (PyJWT + requests)

import jwt
import time
import requests

ENDPOINT_URL    = 'https://your-flexie/listener/abc.../def...'
ENDPOINT_KEY    = 'your-endpoint-key'
ENDPOINT_SECRET = 'your-endpoint-secret'

token = jwt.encode(
    {
        'iss': ENDPOINT_KEY,
        'exp': int(time.time()) + 60,
        'data': {'tenant': 'acme'},
    },
    ENDPOINT_SECRET,
    algorithm='HS256',
)

response = requests.post(
    ENDPOINT_URL,
    headers={'Authorization': f'Bearer {token}'},
    json={'order_id': 12345, 'amount': 99.50},
)
print(response.status_code, response.text)

PHP (firebase/php-jwt + cURL)

use Firebase\JWT\JWT;

$endpointUrl    = 'https://your-flexie/listener/abc.../def...';
$endpointKey    = 'your-endpoint-key';
$endpointSecret = 'your-endpoint-secret';

$token = JWT::encode(
    [
        'iss'  => $endpointKey,
        'exp'  => time() + 60,
        'data' => ['tenant' => 'acme'],
    ],
    $endpointSecret,
    'HS256'
);

$ch = curl_init($endpointUrl);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        "Authorization: Bearer $token",
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode(['order_id' => 12345, 'amount' => 99.50]),
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
$status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, $response;

Go (github.com/golang-jwt/jwt/v5 + net/http)

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "time"

    "github.com/golang-jwt/jwt/v5"
)

func main() {
    endpointURL    := "https://your-flexie/listener/abc.../def..."
    endpointKey    := "your-endpoint-key"
    endpointSecret := []byte("your-endpoint-secret")

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "iss":  endpointKey,
        "exp":  time.Now().Add(60 * time.Second).Unix(),
        "data": map[string]string{"tenant": "acme"},
    })
    signed, err := token.SignedString(endpointSecret)
    if err != nil { panic(err) }

    body, _ := json.Marshal(map[string]any{
        "order_id": 12345, "amount": 99.50,
    })
    req, _ := http.NewRequest("POST", endpointURL, bytes.NewReader(body))
    req.Header.Set("Authorization", "Bearer "+signed)
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()

    fmt.Println(resp.Status)
}

C# / .NET (System.IdentityModel.Tokens.Jwt)

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using Microsoft.IdentityModel.Tokens;

const string EndpointUrl    = "https://your-flexie/listener/abc.../def...";
const string EndpointKey    = "your-endpoint-key";
const string EndpointSecret = "your-endpoint-secret";

var key   = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(EndpointSecret));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var jwt = new JwtSecurityToken(
    issuer:  EndpointKey,
    expires: DateTime.UtcNow.AddMinutes(1),
    claims:  new[] {
        new Claim("data",
            JsonSerializer.Serialize(new { tenant = "acme" }),
            JsonClaimValueTypes.Json)
    },
    signingCredentials: creds
);
var token = new JwtSecurityTokenHandler().WriteToken(jwt);

using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new("Bearer", token);

var body = JsonSerializer.Serialize(new { order_id = 12345, amount = 99.50 });
var resp = await http.PostAsync(EndpointUrl,
    new StringContent(body, Encoding.UTF8, "application/json"));

Console.WriteLine(resp.StatusCode);

Java (io.jsonwebtoken:jjwt-api + eingebauter HttpClient)

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class FlexieEndpointCall {
    public static void main(String[] args) throws Exception {
        var url    = "https://your-flexie/listener/abc.../def...";
        var key    = "your-endpoint-key";
        var secret = "your-endpoint-secret";

        var keySpec = new SecretKeySpec(
            secret.getBytes(StandardCharsets.UTF_8),
            SignatureAlgorithm.HS256.getJcaName()
        );

        String token = Jwts.builder()
            .setIssuer(key)
            .setExpiration(new Date(System.currentTimeMillis() + 60_000))
            .claim("data", Map.of("tenant", "acme"))
            .signWith(keySpec, SignatureAlgorithm.HS256)
            .compact();

        var request = HttpRequest.newBuilder(URI.create(url))
            .header("Authorization", "Bearer " + token)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(
                "{\"order_id\":12345,\"amount\":99.50}"))
            .build();

        var response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.statusCode() + " " + response.body());
    }
}

Ruby (jwt-Gem + net/http)

require 'jwt'
require 'net/http'
require 'json'
require 'uri'

ENDPOINT_URL    = 'https://your-flexie/listener/abc.../def...'
ENDPOINT_KEY    = 'your-endpoint-key'
ENDPOINT_SECRET = 'your-endpoint-secret'

token = JWT.encode(
  { iss: ENDPOINT_KEY,
    exp: Time.now.to_i + 60,
    data: { tenant: 'acme' } },
  ENDPOINT_SECRET,
  'HS256'
)

uri  = URI(ENDPOINT_URL)
http = Net::HTTP.new(uri.host, uri.port).tap { |h| h.use_ssl = true }
req  = Net::HTTP::Post.new(uri.request_uri, {
  'Authorization' => "Bearer #{token}",
  'Content-Type'  => 'application/json',
})
req.body = { order_id: 12345, amount: 99.50 }.to_json

response = http.request(req)
puts response.code, response.body

Aus Flexie selbst heraus (ein Workflow ruft einen geschützten Endpunkt auf)

Wenn Sie einen anderen Flexie-Endpunkt (oder irgendein anderes System, das dieselbe JWT-Form erwartet) aus einem Workflow heraus aufrufen, nutzen Sie die Webhook-Aktion und bauen das Token im Feld Headers:

{% set token = jwtEncode(
     { "iss": "your-endpoint-key",
       "exp": dateAdd(now(), 1, "minutes"),
       "data": { "tenant": "acme" } },
     "your-endpoint-secret",
     "HS256"
) %}
Authorization: Bearer {{ token }}
Content-Type: application/json

Die Funktion jwtEncode ist Teil von Flexie Scripting.

Was der Workflow auf der Empfängerseite ausliest

Für alle obigen Beispiele liest der empfangende Flexie-Workflow den Anfrage-Body und den data-Claim so aus:

Received order {{ __data.order_id }} for {{ __data.amount }}
Tenant: {{ __data.__headers.__jwt_data[0].tenant }}

Spickzettel für Fehlerantworten

Ursache HTTP Body
Fehlendes Token (add_authentication=on) 403 Missing authentication token
Token nicht parsebar, falsche Signatur, abgelaufen oder noch nicht gültig 401 Not authorized. Invalid or expired token. <token>
Token-Aussteller (iss) stimmt nicht mit endpoint_key überein 401 Not authorized. Invalid key. <token>
Origin nicht in cors_domains (allow_cors=on) 403 (leer)
Erfolgreicher Preflight (OPTIONS) 204 (leer, mit CORS-Headern)

Operative Checkliste

Bevor Sie einen geschützten Endpunkt veröffentlichen:

  1. Rotieren Sie das automatisch erzeugte Geheimnis auf einen eigenen Wert und legen Sie es im Secret-Store Ihres aufrufenden Systems ab.
  2. Setzen Sie exp auf jedem Token. Liefern Sie nie langlebige Tokens an Clients aus, die Sie nicht vollständig kontrollieren.
  3. Entscheiden Sie, wer den data-Claim braucht und was er trägt. Behandeln Sie ihn wie jedes andere vom Aufrufer gelieferte Metadatum, vertrauen Sie ihm nicht bei Sicherheitsentscheidungen.
  4. Klären Sie für Browser-Aufrufer zuerst die CORS-Form, direkt plus Token-im-Body oder Proxy-über-Ihr-Backend. Die Kombination „Authorization + CORS-Preflight" funktioniert nicht.
  5. Testen Sie die Fehlerpfade, nicht nur den Happy Path: fehlendes Token, abgelaufenes Token, falsches Geheimnis, falscher Aussteller, falscher Origin. Jeder hat eine eigene Antwort.
  6. Achten Sie auf zurückgespiegelte Tokens in 401-Bodys. Wenn Sie Antworten auf Aufruferseite loggen, schwärzen Sie das Token im Log.

Zurück zu