Identityprovider-App

Identityprovider-App

 Technische Hintergrundinformationen

 Abhängigkeiten

Um d.ecs identity provider per API verwenden zu können, sind folgende Komponenten erforderlich:

  • d.ecs http gateway
  • d.ecs shell
  • d.ecs jstore Version 1.1.0 oder höher

 Einleitung

In diesem Thema findest Du allgemeine Informationen zu d.ecs identity provider, dem Funktionsumfang, sowie zu den Kenntnissen, die hilfreich beim Programmieren von Funktionen und Anpassungen sind.

 Über die d.ecs identity provider-API

Diese Dokumentation beschreibt die einzelnen Schnittstellen, die es Euch ermöglichen, angepasste Funktionen (Customizing) für die Authentifizierung zu erstellen.

 Funktionsumfang von d.ecs identity provider

d.ecs identity provider authentifiziert Benutzer über verschiedene Authentifizierungsverfahren wie z.B. die Windows-Authentifizierung (Kerberos), mit Benutzername und Passwort gegen unterschiedliche Systeme zur Benutzerverwaltung, wie z.B. Active Directory (AD) und stellt hierüber hinaus die erforderlichen Anwenderinformationen für die Nutzung durch andere Apps bereit. Außerdem wird eine API-Schnittstelle bereitgestellt, die die Authentifizierung für Apps erlaubt.

 Verwenden der API-Funktionen

d.ecs identity provider stellt eine API-Schnittstelle zur Verfügung, die es Euch unter anderem erlaubt, folgende Aktionen auszuführen:

 Hintergrund: Ablauf der Authentifizierung und Autorisierung

authentication-and-authorization-flowchart

 Anmelden (Login)

Diese Funktion führt eine Anmeldung aus einer App heraus durch.

Führt einen Redirect auf die URI /identityprovider/login aus und beachtet hierbei, dass die URI in Kleinbuchstaben zu verwenden ist.

Diese Ressource hat einen gültigen Query-Parameter:

ParameterBeschreibung
redirectDies ist ein Pflichtparameter, der den Pfad und Query-Anteil der eigenen URI enthält. Sofern die Anmeldung erfolgreich war, führt d.ecs identity provider einen Redirect zurück an diese URI durch.

Wichtig

Beim Aufruf der URI /identityprovider/login wählt d.ecs identity provider je nach Konfiguration eine beliebige Authentifizierungsmethode. Apps dürfen diese URI daher nicht selbst aufrufen, sondern müssen dies dem Browser überlassen. Client-Anwendungen, die sich anmelden möchten, müssen ein Browser-Control verwenden und eine Redirect-URI definieren, die sie im Browser-Control verwenden können.

Wenn lediglich ein API-Key zur Anmeldung verwendet wird, darf die URI /identityprovider/login?basic=true verwendet werden. Der API-Key wird in diesem Fall als Passwort übergeben.

 Validieren der Anmeldung (Validate)

Ihr könnt mit der Funktion validate prüfen, ob eine Anfrage eine korrekte Anmeldung enthält.

Validieren der erforderlichen Informationen

  • Prüft, ob der Authorization-Header festgelegt ist
  • Prüft, ob der Header ein Bearer-Token enthält
  • Wenn kein Bearer-Token vorhanden ist, prüft zusätzlich, ob das Cookie AuthSessionId vorhanden ist.
  • Ist beides nicht vorhanden, führt aus der App ein Redirect auf die Login-Ressource durch.

Anfrage


GET /identityprovider/validate[?allowExternalValidation=true]

Mit der Angabe von allowExternalValidation=true als Query-Parameter weist Ihr d.ecs identity provider an, auch externe Benutzer zu überprüfen, die nicht über die Provider von d.ecs identity provider bekannt sind, sich aber via OpenID Connect (alternative Anmeldung) angemeldet haben. Das zurückgegebene SCIM-user-Objekt enthält für externe Benutzern lediglich eine E-Mail-Adresse und die besondere Gruppenzugehörigkeit zur Gruppe External User mit der ID 3E093BE5-CCCE-435D-99F8-544656B98681.

HTTP-StatusBeschreibung
401 UnauthorizedUngültiges Token oder kein Token übertragen
403 ForbiddenGültiges Token eines externen Benutzers: allowExternalValidation=false
200 OKGültiges Token
Content-Type: application/hal+json
<SCIM-user-Objekt im Json-Format>

Mit dieser GET-Anfrage wird der Inhalt des Tokens oder Cookies wahlweise als Token oder Cookie an d.ecs identity provider übermittelt. Ob der Inhalt als Bearer-Token oder Cookie weitergegeben wird, hängt nicht davon ab, wie es ursprünglich empfangen wurde. Ihr könnt als Entwickler Eure persönliche Präferenz im Code definieren.

Damit nicht bei jeder Anfrage (Request) der Validate-Endpunkt aufgerufen werden muss, solltt Ihr die Antwort zwischenspeichern.

Verwendet für das Caching das SCIM-user-Objekt. d.ecs identity provider sendet einen CacheControl-Header und legt darin mit MaxAge die Gültigkeitsdauer fest.

Diese Angabe sollte dazu verwendet werden, die Antwort für die angegebene Anzahl Sekunden zu speichern. Innerhalb dieser Zeit kann auf ein erneutes Aufruf von Validate verzichtet werden, wenn die AuthSessionId einer Anfrage 1:1 mit der gespeicherten AuthSessionId übereinstimmt.

Hinweis

Ein Benutzer kann mehrere Sitzungen gleichzeitig haben, bei denen die AuthSessionId unterschiedlich ist. Wird jeweils das SCIM-user-Objekt zur AuthSessionId gespeichert, liegt dieses eventuell mehrfach im Speicher vor. Wenn dies optimiert werden soll, kann der Teil vor dem kaufmännischen Und-Zeichen (&) als Schlüssel für das SCIM-user-Objekt verwendet werden.

 Verwenden des SCIM-Protokolls zur Authentifizierung

Zur weiteren Authentifizierung und für weitere Funktionen stellt d.ecs identity provider die Informationen über Benutzer nach dem SCIM-Protokoll (Protokollversion 1.0 und 1.1) bereit. Das System for Cross-domain Identity Management-Protokoll (SCIM) ist ein herstellerunabhängiger Standard und wurde entwickelt, um die Benutzer-Authentifizierung in cloudbasierten Anwendungen und Diensten zu vereinfachen.

Hinweis

Weitere Informationen zum SCIM-Protokoll findet Ihr unter den folgenden Adressen: https://tools.ietf.org/html/rfc7643 bzw. https://tools.ietf.org/html/rfc7644 (jeweils in englischer Sprache).

Die Basisadresse (URI) des SCIM-Protokolls lautet /identityprovider/scim. Prüft zunächst, ob ein gültiges Token übergeben wurde, um auf die SCIM-Ressource zugreifen zu können.

Unter der genannten URI stellt d.ecs identity provider die beiden SCIM-Ressourcen Users und Groups nur lesend bereit.

Übersicht der verfügbaren Routen:

RouteBeschreibung
/identityprovider/scim/UsersListet die Benutzer aller Benutzerprovider auf.
/identityprovider/scim/Users/{id}Listet einen spezifischen Benutzer auf. Definiert durch den Parameter id.
/identityprovider/scim/GroupsListet die Gruppen aller Benutzerprovider auf.
/identityprovider/scim/Groups/{id}Listet eine spezifische Gruppe auf. Definiert durch den Parameter id.

Folgende Attribute eines SCIM-user-Objekts werden unterstützt und können für eine Filterung verwendet werden:

  • Id
  • UserName
  • Name (familyName, givenName)
  • DisplayName
  • Title
  • Locale
  • PreferredLanguage
  • Emails (value, display)
  • PhoneNumbers (value, display)
  • Groups (value, display)
  • Photos (value, display)

Folgende Attribute eines SCIM-group-Objekts werden unterstützt und können für eine Filterung verwendet werden:

  • Id
  • DisplayName
  • Members (value, display)

Folgende SCIM-Operatoren werden für die Filterung unterstützt:

  • equals (eq)
  • contains (co)
  • startsWith (sw)

Das seitenweise Abrufen (Paging) von SCIM-Objekten wird unterstützt. Weitere Informationen kannst Du der Definition des SCIM-Protokolls entnehmen.

Beispiel einer Abfrage von Benutzern inklusive Filterung


GET /identityprovider/scim/Users?filter=DisplayName%20co%20Doe
Host: example.com


HTTP/1.1 200 OK
Content-Type: application/json

{
    "schema": "urn:scim:schemas:core:1.0",
    "totalResults": 37,
    "itemsPerPage": 37,
    "startIndex": 1,
    "resources": [
        {
            "id": "2b04bb70-10fa-4892-a62a-97627f0fc67f",
            "userName": "example\\doej",
            "name": {
                "familyName": "Doe",
                "givenName": "John"
            },
            "displayName": "Doe, John",
            "title": "Senior Support Specialist",
            "emails": [
                {
                    "value": "doej@example.org"
                }
            ],
            "phoneNumbers": [
                {
                    "value": "+1-234-56789-123456789"
                }
            ],
            "photos": [
                {
                    "value": "/identityprovider/scim/photo/2b04bb70-10fa-4892-a62a-97627f0fc67f"
                }
            ]
        },
        ...
    ]
}

Hinweis

info Wenn du mehrere Benutzer abrufst, werden die Informationen zu den Gruppen, in denen diese Benutzer Mitglieder sind, nicht übertragen. Um die Gruppen zu ermitteln, in denen ein Benutzer Mitglied ist, rufe die Informationen des entsprechenden Benutzers ab.

Beispiel einer Abfrage eines spezifischen Benutzers


GET /identityprovider/scim/Users/2b04bb70-10fa-4892-a62a-97627f0fc67f
Host: example.com


HTTP/1.1 200 OK
Content-Type: application/json

{
    "id": "2b04bb70-10fa-4892-a62a-97627f0fc67f",
    "userName": "example\\doej",
    "name": {
        "familyName": "Doe",
        "givenName": "John"
    },
    "displayName": "Doe, John",
    "title": "Senior Support Specialist",
    "emails": [
        {
            "value": "doej@example.org"
        }
    ],
    "phoneNumbers": [
        {
            "value": "+1-234-56789-123456789"
        }
    ],
    "groups": [
        {
            "value": "972a75b9-15fb-4a66-a3cf-299e29ac6f25",
            "display": "Development Department"
        }
    ],
    "photos": [
        {
            "value": "/identityprovider/scim/photo/2b04bb70-10fa-4892-a62a-97627f0fc67f"
        }
    ]
}

Einige Benutzerprovider liefern mehr Informationen, als das SCIM-Protokoll vorsieht, z.B. der LDAP-Benutzerprovider.
Um alle Informationen eines Benutzer zu erhalten füge dem Aufruf der SCIM-Ressource eines spezifischen Benutzers den Parameter detailLevel=1 hinzu. Durch das Hinzufügen des Parameters detailLevel=1 wird das SCIM-user-Objekt um die Eigenschaft details erweitert. Diese Eigenschaft kann nicht zur Filterung verwendet werden.

Beispiel einer Abfrage eines spezifischen Benutzers mit allen verfügbaren Informationen


GET /identityprovider/scim/Users/2b04bb70-10fa-4892-a62a-97627f0fc67f?detailLevel=1
Host: example.com


HTTP/1.1 200 OK
Content-Type: application/json

{
    "id": "2b04bb70-10fa-4892-a62a-97627f0fc67f",
    "userName": "example\\doej",
    ...,
    "details": [
        {
            "key": "distinguishedname",
            "values": [
                "CN=John Doe,DC=example,DC=org"
            ]
        },
        {
            "key": "givenname",
            "values": [
                "Doe"
            ]
        },
        {
            "key": "sn",
            "values": [
                "John"
            ]
        },
        {
            "key": "displayname",
            "values": [
                "Doe, John"
            ]
        },
        ...
    ]
}

Beispiel einer Abfrage der Gruppen


GET /identityprovider/scim/Groups/
Host: example.com


HTTP/1.1 200 OK
Content-Type: application/json

{
    "schema": "urn:scim:schemas:core:1.0",
    "totalResults": 81,
    "itemsPerPage": 81,
    "startIndex": 1,
    "resources": [
        {
            "id": "0e36c878-4d17-44c5-9eac-9ad8cb03274a",
            "displayName": "Administrators"
        },
        {
            "id": "3399e87a-32d5-4db5-a934-1c3e285d7e71",
            "displayName": "Development Department"
        },
        ...
    ]
}

Hinweis

info Wenn du mehrere Gruppen abrufst, werden die Mitglieder der einzelnen Gruppen nicht übertragen. Um die Mitglieder einer Gruppe zu ermitteln, rufe die Informationen der entsprechenden Gruppe ab.

Beispiel einer Abfrage einer spezifischen Gruppe


GET /identityprovider/scim/Groups/3399e87a-32d5-4db5-a934-1c3e285d7e71
Host: example.com


HTTP/1.1 200 OK
Content-Type: application/json

{
    "id": "3399e87a-32d5-4db5-a934-1c3e285d7e71",
    "displayName": "Development Department",
    "members": [
        {
            "value": "3daa0360-8f02-4bde-bfd3-ac8f21e38107",
            "display": "Diaz, Sanford"
        },
        {
            "value": "4779fd78-f6de-4110-9b11-6e91195754e0",
            "display": "Larson, Priscilla"
        },
        {
            "value": "7e513868-1edc-446a-a042-83428addd663",
            "display": "Sanchez, Jessie"
        },
        ...
    ]
}

 Authentifizieren einer App mit Anfragen außerhalb des Benutzerkontextes

Apps können sich auch authentifizieren, wenn Anfragen (Requests) erforderlich sind, die nicht im Kontext eines angemeldeten Benutzers stattfinden (z.B. Verarbeitung im Hintergrund). Generiert in diesem Fall einen API-Key in d.ecs identity provider und verwendet diesen, um eine Session-ID mithilfe von d.ecs identity provider zu generieren.

Die Authentifizierung mithilfe des API-Keys erfolgt zunächst über ein Bearer-Token. Verwendet den API-Key im Authorization-Header mit dem Schema Bearer und legt den Accept-Header auf application/json fest. Die Login-Route von d.ecs identity provider gibt dann ein DTO mit den Werten AuthSessionId und Expire zurück. Ihr könnt die ID AuthSessionId bis zum angegebenen Ablaufdatum im Expire-Wert zur weiteren Authentifizierung verwenden. Befüllt zu diesem Zweck den Authorization-Header mit dem Schema Bearer mit der zuvor zurückgegebenen ID für den AuthSessionId-Wert und ruft die gewünschte Ressource auf.

Beispiel in C#


using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;

private class AuthSessionInfoDto
{
    public string AuthSessionId { get; set; }
    public DateTime Expire { get; set; }
}

public static void AuthTest()
{
    string apiKey = "<API key>";
    string baseUri = "https://<baseUri>";
    using (HttpClient client = new HttpClient() {BaseAddress = new Uri(baseUri)})
    {
        //Use d.ecs identity provider to convert the API key into an AuthSessionID
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
        client.DefaultRequestHeaders.Add("Accept", "application/json");
        HttpResponseMessage result = client.GetAsync("/identityprovider/login").Result;
        result.EnsureSuccessStatusCode();
        AuthSessionInfoDto authSessionInfo = JsonConvert.DeserializeObject<AuthSessionInfoDto>(result.Content.ReadAsStringAsync().Result);

        //Set the AuthSessionID as a bearer token and retrieve the resource
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authSessionInfo.AuthSessionId);
        client.DefaultRequestHeaders.Add("Accept", "application/hal+json");
        result = client.GetAsync("/exampleapp").Result;
        result.EnsureSuccessStatusCode();
        string content = result.Content.ReadAsStringAsync().Result;
        Console.WriteLine(content);
    }
}

 Inter-App-Kommunikation mit App-Sessions

Apps können eine AuthSessionId anfordern mit der sie sich bei anderen Apps authentifizieren können. Dazu sendet eine App per HTTP-POST ein Json-Objekt mit folgenden Werten an den Endpunkt /identityprovider/appsession:

ParameterBeschreibung
appnameName der App
callbackPfad der URI an den die AuthSessionId gesendet werden soll (relative URI)
requestidAnfrage-ID, um zu prüfen, dass die POST-Anfrage von dieser Anfrage (Request) stammt

Wichtig

Alle drei Parameter müssen übermittelt werden und die Callback-URI muss mit einem Schrägstrich gefolgt vom Namen der App beginnen.

Beispiel


POST /identityprovider/appsession
Host: example.com
Content-Type: application/json

{
    "appname": "exampleapp",
    "callback": "/exampleapp/appsessioncallback",
    "requestid": "UbWN..."
}

Bevor die App auf die Anfrage (Request) antwortet, ruft sie die angegebene Callback-URI auf und übermittelt ein Json-Objekt mit den folgenden Werten:

ParameterBeschreibung
authSessionIdAuthSessionId für die App
expireAblaufdatum der AuthSessionId im ISO 8601-Format
signSignatur über die Parameter appname, authSessionId, expire und requestid
Format: Hex(Sha256(<appname><authSessionId><expire><requestid>))

Hinweis

Die requestid sollte eine 256-bit Zufallszahl sein. Zur Generierung sollte ein kryptographisch sicherer Zufallszahlengenerator verwendet werden.

info: Der HTTP-Statuscode, der beim Aufruf der Callback-URI zurückgegeben wird, wird auch bei der Antwort der App-Session-Anfrage zurückgegeben.

Beispiel-Json-Objekt:


{
    "authSessionId": "KC5r...",
    "expire": "1970-01-01T00:00:00",
    "sign": "FDED..."
}

Hinweis

Eine authSessionId kann nur für die App verwendet werden, welche die authSessionId ausgestellt hat.

Um zu prüfen, ob beim Aufruf der Callback-URI, die Anfrage zur eigenen Anfrage gehört, muss die App die Signatur (sign-Wert aus dem Json-Objekt) nach dem angegeben Muster berechnen und mit der empfangenen Signatur vergleichen.

Beim Abruf des validate-Endpunkts werden id und userName=<appname>@app.idp.d-velop.local zurückgegeben, so dass man erkennen kann, welche App die Anfrage stellt. Ferner wird eine Gruppe mit dem Namen App und der ID 6F3DEBD0-DB38-4061-A085-AD81D6ACF316 angegeben.

 Authentifizieren einer App mit Anfragen im Benutzerkontext ohne Benutzeranmeldung

Apps können sich auch authentifizieren, wenn Anfragen (Requests) erforderlich sind, die nicht im Kontext eines angemeldeten Benutzers stattfinden (z.B. Verarbeitung im Hintergrund). Der Administrator muss diese Funktion in d.ecs identity provider für die jeweilige App freischalten (Manage app trusts). Die so berechtigten Apps können danach mithilfe einer zuvor erstellten App-Session eine kurzzeitig (30 Minuten) gültige Impersonalisierungs-Session für einen bestimmten Benutzer anfordern.

Beispiel in C#


using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;

private class ImpersonateSessionDto
{
    public string AuthSessionId { get; set; }
}

public static void AuthTest()
{
    string appSession = "<App session ID>";
    string userId = "<User ID>";
    string baseUri = "https://<baseUri>";
    using (HttpClient client = new HttpClient() {BaseAddress = new Uri(baseUri)})
    {
        //Use d.ecs identity provider to create an impersonation session 
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", appSession);
        HttpResponseMessage result = client.GetAsync("/identityprovider/impersonatesession?userId=" + userId).Result;
        result.EnsureSuccessStatusCode();
        ImpersonateSessionDto authSessionInfo = JsonConvert.DeserializeObject<ImpersonateSessionDto>(result.Content.ReadAsStringAsync().Result);

        //Set the AuthSessionId as a bearer token and retrieve the resource
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authSessionInfo.AuthSessionId);
        client.DefaultRequestHeaders.Add("Accept", "application/hal+json");
        result = client.GetAsync("/customapp").Result;
        result.EnsureSuccessStatusCode();
        string content = result.Content.ReadAsStringAsync().Result;
        Console.WriteLine(content);
    }
}

 Besondere Gruppen

d.ecs identity provider übernimmt unter anderem die Identifizierung von Administratoren. Wenn ein Benutzer vorab definierte Eigenschaften erfüllt, werden bei der Auslieferung des SCIM-user-Objekts des Benutzers bestimmte Gruppen mit festen id-Werten hinzugefügt. Über die Mitgliedschaft des Benutzers in diesen Gruppen kannst Du diese besonderen Eigenschaften prüfen.

 Die feste Administrator-Gruppe

Wenn ein Benutzer über Administrator-Berechtigungen verfügt und die entsprechenden Eigenschaften erfüllt, wird ihm die Built-In-Admin-Group als Gruppe hinzugefügt. Diese Gruppe hat die feste id DC4885EF-A72C-4489-95A1-F37269D6E48D. > Administratoren-Gruppe: DC4885EF-A72C-4489-95A1-F37269D6E48D