Identityprovider-App

Identityprovider-App

 Einleitung

In diesem Thema findest du allgemeine Informationen zur Identityprovider-App, dem Funktionsumfang und zu den Kenntnissen, die hilfreich beim Programmieren von Funktionen und Anpassungen sind.

 Über die Identityprovider-App

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

 Funktionsumfang von Identityprovider-App

Die Identityprovider-App authentifiziert Benutzer mit verschiedenen Authentifizierungsverfahren wie z.B. die Windows-Authentifizierung (Kerberos) oder mithilfe von Anmeldedaten eines Benutzers. Die Identityprovider-App unterstützt unterschiedliche Systeme zur Benutzerverwaltung, wie z.B. Active Directory (AD). Darüber hinaus stellt die Identityprovider-App 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

Die Identityprovider-App stellt eine API-Schnittstelle für folgende Aktionen zur Verfügung:

 Hintergrund: Ablauf der Authentifizierung und Autorisierung

Die folgende Grafik zeigt den Ablauf der Authentifizierung und Autorisierung.

authentication-and-authorization-flowchart

 Anmelden (Login)

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

Führe eine Umleitung (Redirect) auf die URI /identityprovider/login aus. Gib die URI in Kleinbuchstaben an.

Diese Ressource hat einen gültigen Query-Parameter:

ParameterBeschreibung
redirectPflichtparameter, der den Pfad und die Query-Parameter der eigenen URI enthält. Wenn die Anmeldung erfolgreich war, führt die Identityprovider-App eine Umleitung zurück an diese URI durch.

Wichtig

Beim Aufrufen der URI /identityprovider/login wählt die Identityprovider-App je nach Konfiguration eine beliebige Authentifizierungsmethode. Apps dürfen diese URI daher nicht selbst aufrufen. Die URI sollte stattdessen vom Browser aufgerufen werden. Client-Anwendungen, die sich anmelden möchten, müssen eine Browsersteuerung verwenden und eine Redirect-URI definieren, die sie in der Browsersteuerung verwenden können.

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

 Validieren der Anmeldung (Validate)

Du kannst mit der Funktion validate prüfen, ob eine Anfrage eine korrekte Anmeldung enthält.

Validieren der erforderlichen Informationen

  • Prüfe, ob der Authorization-Header festgelegt ist.
  • Prüfe, ob der Header ein Bearer-Token enthält.
  • Wenn kein Bearer-Token vorhanden ist, prüfe zusätzlich, ob der Cookie AuthSessionId vorhanden ist.
  • Wenn weder das Token noch der Cookie vorhanden ist, führe aus der App heraus eine Umleitung auf die Login-Ressource durch.

Request


GET /identityprovider/validate[?allowExternalValidation=true]

Mit der Angabe von allowExternalValidation=true als Query-Parameter weist du die Identityprovider-App an, auch externe Benutzer zu überprüfen, die nicht über die Provider der Identityprovider-App bekannt sind, sich aber via OpenID Connect (alternative Anmeldung) angemeldet haben. Das zurückgegebene SCIM-user-Objekt enthält für externe Benutzer 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.
404 Not FoundGültiges Token eines gelöschten Benutzers.
200 OKGültiges Token.
Content-Type: application/hal+json
<SCIM-user-Objekt im JSON-Format>

Mit dieser HTTP GET-Anfrage wird der Inhalt des Tokens oder des Cookies wahlweise als Token oder als Cookie an die Identityprovider-App übermittelt. Ob der Inhalt als Bearer-Token oder als Cookie weitergegeben wird, kannst du im Code definieren.

Damit du nicht bei jeder Anfrage (Request) den Validate-Endpunkt aufrufen musst, solltest du die Antwort zwischenspeichern. Verwende für das Caching das SCIM-user-Objekt. Die Identityprovider-App sendet einen CacheControl-Header und legt darin mit MaxAge die Gültigkeitsdauer fest.

Du solltest die Angabe der Gültigkeitsdauer dazu verwenden, die Antwort für die angegebene Anzahl der Sekunden zu speichern. Innerhalb dieser Zeit musst du den Validate-Endpunkt nicht noch einmal aufrufen, wenn die AuthSessionId einer Anfrage mit der gespeicherten AuthSessionId identisch ist.

Hinweis

Ein Benutzer kann ein Konto in mehreren Sitzungen verwenden, bei denen die AuthSessionId unterschiedlich ist. Wenn jeweils das SCIM-user-Objekt zur AuthSessionId gespeichert wird, liegt das Objekt eventuell mehrfach im Speicher vor. Um die Speicherung zu optimieren, kannst du den Teil der Sitzungs-ID vor dem kaufmännischen Und-Zeichen (&) als Schlüssel für die SCIM-user-Objekte verwenden.

 Verwenden des SCIM-Protokolls zur Authentifizierung

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

Hinweis

Weitere Informationen zum SCIM-Protokoll findest du in den Dokumentationen der Internet Engineering Task Force: 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üfe zunächst, ob ein gültiges Token übergeben wurde, um auf die SCIM-Ressource zugreifen zu können.

Unter der genannten URI stellt die Identityprovider-App die beiden SCIM-Ressourcen Users und Groups nur lesend bereit.

Verfügbare Routen

RouteBeschreibung
/identityprovider/scim/UsersListet die Benutzer aller Benutzerprovider auf.
/identityprovider/scim/Users/{id}Listet einen spezifischen Benutzer auf. Der Platzhalter id steht für die ID des spezifischen Benutzers.
/identityprovider/scim/GroupsListet die Gruppen aller Benutzerprovider auf.
/identityprovider/scim/Groups/{id}Listet eine spezifische Gruppe auf. Der Platzhalter id steht für die ID der spezifischen Gruppe.

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)
  • PhoneNumbers (value)
  • Groups (value, display)
  • Photos (value, type)

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%20Smith
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\\smithj",
            "name": {
                "familyName": "Smith",
                "givenName": "John"
            },
            "displayName": "Smith, John",
            "title": "Senior Support Specialist",
            "emails": [
                {
                    "value": "smithj@example.org"
                }
            ],
            "phoneNumbers": [
                {
                    "value": "+1-234-56789-123456789"
                }
            ],
            "photos": [
                {
                    "value": "/identityprovider/scim/photo/2b04bb70-10fa-4892-a62a-97627f0fc67f"
                }
            ]
        },
        ...
    ]
}

Hinweis

Wenn du mehrere Benutzer abrufst, werden nur die Informationen zu den einzelnen Benutzern übertragen. Du erhältst keine Informationen zu den Gruppen, in denen diese Benutzer Mitglieder sind. 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\\smithj",
    "name": {
        "familyName": "Smith",
        "givenName": "John"
    },
    "displayName": "Smith, John",
    "title": "Senior Support Specialist",
    "emails": [
        {
            "value": "smithj@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. Du kannst diese Eigenschaft nicht zur Filterung verwenden.

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\\smithj",
    ...,
    "details": [
        {
            "key": "distinguishedname",
            "values": [
                "CN=John Smith,DC=example,DC=org"
            ]
        },
        {
            "key": "givenname",
            "values": [
                "Smith"
            ]
        },
        {
            "key": "sn",
            "values": [
                "John"
            ]
        },
        {
            "key": "displayname",
            "values": [
                "Smith, 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

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). Generiere in diesem Fall einen API-Schlüssel in der Identityprovider-App und verwende ihn, um eine AuthSessionId mithilfe der Identityprovider-App zu generieren.

Die Authentifizierung mithilfe des API-Schlüssels erfolgt zunächst mit einem Bearer-Token. Verwende den API-Schlüssel im Authorization-Header mit dem Schema Bearer und lege den Accept-Header auf application/json fest. Die Login-Route der Identityprovider-App gibt dann ein Datentransferobjekt mit den Werten AuthSessionId und Expire zurück. Du kannst die ID AuthSessionId bis zum angegebenen Ablaufdatum im Expire-Wert zur weiteren Authentifizierung verwenden. Befülle zu diesem Zweck den Authorization-Header mit dem Schema Bearer mit der zuvor zurückgegebenen ID für den AuthSessionId-Wert und rufe 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 ein JSON-Objekt mit einer HTTP-POST-Anfrage an den Endpunkt /identityprovider/appsession. Das JSON-Objekt enthält folgende Werte:

ParameterBeschreibung
appnameName der App.
callbackPfad der URI an den die AuthSessionId gesendet werden soll (relative URI).
requestidAnfrage-ID, um zu prüfen, ob die HTTP-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 mit den Parametern appname, authSessionId, expire und requestid.
Format: Hex(Sha256(<appname><authSessionId><expire><requestid>)).

Hinweis

Der Parameter requestid sollte eine 256-Bit Zufallszahl sein. Zur Generierung solltest du einen kryptographisch sicheren Zufallszahlengenerator verwenden.

info: Beim Aufrufen der Callback-URI wird derselbe HTTP-Statuscode zurückgegeben, der auch bei der Antwort der App-Session-Anfrage zurückgegeben wird. Davon ausgenommen sind Statuscodes, die auf Serverfehler (HTTP 5xx) hinweisen. Wenn ein Statuscode für einen Serverfehler empfangen wird, sendet die App eine 400 Bad Request-Antwort.

Beispiel-JSON-Objekt:


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

Hinweis

Der Parameter 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, sodass du erkennen kannst, welche App die Anfrage stellt. Es wird außerdem 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 der Identityprovider-App für die jeweilige App freischalten (Manage app trusts). Die so berechtigten Apps können danach mithilfe einer zuvor erstellten App-Session eine kurzzeitig gültige Impersonalisierungs-Session für einen bestimmten Benutzer anfordern. Die Sitzung ist 30 Minuten gültig.

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

Die Identityprovider-App übernimmt unter anderem die Identifizierung von Administratoren. Wenn ein Benutzer administrative Rechte besitzt, wird bei der Auslieferung des SCIM-user-Objekts dem Benutzer eine bestimmte Gruppe mit festem id-Wert hinzugefügt. Es gibt zwei Möglichkeiten, einem Benutzer administrative Rechte zu erteilen. Entweder ist der Benutzer Mitglied in der Administratorengruppe eines Providers oder Mitglied in der Gruppe Administrative group for the tenant. Mithilfe der Mitgliedschaft des Benutzers in diesen Gruppen kannst du prüfen, ob der Benutzer ein Administrator ist.

 Die feste Administratorgruppe

Wenn ein Benutzer über Administratorberechtigungen verfügt, wird er zur Gruppe Built-In-Admin-Group hinzugefügt. Diese Administratorengruppe hat die feste ID DC4885EF-A72C-4489-95A1-F37269D6E48D.