Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
205 lines (128 sloc) 22 KB

Universal Stringbased Application Communication Protokoll | USAC-Protocoll

VERSION: 0.01

Ziele

USAC hat sich zum Ziel gesetzt, eine neue komplette Protokoll-Ebene in den Körper des HTTP-Protokolls einzubetten und dabei trotzdem noch alle Parameter zu beinhalten, um eigenständig in der Client-Server-Kommunikation zu agieren. HTTP ist also im Grunde der ursprüngliche Treiber gewesen. Aber funktionieren soll das ganze auch mit einer eigenen Client-Server-Architektur auf Basis von TCP. Im Details sollten die nachfolgenden Bedingungen berücksichtigt werden:

Klare Trennungen der Protokollebenen

Während z.B. bei REST das HTTP-Protokoll nicht nur als Transportprotokoll, sondern auch als Anwendungsprotokoll verwendet, soll USAC das Transportprotokoll klar vom Anwendungsprotokoll trennen. Denn während das Web und darauf basierende Funktionen wie z.B. WebDAV klar mit dem HTTP-Protokoll verknüpft sind, sind Webservices meist Protokolle, die an die Eigenschaften von HTTP angepasst wurden und damit zwangsläufig Limitierungen ausgesetzt sind. Allerdings können Anwendungen auch gesonderte Anforderungen haben, die von HTTP nicht oder nur schwer bedient werden können.

Einfachere Entwicklung

Ein HTTP-Header besteht in der Regel aus zeilenbasierten Schlüssel-Wertpaaren, die auf der Anwendungsebene anders verarbeitet werden, als es mit HTTP gemacht wird. Das hat zur folge, das Anwendungsparameter erst aus dem Header gelöst, ausgewertet und interpretiert werden müssen, während zusätzliche Parameter, die HTTP nicht bietet, oftmals im Körper einer Anfrage/Antwort zusätzlich eingepflegt werden. Mit einer zusätzlichen Protokollebene ist es z.B. auf Clientseite nur erforderlich, den HTTP Statuscode zu prüfen, um eine erfolgreiche Anfrage sicherzustellen. Da genau dieser Code in den ersten 3 Zeichen der Rückantwort steht, ist das auslesen nicht kompliziert. Alles andere betreffend der Anwendung wird hingegen aus dem JSON-Dokumenten entnommen, für die es in nahezu jeder Sprache einen Parser gibt und das Handlich mit diesen Daten stark vereinfacht.

Serverseitig direkterer Zugriff

Serverseitige Sprachen haben historisch bedingt einen vereinfachten Zugriff auf Datenparameter aus der URL oder aus dem Körper. Dies kommt aus der Formularverarbeitung des Webs. Die Parameter module und functionaus der URL url.app/?module=users&function=delete werden in PHP über das Array $_GETabgeholt, während für Formulardaten aus einem POST-Request das Array $_POSTverwendet wird. Dies funktioniert allerdings nicht, wenn im Körper ein JSON-Dokument mitgeliefert wird. Hier müssen serverseitige Sprachen über Rohabfragen (die bei PHP mittlerweile nur noch eingeschränkt funktionieren) oder mit virtuellen Inputstreams gearbeitet werden (was in PHP nun gängige Praxis ist). Da USAC 2 Ebenen vorsieht, besteht die erste Ebene aus den für Formulare üblichen Schlüsseln, während als Inhalt dieser Schlüssel JSON-Dokumente übertragen werden. So kann man in PHP mit den folgenden Codes direkt auf die Daten zugreifen:

$HeaderData = json_decode($_POST['HEADER']); // Erfasst USAC Header und dekodiert ihn in ein Array
$WorkData = json_decode($_POST['DATA']); // Erfasst USAC Datenkörper und dekodiert ihn in ein Array

echo $HeaderData['BASE_TYPE']; // Würde je nach Typ LOGIN, LOGINROLE, WORK etc. ausgeben

So leicht kann es sein. Ohne Daten aus dem Request-Header des HTTP-Requests zu fummeln.

Leicht umzusetzen

Da man in seiner Anwendung nicht mehr mit den HTTP-Eigenschaften konfrontiert wird sondern einzig den Statuscode verwendet, kann man sich voll und ganz auf die Anwendung selbst konzentrieren. HTTP ist nur noch das Transportprotokoll. Man kann die Verschlüsselung über die HTTPS Fähigkeiten nutzen, ohne sich zeitgleich mit dem Herauslösen von Cookies oder anderen Headerinformationen auseinanderzusetzen. HTTP wird einzig und alleine zum übertragen der Daten verwendet. Alles andere, was die Anwendung betrifft, befindet sich ausschließlich im Körper der Kommunikation. Die vorgegebenen Schlüssel stellen hierbei ein Minimum dar, um eine gewisse Einheit abzubilden und Kompatibilitäten zu ermöglichen. Erweitert werden kann das ganze aber problemlos durch eigene Schlüssel und ermöglicht so eine umfangreiche Anpassbarkeit.

Webressource vs. Anwendungsressource

Die strikte Trennung bedeutet auch, das eines der Ziele sein soll, Anwendungsressourcen nicht über die URL anzusprechen. Die URL ist ein Zeiger auf den Anwendungsserver. Und dies ist auch die einzige Ressource, die der Webserver liefern soll. Die Anwendungsressourcen werden über den USAC-Header angesprochen und von Anwendungsserver ausgewertet. In der Regel passierte in der klassischen serverseitigen Webentwicklung nichts anderes. Zu Zeiten, wo mit Rewrite die URL's noch nicht aufgehübscht wurden, war das auch klarer zu erkennen. Die einzige Ressource war immer die index.php, wohingegen die genauen Ressourcen über die URL-Parameter spezifiziert wurden. Heute ist das Vorgehen überwiegend identisch. Rewrites verschleiern es nur. Diese URL-Parameter wandern mit USAC allerdings in den USAC-Header. Somit kann mit nur einer einzigen URL gearbeitet werden. Das ständige manipulieren des URL-Strings entfällt.

Konzeptleitfaden

Das Konzept nimmt sich einigen Regeln an, die leicht zu merken und klar zu verstehen sind. An Hand dieser Regeln kann man unter Umständen auch einige Designentscheidungen eher verstehen.

  1. Anwendung ist Anwendung. Protokoll ist Protokoll. Web ist Web. Service ist Service.

    Diese Regel soll eigentlich nichts anderes Sagen als "Jeder Baustein erfüllt seinen Zweck. Und zwar genau seinen!". In diesem Fall steht diese Regel für die Trennung von Transport- und Anwendungsprotokoll. HTTP und auch TCP sind Transportprotokolle. Im Web (für den Browser) übernimmt das HTTP-Protokoll zugleich die Aufgabe des Anwendungsprotokolls. Wir entwickeln mit USAC aber keine Webanwendung, sondern eine Client-Server Anwendung. USAC kann daher HTTP als Transportprotokoll verwenden, ohne das die Anwendung HTTP-spezifische Header berücksichtigen muss. Dadurch ist USAC auch transportabel und kann direkt per TCP oder UDP verwendet werden, ohne ein zusätzliches standardarisiertes Transportprotokoll wie HTTP zu verwenden. Alternativ wären auch andere stringbasierte Transportprotokolle möglich.

  2. Der Server ist der Chef!

    Diese Regel ist selbsterklärend. Der Chef gibt die Fahrtrichtung vor. Und so sollten auch die Anwendungen mit USAC entwickelt werden. Anfragen vom Client sind auch als Anfragen und nicht als Befehle zu sehen. Der Server prüft also alles gegen, was ihm sein Angestellter gibt. Bei bidirektionaler Kommunikation kann natürlich auch der Server den Client ansprechen. Aber auch hier gilt, das der Chef die Kommandos gibt. Der Chef fragt also keine Ressource an, sondern gibt eine Klare Anweisung.

  3. Der Knochen kommt nicht zum Hund!

    Der Server (Chef) weiß in der Regel nicht, was der Client von ihm will. Somit wartet er auf eine Anfrage vom Client und reagiert direkt auf diese. Der Chef liefert nach erfolgreichem Login niemals direkt in der selben Antwort Arbeitsdaten mit. Und wenn der Chef den Angestellten kontaktiert, dann sind es entweder klare Anweisungen oder Statusupdates (z.B. neue Message im Postfach).

  4. Der Kunde ist ein Modul, ihn zu verwalten Funktionen und seine Daten sind der Datensatz!

    Diese Regel ist eine Anspielung auf die App-Schlüssel, die im Folgendem noch beschrieben werden. Dort sind für den Header genau drei Felder bestimmt. Diese Felder dienen dazu, die Ressourcen so genau wie möglich zu spezifizieren, damit der Chef genau weiß, was der Client vorhat. Dabei bestimmt das Modul den Datenbestand oder den Prozess, mit dem gearbeitet werden soll. Funktionen bestimmen das genaue vorgehen beim Arbeiten mit dem Bestand oder Prozess. Und zum Schluss kann man auch noch den genauen Datensatz spezifizieren, mit dem gearbeitet werden soll.

  5. Der Kopf steuert den Server. Der Datenbereich die Funktion!

    Das bedeutet, das Funktionen ihre Steuerung ausschließlich über die Daten des Datenbereichs bekommen. Es ist nicht vorgesehen, eine Funktion über den Kopf oder der URL (HTTP) zu steuern. In der Webentwicklung ist so etwas Gang und gebe, dem Server über die URL mitzuteilen, wie er Daten sortieren soll oder nach welchem Begriff eine Suchfunktion suchen soll. Der Header stellt für solche Fälle allerdings keinerlei Felder zur Verfügung. Der Kopf steuert den Server. Der Datenbereich die Funktion!

Die Ebenen im Detail

Ebene 1

Die erste Ebene ist die Selektions-Ebene. Über diese wird der Körper von den Kopfdaten strikt getrennt. Damit diese Strikt von einander getrennt werden können, dürfen und sollen weder das Kopf-, noch das Körper-Dokument, in einem JSON-Dokument zusammengefasst werden. Aus diesem Grund nutzt HBAP hier die Trennung durch unterschiedliche Dokument-Formen. Ebene 1 sind klassische HTTP-Parameter, in denen die JSON-Dokumente aus Ebene 2 liegen. Dies hat zugleich mehrere Vorteile:

  1. Direkte Abfrage in serverseitigen Programmiersprachen wie PHP über das $_POST Array ($_POST['HEADER'] und $_POST['DATA']). Kein Workarround über den Inputstream notwendig.
  2. Kopf- und Datenstrukturen liegen nicht in einem Dokument. Man kann z.B. das Datendokument direkt ohne weitere Verarbeitung durchlaufen, nachdem man es in ein Array, einer Map/Dictonary oder eine Liste deserialisiert hat.
  3. Sollte eine Sprache/Bibliothek keinen Parser für HTTP-Parameter besitzen, lässt es sich leicht umsetzen, da die Blöcke in der Regel nur durch 2 Zeichen getrennt werden müssen.
  4. Dokumente haben in der Regel nur eine Ebene (Ähnlich einem einfachen Webformular). Da das verarbeiten von JSON gerade bei strikt typisierten Sprachen und festen Größen von Arrays fummelig ist, kommt einem der Wegfall einer Ebene durchaus entgegen.
  5. Man kann bei kleineren Datenmengen die Informationen auch über die URL übertragen und behält die strikte Trennung bei. Allerdings verstößt dies wieder gegen die strikte Trennung von Trägerprotokoll und Anwendungsprotokoll.

Das Schema:

HEADER=KOPFDATEN_ALS_JSONDOKUMENT&DATA=DATENBEREICH_ALS_JSONDOKUMENT

Ebene 2

Die zweite Ebene besteht aus JSON-Dokumenten. Im Kopfbereich sind strikt nur Schlüssel-Wert-Paare erlaubt. Man sollte hier keine tiefere Verschachtelungen bilden und sollten auch nicht nötig sein. Im Datenbereich sind Schlüssel-Wert-Paare empfohlen, müssen aber nicht strikt eingehalten werden. JSON-Dokumente kommen im Gegensatz zu XML mit weniger Overhead aus und erlauben so dieses Konzept. Denn trotz immer schnellerer Breitbandanschlüsse ist es bis heute noch nicht möglich, in jeder Situation davon zu profitieren. Man profitiert aber in der Regel davon, strukturiert Daten auszutauschen, ohne ein wuchtiges XML-DOM zu verwenden.

Da beide Bereiche in eigenen Dokumenten liegen, müssen auch beide einzeln geparst werden und landen direkt auch in zwei unterschiedlichen Strukturen, die man getrennt von einander Sicher behandeln kann. Damit kann und soll ausgeschlossen werden, das versehentlich Kopfdaten als Arbeitsdaten verarbeitet werden und umgekehrt.

Der Headerbereich

AUTH_ Schlüssel

Die AUTH- Schlüssel stellen Informationen dar, mit denen die Authentifizierung und Authorisierung innerhalb einer Anwendung erfolgt. Sendet der Server diese Schlüssel, sind diese als Informationen für den Client zu sehen, die er sich merken soll, wohingegen der Client diese bei jeder Anfrage senden muss, um sich beim Server zu Authentifizieren. Diese Schlüssel sind optional, da auch durchaus Daten abgefragt werden können, die der Allgemeinheit offen stehen.

Die AUTH_ Schlüssel sind im Grunde wie Session-Cookies zu verstehen. Allerdings soll hier mit einem festen Muster und mindestens zwei Pflichtschlüsseln klar definiert werden, wie die Authorisierung erfolgen soll. Einen Ablaufzeitpunkt der Session ist in dem Protokoll nicht vorgesehen. Der Server gibt an, wie lange eine Session Gültigkeit hat und der Client bestimmt, wie lange er sich die Session merken soll. Ist die Session serverseitig abgelaufen, kann der Server dies dem Client melden und dieser kann die Sessioninformationen überschreiben bzw. löschen. Im Gegenzug kann der Server bei langer Inaktivität (Nichtnutzung der Token) die Sessions löschen.

Client-Schlüssel

  • AUTH_ID: Mit diesem Schlüssel sendet der Client den Benutzernamen beim Login an den Server. Ist der Benutzer bereits eingeloggt, sendet der Client hierrüber den Benutzerschlüssel, den der Client beim Login bekam.
  • AUTH_KEY: Mit diesem Schlüssel sendet der Client das Passwort beim Login an den Server. Ist der Benutzer bereits eigeloggt, sendet der Client hierrüber den Authorisierungs-Schlüssel.
  • AUTH_OPEN: Dieser Schlüssel ist in seiner Definition offen und kann als zusätzlicher Sicherheitsschlüssel dienen. Der Benutzer kann hierrüber eine zusätzliche PIN an den Server senden. Oder es kann als Mandants-Feld verwendet werden. Man kann darüber auch die Abteilung regeln. Der Schlüssel ist optional und kann auch für einen dritten Token nach erfolgreichem Login verwendet werden.

Server-Schlüssel

  • AUTH_ID: Mit diesem Schlüssel sendet der Server bei erfolgreichem Login einen Identifizierungstoken oder eine Session-ID an den Client. Der Client muss sich diesen merken, da jede weitere authorisierungspflichtige Anfrage diesen Token erfordert (Siehe Client-Schlüssel). Der Server sendet diesen nur einmal bei Login!
  • AUTH_KEY: Mit diesem Schlüssel sendet der Server einen Authentifizierungstoken an den Client. Der Client muss sich auch diesen merken, da jede weitere authorisierungspflichtige Anfrage diesen Token erfordert (Siehe Client-Schlüssel). Der Server sendet diesen nur einmal bei Login!
  • AUTH_OPEN: Dieser Schlüssel kann ebenfalls dazu genutzt werden, einen weiteren Daten-Token an den Client zu senden, den er sich merken und bei jeder weiteren authorisierungspflichtigen Anfrage an den Server senden muss. Es ist also empfohlen, auch diesen nur einmal an den Client bei erfolgreichem Login zu senden.

APP_ Schlüssel

Die APP_ Schlüssel sind ebenfalls optional und können dazu genutzt werden, durchweg nur eine URL im Client anzusprechen und auf dem Server ein passendes Routing zu implementieren. Somit kann einem das lästige Erzeugen eines URL-Strings erspart bleiben. Diese Schlüssel verwendet ausschließlich der Client. Die App-Schlüssel dienen nur dazu, Ressourcen gezielt anzufordern. Sie dienen aber nicht dazu, Module und Funktionen zu steuern. Dazu ist der Datenbereich da. Suchfunktionen sowie die Steuerung von Listensortierungen können dort problemlos eingepflegt werden.

  • APP_MODULE: Mit dem Schlüssel teilt der Client mit, welches Modul er verwenden möchte. Module könnten zum Beispiel in einer ERP-Lösung der Kundenstamm, die Auftragsverarbeitung oder das Lagermanagement sein.

  • APP_FUNCTION: Mit diesem Schlüssel spricht der Client eine spezifische Funktion an. Zum Beispiel das Anlegen eines Kunden, das Beenden eines Auftrags oder das Buchen eines Warenausgangs im Lager. Wird dies weggelassen, kann das angeforderte Modul eine Default-Funktion ausführen.

  • APP_DATA: Mit diesem Schlüssel kann der Client einen genauen Datensatz gezielt bearbeiten. Der Wert kann zum Beispiel eine ID sein. Im Falle einer Listenfunktion kann hier aber auch eine Datenspalte angegeben werden, nach der sortiert wird.

BASE_ Schlüssel

Die BASE_ Schlüssel sind die Basis-Protokoll Schlüssel. Diese dienen dazu, den Status über die angeforderten Ressourcen dem Client mitzuteilen, während der Client selbst den Typ der Anfrage definiert.

Clientschlüssel BASE_TYPE

Der BASE_TYPE Schlüssel definiert den Typ der Anfrage und kann vom Server genutzt werden, ein Vorrouting durchzuführen. Die APP_ Schlüssel sollten dabei ausschließlich nur verarbeitet werden, wenn der BASE_TYPE den Wert WORK hat. Dieser Schlüssel ist ein Pflichtschlüssel bei jeder Anfrage. In der Echtzeitkommunikation kann dieser Schlüssel auch vom Server genutzt werden. In diesem Feld wird der Anweisungstyp klar und direkt angegeben.

  • WORK: Work ist der Arbeitstyp und soll dem Server anweisen, eine Sessionüberprüfung durchzuführen. Werden keine gültigen Session-Informationen über die AUTH_ Schlüssel mitgesendet, soll der Server den Client als Anonym behandeln und den Zugriff auf Daten, Module und Funktionen entweder einschränken oder ganz sperren.
  • LOGIN: Hiermit fordert der Client eine Session an Hand der AUTH_ Schlüssel an. Der Server validiert die Werte und sendet einen entsprechenden Status zusammen mit den Token an den Client zurück.
  • LOGOUT: Der Client fordert hiermit den Server auf, die Session zu beenden.
  • ROLE: Hiermit kann der Client eine Liste seiner Berechtigungen beim Server anfragen, die er im Datenbereich mitgeteilt bekommen soll. Hierfür sollte der Client bereits eingeloggt und AUTH-Token vom Server bekommen haben.
  • LOGINROLE: Eine Kombination aus Login und Role. Der Server sendet bei erfolgreichem Login neben den Token auch direkt die Zugriffsliste mit.
  • USER: Hiermit kann der Client seine Benutzerdaten anfragen.

Clientschlüssel BASE_APPKEY

Der Schlüssel BASE_APPKEY ist ein optionaler Schlüssel und kann einen Applikation-Token (auch oft als API-Key bekannt) enthalten, der die Client-Anwendung gegenüber dem Server identifiziert. In der Regel betten Entwickler diesen Schlüssel bereits in ihrer Anwendung ein und ist im Grunde für alle Instanzen dieser Anwendung gültig. Es soll in erster Linie nur erschweren, das fremde Anwendungen sich an der API anmelden. Ein solcher Token kann aber auch als UserAgent vom HTTP-Request mitgesendet werden. Wer aber komplett auf die Arbeit mit den HTTP-Headern verzichten möchte, kann diesen Schlüssel verwenden.

Serverschlüssel BASE_STATE

Der BASE_STATESchlüssel gibt einen Status zurück, der kurz und knapp genau sagt, was los ist. Die Statis sind nicht wie üblich in Nummern aufgelöst, sondern in kurzen und somit trotzdem vergleichbaren Strings angegeben. Das hat zum einen den Vorteil, das es bei der Definition von Statuscodes nicht zu Verwirrungen kommt und zum anderen können diese theoretisch direkt ausgegeben werden, ohne erst eine Nummer zu einer Fehlermeldung aufzulösen.

  • LOGIN_OK: Dies meldet der Server, wenn der Login erfolgreich gewesen ist. In diesem Fall muss der Client sich die AUTH-Token merken.
  • LOGIN_FAILED: Wenn der Login fehlgeschlagen ist, sendet der Server dies zurück.
  • ROLE_DATA: Dieser Status kommt vom Server, wenn im Datenbereich die Rollenliste enthalten ist.
  • USER_DATA: Dieser Status kommt vom Server, wenn im Datenbereich die Benutzerdaten enthalten sind.
  • INVALID_SESSION: Wenn die Session vom Server beendet oder die Token falsch sind, meldet der Server diesen Wert zurück. In diesem Fall sollte der Datenbereich immer leer sein und der Client sollte auch auf seiner Seite die Session beenden.
  • INVALID_RESSOURCE: Dieser Status kann verwendet werden, wenn die angeforderten Ressourcen nicht existieren.
  • INVALID_DATA: Dies kann zurückgeliefert werden, wenn der Datenbereich vom Client ungültig ist und von der Funktion nicht verarbeitet werden kann. Im Datenbereich können dann die ungültigen Daten genauer spezifiziert werden.
  • REQUEST_OK: Standard-Status bei erfolgreichen WORK-Anfragen.
  • INVALID_APPKEY: Kann vom Server zurückgemeldet werden, wenn der App-Key nicht gültig ist.

Serverschlüssel BASE_MESSAGE

Hiermit kann eine zusätzliche Nachricht vom Server an den Client geschickt werden, die der selbige direkt an den Benutzer weitergeben kann oder der Wert kann eine zusätzliche Steuerung in der bidirektionalen Kommunikation enthalten (Siehe BASE_TYPE für den Server in der TCP-Kommunikation).

BASE_TYPE für den Server in der TCP-Kommunikation

Meldet sich ohne Anfrage der Server zur Wort, dann kann der Server hierrüber zurückmelden, was er vom Client will bzw. Aktualisierungen mitteilen.

  • NEW_INFO: Dieser Typ gibt an, das eine neue Information verfügbar ist. Welche Art von Information kann zum einen im Schlüssel BASE_MESSAGE in Form eines technischen Wertes (z.B. NEW_MAIL oder NEW_COSTUMER) bestimmt werden oder es wird einfach der Datenbereich dafür genutzt.
  • NEW_LOGIN: Der Server kann diese Anweisung senden, wenn ein Benutzer während seiner Session bearbeitet wurde und eine clientseitige Aktualisierung erforderlich ist.
  • TASK_UPDATE: Wenn die Software Prozesse im Hintergrund abarbeitet, kann der Nutzer, der diese Bearbeitung angestoßen hat, über Neuigkeiten informiert werden wie Fehler oder Abschluss.
  • TECHNICAL_PROBLEMS: Gerade bei mehrschichtigen System kann es hilfreich sein, den Client direkt bei technischen Problemen zu informieren (z.B. Datenbankausfall).
  • MAINTENENCE_ANNOUNCEMENT: Zur Ankündigung von Wartungsarbeiten.

Der Datenbereich

Viel ist zu diesem Bereich nicht zu sagen. In diesem Bereich werden Daten im JSON-Format ausgetauscht. An sich unterliegen diese keinerlei zusätzlichen Regeln. Wichtig dabei ist nur zu wissen, das dieser Bereich nur für Daten und zum Steuern von Funktionen gedacht ist. Hier werden weder Ressourcen angefordert noch die Steuerung des Servers vorgenommen.

Beispiele: Leserlich

Client-Anfrage

HEADER={
    "BASE_TYPE": "LOGINROLE",
    "AUTH_ID": "BennoX",
    "AUTH_KEY": "Secret2019#",
    "AUTH_OPEN": "1982"
}

Serverantwort

HEADER={
    "BASE_STATE": "LOGIN_OK",
    "AUTH_ID": "65D140613CDF6558B43050ED22A5D82EE3E55694",
    "AUTH_KEY": "5CC6B356A1A30503C4A913F687777A8E4E41DF55",
    "AUTH_OPEN": "D531AD5CC8B6ADFE82A0CB2D9068B628C082208C"
}&DATA={
    "customer": 5,
    "warehouse": 3,
    "bills": 1
}

Beispiele: Roh

Client-Anfrage

HEADER=%7B%22BASE_TYPE%22%3A%22LOGINROLE%22%2C%22AUTH_ID%22%3A%22BennoX%22%2C%22AUTH_KEY%22%3A%22Secret2019%23%22%2C%22AUTH_OPEN%22%3A%221982%22%7D

Serverantwort

HEADER=%7B%22BASE_STATE%22%3A%22LOGIN_OK%22%2C%22AUTH_ID%22%3A%2265D140613CDF6558B43050ED22A5D82EE3E55694%22%2C%22AUTH_KEY%22%3A%225CC6B356A1A30503C4A913F687777A8E4E41DF55%22%2C%22AUTH_OPEN%22%3A%22D531AD5CC8B6ADFE82A0CB2D9068B628C082208C%22%7D&DATA=%7B%22customer%22%3A5%2C%22warehouse%22%3A3%2C%22bills%22%3A1%7D
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.