Open Source Dokumentenmanagement
Dokumentation

Durchsuchbare Dokumentation aufrufen | Zurück zur Dokumentationsübersicht

Navigation: Dokumentationen agorum core > Übersicht tags


JavaScript-Bibliothek common/jwt

Ab welcher Version verfügbar?
agorum core 9.0.7

Dieses Modul bietet Funktionen für die Erstellung und Dekodierung von JWT (JSON Web Tokens).

JWT können genutzt werden, um Zugriff auf bestimmte Services zu gewähren, ohne zuvor ein Login machen zu müssen. Die häufigste Verwendung ist folgende:

Die Signaturmethode ist HS256.

Im HTTP-Header wird solch ein Token in der Regel folgendermaßen mitgegeben:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Hinweis:  Im Moment ist ein JWT nur in custom-services nutzbar. Dies wird sich in Zukunft auf alle agorum core API Services ausweiten.

Verwendung


let jwt = require('common/jwt');

Funktionen


create

Diese Funktion erstellt ein neues Token.


Aufruf

let token = jwt.create(id, issuer, subject, audience, secret, ttlMillis);


Parameter

Letztendlich handelt es sich um diverse Zeichenketten, bei denen man an sich beliebige Werte hinterlegen kann.

Definition: https://de.wikipedia.org/wiki/JSON_Web_Token

Parameter Beschreibung
id Eine eindeutige case-sensitive Zeichenfolge, welche das Token eindeutig identifiziert. Hiermit kann verhindert werden, dass das Token mehrfach verwendet wird. Hierbei kann es sich etwa um eine durchgezählte Nummer, einen GUID oder einen Hashwert handeln.
issuer Der Aussteller des Tokens, z. B. agorum core oder der Benutzer, der es ausstellt.
subject Definiert, für welches Subjekt die Claims gelten. Das Feld definiert also, für wen oder was die Claims getätigt werden.
audience (optional) Die Zieldomäne, für die das Token ausgestellt wurde. Letztendlich kann hier ein String hinterlegt werden, um für einen Benutzer für verschiedene Bereiche / Systeme  die Gültigkeit zu bestimmen.
secret Ein geheimer Schlüssel, der zur digitalen Unterschrift der Informationen dient. Dieser Schlüssel muss geheim bleiben. Mit diesem Schlüssel wird die Nachricht auch mit der Funktion "decode" wieder dekodiert. Der Schlüssel sollte eine gewisse Länge besitzen, je länger, desto sicherer.

Empfehlung
Mindestens 32 Zeichen
ttlMillis time-to-live in Millisekunden. Wird ein Wert > 0 eingetragen, so erhält das Token eine Gültigkeitsdauer, die durch die Angabe von ttlMillis definiert ist. Wird 0 mitgegeben, so ist die Gültigkeit unendlich.


Beispiel

let jwt = require('common/jwt');
let token = jwt.create('id1', 'issuer1', 'subject1', 'audience1', 'mytopsecret01', 60000);

// Rückgabe des Tokens
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJpZDEiLCJpYXQiOjE1NTYxMTQxOTgsInN1YiI6InN1YmplY3QxIiwiYXVkIjoiYXVkaWVuY2UxIiwiaXNzIjoiaXNzdWVyMSIsImV4cCI6MTU1NjExNDI1OH0.8xhmXc9IEZ5AQ_jO95hm4EGWV-PUIti4Lub9DZyX0b0

Mit dem folgenden Skript lassen Sie das System die Authentifizierung mittels JSON Web Token prüfen. Anschließend verarbeitet das System die Parameter und die hochgeladene Datei.

/* global sc, request */
//imports
let objects = require('common/objects');
let metadb = require('common/metadb');
let jwt = require('common/jwt');

// Java imports
/* Konvertiert einen Stream in einen lesbaren String
 * 
 * Param InputStream <is>:   Der input stream, der die Daten enthält
 * Param String <encoding>:  Die Codierung des Streams. Meistens 'UTF-8'
 */
let streamToString = Packages.agorum.commons.string.StringConverterUtils.toString; // (InputStream is, String encoding)
/* [Singleton] Objekt, das den Zugriff auf die Agorum-Protokolle und -Statistiken ermöglicht
 * 
 * [static] instance: Die Instanz des Singleton-Objekts
 *
 * function <error>, Params: (String error) - loggt einen Fehler in den Server-Log
 */
let scs = Packages.agorum.roi.statistic.SessionControllerStatistic;

// der Pre-Shared Key wird in der MetaDb gespeichert
const encryptedPSK = metadb.read('MAIN_MODULE_MANAGEMENT/customers/agorum/secret');

/* metaddb.decrypt ist verfügbar für agorum [Internal] 9.5.3 oder [External] 10.0.
 * bei älteren Versionen kommentieren Sie die Nachfolgende Zeile aus [HACK!]
 */
//metadb.decrypt = (encrypted) => Packages.agorum.roi.ejb.common.CryptKeyController().tryDecrypt(encrypted, 'metadb', sc);

/* Diese Funktion prüft das vom Client gesendete Autorisierungs-Token.
 *
 * Param <authToken>:     Das vom Client gesendete Autorisierungs-Token.
 * 
 * Param <psk>:           Der verschlüsselte Pre-Shared Key.
 * 
 * Param <logFailedAuth>: Wenn dieser Parameter auf einen <truthy>-Wert gesetzt ist, wird jede fehlgeschlagene Authentifizierungsanfrage 
 *                        protokolliert, sodass sie in den Agorum-Serverprotokollen sichtbar ist.
 *                        
 * Rückkgabe: 
 */
let checkAuth = (authToken, psk, logFailedAuth) => {
  try {
    return jwt.decode(authToken.slice(7), metadb.decrypt(psk));
  }
  catch (ex) {
    if (logFailedAuth) {
      
      scs.instance.error(ex);
    }
    
    return null;
  }
};

let saveFile = (file, target) => {
  return objects.create('file', {
    name: file.parameters.filename,
    content: file.stream,
    target: target || objects.find('home:MyFiles')
  });
};


/* <request.header> is an JSON Object, containing all html headers as key-value pairs.
 * The values are always represented as array of values.
 */

// Prüfen, ob der Client ein Autorisierungs-Token sendet
if (!request.header.authorization || !request.header.authorization[0]) {
  throw new Error('401 - Authorization needed');
}

// Validiert das Autorisierungs-Token
// authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJpZDEiLCJpYXQiOjE2MDgyODU5MDksInN1YiI6ImtvcHAuZG9jdW1lbnQudXBsb2FkIiwiYXVkIjoia29wcCIsImlzcyI6ImFnb3J1bSJ9.fNRGid5q_AO5sgZwkXkApXzDavxAUlrp-iRYEHE5n0E
let claim = checkAuth(request.header.authorization[0], encryptedPSK, true);
if (!claim) {
  throw new Error('403 - Authorization failed');
}

// Sie können das Auth-Token jetzt validieren. Der Ablauf wird von der Decode-Funktion geprüft!
/* claim: {
 *   id: '<ID>',                   // Eindeutige Kennung
 * 
 *   issuedAt '<Date>',            // Zeitstempel der Ausgabe
 *   expiration: null | '<Date>',  // Zeitstempel des Ablaufs
 * 
 *   issuer: 'agorum',             // Wer den Token ausgegeben hat
 *   audience: '<audience>', 
 *   subject: '<subject>',
 * }
 */

// Starten
/* Definierte input-Parameter:
 * File <file>:             Datei, die hochgeladen wird
 * String <type>:           String, der den Dateityp beschreibt
 * String <year>:           Jahr, in dem die Dateien erstellt wurden
 * String <customerNumber>: Nummer des Kunden, der die Datei gesendet hat
 */
/* Jeder Anhang hat die folgenden Eigenschaften:
 * {
 *   stream:        // Wert | stream
 *   type:          // mime-Type des Streams
 *   parameters: {  // Eigenschaften des Anhangs
 *     name:        //Name der Eigenschaft
 *     [filename]:  //Nur bei der Eigenschaft'FILE': originaler Dateiname
 *       ...?
 *   }
 * }
 */
let attachments = request.attachments;
// Wenn Sie den URL-Parameter anstelle des HTML-Bodys verwenden
// let urlParams = request.params;
let file, params = {};

request.attachments.forEach(attachment => {
  if (attachment.parameters.filename) {
    // wenn 'fileName' definiert ist, muss der Parameter die Upload-Datei sein!
    file = attachment;
  }
  else {
    //  Wenn 'filename' nicht vorhanden ist, kann der Parameter-Stream ausgelesen werden, um seinen Wert zu erhalten
    params[attachment.parameters.name] = streamToString(attachment.stream, 'UTF-8');
  }
});

// Prüfen, ob etwas hochgeladen wurde
if (!file) {
  throw new Error('400 - Missing upload file');
} 
file = saveFile(file /*, '/roi/agorum/path/to/upload/dir' */);  

// Platz für zusätzliche Code:
//
// Vielleicht möchten Sie hier einen Workflow starten oder etwas anderes mit dieser Datei machen...
//


// Sie können die Urls angeben, unter denen Ihre Datei in agorum zu finden ist
// SSO:      Single Sign on - wenn aktiviert, können Sie direkt zu dieser Datei navigieren
// Redirect: Wenn es kein SSO gibt, gelangt der Benutzer auf eine Anmeldeseite und wird zur Datei weitergeleitet,
//            wenn er sich erfolgreich authentifiziert hat (per Cookie, per Passwort, per was auch immer)
let domain = 'https://your.agorum';
let ssoPath = 'https://agorumdocproxy.agorum.com/api/rest/object/embed/' + file.UUID;
let redirectPath = '/roiwebui/home_module/?url=' + encodeURIComponent(ssoPath);

// Schließlich wird das Ergebnis der Anfrage zurückgegeben
({
  uuid: file.UUID,
  sso_url: domain + ssoPath,
  red_url: domain + redirectPath
});

decode

Diese Funktion dekodiert ein Token und prüft, ob die Signatur noch gültig ist.

Aufruf

let claim = jwt.decode(token, secret);

// Rückgabe
{
  "audience" : "audience1",
  "subject" : "subject1",
  "expiration" : null,
  "issuedAt" : "2019-04-24T14:24:57.000Z",
  "id" : "id1",
  "issuer" : "issuer1"
}


Parameter

Parameter Beschreibung
token Angabe des JWT.
secretz Angabe des Secret-Keys (siehe create)

 

Return

Es werden die Werte zurückgegeben, die bei der Erstellung definiert wurden


Exceptions

Das nachfolgende Skript ist eine Klasse, die ein Token erstellt und dieses im Anschluss direkt entschlüsselt. Damit wird ebenfalls geprüft, ob die angegebenen Daten (vornehmlich das Secret) auch funktionieren. Die Ausgabe zeigt, was alles im JWT codiert ist und wie das Token am Ende aussieht.

let jwt = require('common/jwt');

let createAndShow = () => {
  let result = {
    psk: 'ep1$a/&E0u8|=QuL?5V=yrS-SLQG$G2q',
    id: 'cb63dc3d-fb17-4a04-8a10-69eeb0552016',
    issuer: 'agorum',
    subject: 'customer.document.upload',
    audience: 'customer',
    ttl: 100000
  };
  
  /*
 * Param <id>:        asd
 * Param <issuer>:    asd
 * Param <subject>:   asd
 * Param <audience>:  asd
 * Param <secret>:    secret Schlüssel, mindestens 32 Zeichen lang (alphanumerische Zeichen and Sonderzeichen erlaubt)                 
 * Param <ttlMillis>: 0 = unendlich; 1+ = TTL in Millisekunden
 */
  result.token = jwt.create(result.id, result.issuer, result.subject, result.audience, result.psk, result.ttl);
  
/*
 * Param <token>:        asd
 * Param <secret>:    secret Schlüssel, mindestens 32 Zeichen lang (alphanumerische Zeichen und Sonderzeichen erlaubt)                 
 */
  result.decrypted = jwt.decode(result.token, result.psk);
  
  return result;
};

createAndShow();