OBS/Kostenpflichtige Module/RESTServer/Zugaenge: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 28: | Zeile 28: | ||
| JWT || Schalter, ob für diesen Zugang ein JWT-Token nötig ist | | JWT || Schalter, ob für diesen Zugang ein JWT-Token nötig ist | ||
|- | |- | ||
| JWT-Endpunkt || | | JWT-Endpunkt || Pfad, über den der JWT-Token bezogen wird (z.B. ''oauth'' oder ''meineapp/dev/auth/login''). Verglichen wird der '''vollständige Pfad''', mehrstufige Angaben mit ''/'' sind also erlaubt | ||
|- | |- | ||
| JWT-Key || Geheimer Schlüssel zur Signierung/Verifikation, per Schlüsselsymbol generierbar | | JWT-Key || Geheimer Schlüssel zur Signierung/Verifikation, per Schlüsselsymbol generierbar | ||
|- | |- | ||
| JWT-Exp || Gültigkeitsdauer eines ausgestellten Tokens in Minuten | | JWT-Exp || Gültigkeitsdauer eines ausgestellten Access-Tokens in '''Minuten'''. Derselbe Wert wird dem Konsumenten in der Token-Antwort als ''expiresIn'' in '''Sekunden''' mitgeteilt und steht dem Endpunkt-Skript als ''_OBS_JWT_EXP_SEC'' zur Verfügung | ||
|} | |} | ||
| Zeile 82: | Zeile 82: | ||
* ''error'' = Fehlertext (bei status=9) | * ''error'' = Fehlertext (bei status=9) | ||
* ''_OBS_JWT_ID'', ''_OBS_JWT_SUBJECT'', ''_OBS_JWT_AUDIENCE'' für die JWT-Claims (optional) | * ''_OBS_JWT_ID'', ''_OBS_JWT_SUBJECT'', ''_OBS_JWT_AUDIENCE'' für die JWT-Claims (optional) | ||
* ''_OBS_JWT_CLAIM_<name>'' | * ''_OBS_JWT_CLAIM_<name>'' für beliebige Custom-Claims (z.B. Mandant ''tenant'', Rollen ''roles''); im Folge-Skript lesbar | ||
* ''_OBS_JWT_REFRESH_ID'' (optional) | * ''_OBS_JWT_REFRESH_ID'' (optional) löst die Ausstellung eines Refresh-Tokens aus; ''_OBS_JWT_REFRESH_EXP'' setzt dessen Lebensdauer in Minuten (Default 90 Tage) | ||
zurückliefert. Bei Erfolg signiert der Server einen Token (HS256, Issuer ''OBS REST-Server'', Lebenszeit gemäß JWT-Exp) und antwortet: | zurückliefert. Bei Erfolg signiert der Server einen Token (HS256, Issuer ''OBS REST-Server'', Lebenszeit gemäß JWT-Exp) und antwortet: | ||
{"token": "eyJhbGciOi...", "serverTime": "2026-06-29T15:30:12+02:00"} | {"token": "eyJhbGciOi...", | ||
"accessToken": "eyJhbGciOi...", | |||
"expiresIn": 28800, | |||
"serverTime": "2026-06-29T15:30:12+02:00"} | |||
Zum Aufbau der Antwort: | |||
* ''token'' und ''accessToken'' enthalten '''denselben''' Token. ''accessToken'' ist der Name, den die meisten Client-Bibliotheken erwarten; ''token'' bleibt für bestehende Konsumenten erhalten. | |||
* ''expiresIn'' ist die Gültigkeit in '''Sekunden''' (JWT-Exp × 60). | |||
* '''Alle weiteren Felder, die das Authenticate-Skript zurückgibt, werden mit ausgeliefert''' - mit Ausnahme von ''status'' und den ''_OBS_''-Steuerfeldern. So kann das Skript z.B. eine Benutzer-Id, den Mandanten oder Rollen direkt in die Anmelde-Antwort legen, ohne dass der Client dafür einen zweiten Aufruf braucht. | |||
Beispiel mit Zusatzfeldern aus dem Skript: | |||
{"benutzerId": "4711", "tenant": "nord", "roles": ["TECHNIKER"], | |||
"token": "eyJ...", "accessToken": "eyJ...", "refreshToken": "eyJ...", | |||
"expiresIn": 28800, "serverTime": "2026-06-29T15:30:12+02:00"} | |||
Wird die Anmeldung abgelehnt (''status'' = 9), antwortet der Server mit | |||
'''401 Unauthorized''' und reicht den Text aus dem Feld ''error'' als | |||
''error.message'' an den Client durch - der Konsument kann ihn also direkt | |||
anzeigen. Die Formulierung liegt damit beim Skript. | |||
{{Hinweis|Der durchgereichte Text ist für den Endanwender gedacht und sollte | |||
keine internen Details verraten. „Benutzer oder Passwort ist falsch" ist eine gute | |||
Meldung, „Kein Datensatz in BENUTZ mit b_name=meier" nicht.}} | |||
===Phase 2: Token-Verwendung=== | ===Phase 2: Token-Verwendung=== | ||
| Zeile 101: | Zeile 125: | ||
===Phase 3: Token erneuern (Refresh)=== | ===Phase 3: Token erneuern (Refresh)=== | ||
Soll die App lange ohne Neuanmeldung arbeiten, stellt das Authenticate-Skript | Soll die App lange ohne Neuanmeldung arbeiten, stellt das Authenticate-Skript zusätzlich ein Refresh-Token aus (Feld ''_OBS_JWT_REFRESH_ID''). Zum Erneuern ruft der Client '''denselben JWT-Endpunkt''' auf, aber mit dem Refresh-Token im ''Authorization''-Header: | ||
POST https://api.meinserver.de/oauth/ | POST https://api.meinserver.de/oauth/ | ||
| Zeile 107: | Zeile 131: | ||
Authorization: Bearer eyJhbGciOi... (Refresh-Token) | Authorization: Bearer eyJhbGciOi... (Refresh-Token) | ||
Der Server erkennt am Bearer-Token den Refresh-Fall, verifiziert das Token (Signatur, Ablauf, ''token_use=refresh'') und ruft im selben Skript die Methode '''Refresh''' auf. Das Skript | Der Server erkennt am Bearer-Token den Refresh-Fall, verifiziert das Token (Signatur, Ablauf, ''token_use=refresh'') und ruft im selben Skript die Methode '''Refresh''' auf. Das Skript prüft seine Sperrtabelle, rotiert das Refresh-jti und liefert neue Claims. Der Server antwortet mit einem rotierten Paar ''{"token","refreshToken","serverTime"}''. Ein bereits benutztes (rotiertes) Refresh-Token wird mit 401 abgelehnt. | ||
Details und ein vollständiges Beispiel siehe [[OBS/Kostenpflichtige Module/RESTServer/Scripting|Scripting]]. | Details und ein vollständiges Beispiel siehe [[OBS/Kostenpflichtige Module/RESTServer/Scripting|Scripting]]. | ||
Version vom 17. August 2026, 07:49 Uhr
- A Preise aktualisieren
- C Personen übertragen
- E Kategorien verwalten
- G Kataloge verwalten
- I Merkliste übertragen
- K Varianten übertragen
- L Artikelvarianten übertragen
- M Referenzarten übertragen
- N Lagerbestände verwalten
- U Bestellungen einlesen
- V leere Passworte füllen
- W Update-Informationen zurücksetzen
- X Konfiguration
- Z Protokoll
Zugänge
Ein Zugang (Account) ist der Kanal, über den ein externer Konsument den REST-Server anspricht. Er bündelt API-Key, Host-Beschränkung, CORS-Konfiguration, optionale JWT-Authentifizierung und optionale mTLS-Subject-Prüfung.
Aufruf
Stammdaten -> Z Weitere Stammdaten -> REST-Server -> Zugänge
Felder
| Feld | Beschreibung |
|---|---|
| Name | Sprechender Name, erscheint im Protokoll und der Statistik |
| Aktiv | Inaktive Zugänge werden bei der Authentifizierung mit 403 abgewiesen |
| API-Key | Eindeutiger Schlüssel, den der Konsument im HTTP-Header apikey senden muss. Per Schlüsselsymbol generierbar |
| Host | Optionaler Hostname oder IP-Adresse des Konsumenten. Stimmt die anfragende IP nicht überein, wird mit 403 abgelehnt |
| CORS-Origins | Liste erlaubter Origins für Browser-Aufrufe, mehrere durch ; getrennt |
| mTLS-Subject | Erwartete RDN-Komponente(n) im Client-Zertifikat-Subject, Komma-/Semikolon-getrennt (alle müssen vorkommen; nur sinnvoll bei mTLS-Servern) |
| JWT | Schalter, ob für diesen Zugang ein JWT-Token nötig ist |
| JWT-Endpunkt | Pfad, über den der JWT-Token bezogen wird (z.B. oauth oder meineapp/dev/auth/login). Verglichen wird der vollständige Pfad, mehrstufige Angaben mit / sind also erlaubt |
| JWT-Key | Geheimer Schlüssel zur Signierung/Verifikation, per Schlüsselsymbol generierbar |
| JWT-Exp | Gültigkeitsdauer eines ausgestellten Access-Tokens in Minuten. Derselbe Wert wird dem Konsumenten in der Token-Antwort als expiresIn in Sekunden mitgeteilt und steht dem Endpunkt-Skript als _OBS_JWT_EXP_SEC zur Verfügung |
API-Key
Der API-Key ist die Basis-Authentifizierung. Der Client übergibt ihn im HTTP-Header:
apikey: abcd-efgh-ijkl-mnop-qrstuvwxyz12
Wird ein Aufruf ohne apikey-Header gestellt, sucht der Server intern nach einem Zugang mit API-Key * - damit lassen sich (mit Vorsicht!) auch öffentliche Endpunkte ohne Authentifizierung realisieren.
Host-Beschränkung
Ist im Feld Host ein Wert hinterlegt, akzeptiert der Server Anfragen unter diesem Zugang nur dann, wenn die Remote-IP exakt diesem Host entspricht oder durch DNS-Auflösung auf diese IP zeigt. DNS-Auflösungen werden 5 Minuten gecached.
CORS
Soll ein Endpunkt aus einer Browser-Anwendung (Single-Page-App, JavaScript) heraus aufgerufen werden, muss das Origin der Anwendung in der CORS-Liste des Zugangs eingetragen sein.
Format der Liste:
https://app.kunde.de;https://test.kunde.de;https://localhost:8080
Ablauf:
- Browser sendet einen Preflight-Request (OPTIONS). Der REST-Server prüft das Origin gegen die globale Origin-Liste aller Accounts (Cache, TTL 60s) und antwortet mit 204 + CORS-Header, falls erlaubt.
- Der eigentliche Request wird gegen die Origin-Liste des aktuell verwendeten Zugangs geprüft.
- Stimmt das Origin nicht, wird mit 403 Origin nicht erlaubt für Account ... abgelehnt.
Erlaubte Methoden: GET, POST, PUT, DELETE, PATCH. Erlaubte Header: Content-Type, apikey, Authorization, Accept. Diese Werte sind systemseitig festgelegt und werden in der Preflight-Antwort zurückgegeben.
JWT-Authentifizierung
Wird ein Zugang mit aktiver JWT-Pflicht aufgerufen, sind die folgenden Phasen zu durchlaufen (Phase 3 nur bei genutztem Refresh):
Phase 1: Token-Bezug
Der Konsument ruft den JWT-Endpunkt des Zugangs auf, z.B.:
POST https://api.meinserver.de/oauth/ Header: apikey: [API-KEY] Body: {"username":"...", "password":"..."}
Der Server führt das im Zugang hinterlegte Authentifizierungs-Skript aus (F7 in der Zugangs-Liste). Das Skript muss eine Funktion Authenticate bereitstellen, die ein JSON-Objekt mit:
- status = 1 bei Erfolg, 9 bei Misserfolg
- error = Fehlertext (bei status=9)
- _OBS_JWT_ID, _OBS_JWT_SUBJECT, _OBS_JWT_AUDIENCE für die JWT-Claims (optional)
- _OBS_JWT_CLAIM_<name> für beliebige Custom-Claims (z.B. Mandant tenant, Rollen roles); im Folge-Skript lesbar
- _OBS_JWT_REFRESH_ID (optional) löst die Ausstellung eines Refresh-Tokens aus; _OBS_JWT_REFRESH_EXP setzt dessen Lebensdauer in Minuten (Default 90 Tage)
zurückliefert. Bei Erfolg signiert der Server einen Token (HS256, Issuer OBS REST-Server, Lebenszeit gemäß JWT-Exp) und antwortet:
{"token": "eyJhbGciOi...",
"accessToken": "eyJhbGciOi...",
"expiresIn": 28800,
"serverTime": "2026-06-29T15:30:12+02:00"}
Zum Aufbau der Antwort:
- token und accessToken enthalten denselben Token. accessToken ist der Name, den die meisten Client-Bibliotheken erwarten; token bleibt für bestehende Konsumenten erhalten.
- expiresIn ist die Gültigkeit in Sekunden (JWT-Exp × 60).
- Alle weiteren Felder, die das Authenticate-Skript zurückgibt, werden mit ausgeliefert - mit Ausnahme von status und den _OBS_-Steuerfeldern. So kann das Skript z.B. eine Benutzer-Id, den Mandanten oder Rollen direkt in die Anmelde-Antwort legen, ohne dass der Client dafür einen zweiten Aufruf braucht.
Beispiel mit Zusatzfeldern aus dem Skript:
{"benutzerId": "4711", "tenant": "nord", "roles": ["TECHNIKER"],
"token": "eyJ...", "accessToken": "eyJ...", "refreshToken": "eyJ...",
"expiresIn": 28800, "serverTime": "2026-06-29T15:30:12+02:00"}
Wird die Anmeldung abgelehnt (status = 9), antwortet der Server mit 401 Unauthorized und reicht den Text aus dem Feld error als error.message an den Client durch - der Konsument kann ihn also direkt anzeigen. Die Formulierung liegt damit beim Skript.
Phase 2: Token-Verwendung
Bei allen folgenden Aufrufen sendet der Client den Token im Authorization-Header:
GET https://api.meinserver.de/kalender/v1 Header: apikey: [API-KEY] Authorization: Bearer eyJhbGciOi...
Der Server verifiziert Signatur, Issuer, Ausstellzeitpunkt und Ablauf (Toleranz: 20 Sekunden Clock-Skew). Im Skript stehen die Claims als Parameter _OBS_JWT_ID, _OBS_JWT_SUBJECT und _OBS_JWT_AUDIENCE zur Verfügung.
Phase 3: Token erneuern (Refresh)
Soll die App lange ohne Neuanmeldung arbeiten, stellt das Authenticate-Skript zusätzlich ein Refresh-Token aus (Feld _OBS_JWT_REFRESH_ID). Zum Erneuern ruft der Client denselben JWT-Endpunkt auf, aber mit dem Refresh-Token im Authorization-Header:
POST https://api.meinserver.de/oauth/ Header: apikey: [API-KEY] Authorization: Bearer eyJhbGciOi... (Refresh-Token)
Der Server erkennt am Bearer-Token den Refresh-Fall, verifiziert das Token (Signatur, Ablauf, token_use=refresh) und ruft im selben Skript die Methode Refresh auf. Das Skript prüft seine Sperrtabelle, rotiert das Refresh-jti und liefert neue Claims. Der Server antwortet mit einem rotierten Paar {"token","refreshToken","serverTime"}. Ein bereits benutztes (rotiertes) Refresh-Token wird mit 401 abgelehnt. Details und ein vollständiges Beispiel siehe Scripting.
mTLS-Subject-Prüfung
Wird im Feld mTLS-Subject ein Wert hinterlegt, prüft der Server zusätzlich zur Standard-mTLS-Validierung, ob der Subject-DN des (CA-validierten) Client-Zertifikats die hinterlegten RDN-Komponenten enthält. Verglichen wird jede Komponente vollständig (kein Substring-Match) und case-insensitiv; mehrere Komponenten werden durch Komma oder Semikolon getrennt und müssen alle im Subject vorkommen.
Beispiele für Subject-Strings im Cert (OneLine-DN):
/C=DE/O=Kunde GmbH/CN=client-prod.kunde.de /C=DE/O=Kunde GmbH/CN=client-test.kunde.de
Mit CN=client-prod.kunde.de im Feld wird genau das prod-Cert akzeptiert, das test-Cert mit 401 abgewiesen. Da jede RDN-Komponente vollständig verglichen wird, matcht z.B. CN=client1 nicht fälschlich auch CN=client10. Mehrere Bedingungen lassen sich mit Komma/Semikolon kombinieren, z.B. O=Kunde GmbH;CN=client-prod.kunde.de.
Listen-Funktionen
In der Zugänge-Liste (allein):
- Einfg - neuer Zugang
- Return - Zugang bearbeiten
- F4 - Sortierung
- F7 - JWT-Authentifizierungs-Skript bearbeiten
- F8 - Statistik für den Zugang
Aus der Endpunkt-Liste via F6 geöffnet (Auswahl für Berechtigung):
- F2 - markierte Zugänge dem Endpunkt zuordnen
- F5 - Zugang in der Liste markieren / Markierung aufheben